rea-agents 2.0.0 → 2.1.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 (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -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
+ };