rea-agents 1.6.0 → 2.0.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 (238) hide show
  1. package/README.md +157 -66
  2. package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
  3. package/bridge/hopper_bridge.py +13 -3
  4. package/dist/application/AbortablePromise.js +25 -0
  5. package/dist/application/AnalysisProvider.js +3 -0
  6. package/dist/application/AnalysisProviderEvaluation.js +60 -0
  7. package/dist/application/AnalysisProviderRegistry.js +228 -0
  8. package/dist/application/AnalysisSnapshotCache.js +41 -21
  9. package/dist/application/AnalysisSnapshotFiles.js +5 -6
  10. package/dist/application/BinarySession.js +218 -101
  11. package/dist/application/CapabilityInventory.js +18 -0
  12. package/dist/application/CompositeProvider.js +20 -8
  13. package/dist/application/CrossVersionInvestigation.js +10 -6
  14. package/dist/application/DirectAnalysis.js +49 -35
  15. package/dist/application/Doctor.js +110 -8
  16. package/dist/application/ElectronBoundaryAnalysis.js +96 -0
  17. package/dist/application/ElectronBoundaryGraph.js +9 -0
  18. package/dist/application/ElectronBoundaryGraphContext.js +62 -0
  19. package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
  20. package/dist/application/ElectronBoundaryGraphNative.js +86 -0
  21. package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
  22. package/dist/application/EnhancedTools.js +39 -14
  23. package/dist/application/InvestigationProviders.js +41 -0
  24. package/dist/application/JavaScriptApplicationEvidence.js +26 -0
  25. package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
  26. package/dist/application/JavaScriptApplicationService.js +74 -0
  27. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
  28. package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
  29. package/dist/application/JavaScriptArtifactAnalysis.js +305 -0
  30. package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
  31. package/dist/application/JavaScriptArtifactFiles.js +231 -0
  32. package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
  33. package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
  34. package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
  35. package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
  36. package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
  37. package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
  38. package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
  39. package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
  40. package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
  41. package/dist/application/JavaScriptReplayPlanning.js +201 -0
  42. package/dist/application/JavaScriptReplayService.js +266 -0
  43. package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
  44. package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
  45. package/dist/application/MacHopper.js +20 -4
  46. package/dist/application/ManagedApplicationGraphService.js +55 -0
  47. package/dist/application/ManagedMemberComparisonService.js +122 -0
  48. package/dist/application/ManagedNativeVerificationService.js +39 -0
  49. package/dist/application/ManagedReconstructionService.js +58 -0
  50. package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
  51. package/dist/application/PermissionAuthority.js +1 -1
  52. package/dist/application/ProcessCaptureLifecycle.js +1 -1
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProcessSampling.js +16 -12
  55. package/dist/application/ProjectPermissionStore.js +1 -0
  56. package/dist/application/SessionProviderRouter.js +219 -0
  57. package/dist/application/Setup.js +84 -60
  58. package/dist/application/SetupClients.js +1 -1
  59. package/dist/application/SetupPlan.js +9 -2
  60. package/dist/application/SupportedClients.js +40 -16
  61. package/dist/application/Upgrade.js +1 -1
  62. package/dist/application/runtime.js +11 -6
  63. package/dist/artifacts/AsarArtifactReader.js +23 -3
  64. package/dist/browser/CdpBrowserProvider.js +4 -2
  65. package/dist/browser/CdpCaptureEvents.js +49 -0
  66. package/dist/browser/CdpElectronInspection.js +106 -162
  67. package/dist/browser/CdpElectronProvider.js +9 -3
  68. package/dist/browser/CdpElectronScriptEvents.js +69 -0
  69. package/dist/browser/CdpElectronScripts.js +94 -0
  70. package/dist/browser/CdpElectronWorkers.js +56 -0
  71. package/dist/browser/CdpEndpoint.js +14 -1
  72. package/dist/browser/CdpPageCapture.js +8 -4
  73. package/dist/catalogIdentity.js +6 -42
  74. package/dist/cli.js +446 -34
  75. package/dist/cliApplicationCommands.js +62 -0
  76. package/dist/cliBrowserAdvancedCommands.js +5 -4
  77. package/dist/cliBrowserCommands.js +5 -4
  78. package/dist/cliCommandNames.js +56 -0
  79. package/dist/cliElectronCommands.js +106 -3
  80. package/dist/cliEvidenceCommands.js +4 -3
  81. package/dist/cliInvestigationCommands.js +2 -1
  82. package/dist/cliJsonInput.js +54 -0
  83. package/dist/cliPolicyCommands.js +2 -1
  84. package/dist/cliProcessCommands.js +3 -2
  85. package/dist/config.js +101 -2
  86. package/dist/contracts/applicationToolContracts.js +91 -0
  87. package/dist/contracts/artifactToolContracts.js +5 -9
  88. package/dist/contracts/browserToolContracts.js +14 -62
  89. package/dist/contracts/electronToolContracts.js +45 -19
  90. package/dist/contracts/enhancedInputs.js +2 -2
  91. package/dist/contracts/errorSchemas.js +1 -0
  92. package/dist/contracts/investigationExamples.js +0 -6
  93. package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
  94. package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
  95. package/dist/contracts/managedToolContracts.js +153 -0
  96. package/dist/contracts/managedWorkflowExamples.js +346 -0
  97. package/dist/contracts/managedWorkflowToolContracts.js +203 -0
  98. package/dist/contracts/nativeToolContracts.js +2 -6
  99. package/dist/contracts/promptContracts.js +18 -2
  100. package/dist/contracts/providerSelection.js +28 -0
  101. package/dist/contracts/sessionLifecycleInputs.js +2 -0
  102. package/dist/contracts/toolContractExamples.js +10 -25
  103. package/dist/contracts/toolContracts.js +69 -65
  104. package/dist/contracts/toolEffects.js +160 -0
  105. package/dist/contracts/toolOutputSchemas.js +152 -42
  106. package/dist/doctorRuntime.js +47 -0
  107. package/dist/domain/analysisProfile.js +78 -0
  108. package/dist/domain/analysisSnapshot.js +92 -46
  109. package/dist/domain/artifactComparison.js +3 -4
  110. package/dist/domain/artifactGraph.js +1 -0
  111. package/dist/domain/binaryTarget.js +4 -29
  112. package/dist/domain/browserObservation.js +3 -0
  113. package/dist/domain/bundleComparison.js +4 -4
  114. package/dist/domain/electronObservation.js +13 -0
  115. package/dist/domain/electronStaticAnalysis.js +9 -0
  116. package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
  117. package/dist/domain/electronStaticAnalysisIpc.js +171 -0
  118. package/dist/domain/electronStaticAnalysisNative.js +164 -0
  119. package/dist/domain/electronStaticAnalysisTypes.js +1 -0
  120. package/dist/domain/electronStaticAnalysisValues.js +103 -0
  121. package/dist/domain/errors.js +148 -7
  122. package/dist/domain/evidence.js +44 -5
  123. package/dist/domain/evidenceBundle.js +9 -7
  124. package/dist/domain/functionComparison.js +9 -3
  125. package/dist/domain/functionComparisonNormalization.js +37 -0
  126. package/dist/domain/functionComparisonSchemas.js +3 -8
  127. package/dist/domain/functionDossierEvidence.js +4 -1
  128. package/dist/domain/hopperValues.js +66 -9
  129. package/dist/domain/inputIssueProjection.js +52 -0
  130. package/dist/domain/investigationWorkspace.js +2 -1
  131. package/dist/domain/javascriptApplicationAnalysis.js +155 -0
  132. package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
  133. package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
  134. package/dist/domain/javascriptApplicationGraph.js +282 -0
  135. package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
  136. package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
  137. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
  138. package/dist/domain/javascriptApplicationVersionItems.js +221 -0
  139. package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
  140. package/dist/domain/javascriptAstFingerprint.js +143 -0
  141. package/dist/domain/javascriptFeatureSeed.js +140 -0
  142. package/dist/domain/javascriptFeatureTrace.js +232 -0
  143. package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
  144. package/dist/domain/javascriptFeatureTraversal.js +88 -0
  145. package/dist/domain/javascriptNativeHandoff.js +85 -0
  146. package/dist/domain/javascriptReplay.js +361 -0
  147. package/dist/domain/javascriptRuntimeLoadState.js +106 -0
  148. package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
  149. package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
  150. package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
  151. package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
  152. package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
  153. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
  154. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
  155. package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
  156. package/dist/domain/javascriptStaticAnalysis.js +346 -0
  157. package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
  158. package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
  159. package/dist/domain/javascriptStaticAnalysisState.js +23 -0
  160. package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
  161. package/dist/domain/managedApplicationGraph.js +556 -0
  162. package/dist/domain/managedArtifact.js +444 -0
  163. package/dist/domain/managedMemberComparison.js +565 -0
  164. package/dist/domain/managedNativeVerification.js +408 -0
  165. package/dist/domain/managedReconstruction.js +183 -0
  166. package/dist/domain/managedRuntimeCorrelation.js +175 -0
  167. package/dist/domain/reconstructionVerificationSchemas.js +1 -3
  168. package/dist/dotnet/ManagedArtifactInspector.js +318 -0
  169. package/dist/dotnet/ManagedMemberInspector.js +887 -0
  170. package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +368 -0
  172. package/dist/dotnet/ManagedMetadataLayout.js +322 -0
  173. package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
  174. package/dist/dotnet/ManagedPeReader.js +157 -0
  175. package/dist/dotnet/ManagedReaderFailure.js +10 -0
  176. package/dist/dotnet/ManagedStaticProvider.js +203 -0
  177. package/dist/generatedPackageMetadata.js +2 -2
  178. package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
  179. package/dist/ghidra/GhidraClient.js +400 -0
  180. package/dist/ghidra/GhidraClientTypes.js +1 -0
  181. package/dist/ghidra/GhidraDefaults.js +20 -0
  182. package/dist/ghidra/GhidraDiagnostics.js +43 -0
  183. package/dist/ghidra/GhidraDoctor.js +59 -0
  184. package/dist/ghidra/GhidraFunctionValues.js +264 -0
  185. package/dist/ghidra/GhidraInstallation.js +239 -0
  186. package/dist/ghidra/GhidraInventoryValues.js +217 -0
  187. package/dist/ghidra/GhidraLauncher.js +139 -0
  188. package/dist/ghidra/GhidraProvider.js +366 -0
  189. package/dist/ghidra/GhidraRequestQueue.js +98 -0
  190. package/dist/ghidra/GhidraResponseBuffer.js +31 -0
  191. package/dist/ghidra/GhidraResponseRouter.js +24 -0
  192. package/dist/ghidra/GhidraSessionError.js +39 -0
  193. package/dist/ghidra/GhidraSessionValues.js +62 -0
  194. package/dist/ghidra/GhidraSocketConnection.js +46 -0
  195. package/dist/ghidra/protocol.js +69 -0
  196. package/dist/hopper/BridgeLauncher.js +19 -54
  197. package/dist/hopper/HopperAnalysisProfile.js +106 -0
  198. package/dist/hopper/HopperClient.js +154 -162
  199. package/dist/hopper/HopperProvider.js +99 -7
  200. package/dist/hopper/HopperSessionValues.js +27 -0
  201. package/dist/hopper/HopperSocketConnection.js +31 -0
  202. package/dist/main.js +14 -0
  203. package/dist/native/NativeMacOSProvider.js +3 -1
  204. package/dist/process/PendingOperations.js +59 -0
  205. package/dist/process/PrivateRuntimeRoot.js +33 -0
  206. package/dist/process/ProviderDeadline.js +95 -0
  207. package/dist/process/ProviderProcess.js +275 -0
  208. package/dist/replay/JavaScriptReplayWorker.js +286 -0
  209. package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
  210. package/dist/replay/LinuxRuntimeClosure.js +46 -0
  211. package/dist/replay/LinuxSeccompPolicy.js +40 -0
  212. package/dist/replay/ReplayWorkerProtocol.js +55 -0
  213. package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
  214. package/dist/server/createServer.js +53 -0
  215. package/dist/server/promptCompletion.js +9 -3
  216. package/dist/server/registerApplicationTools.js +115 -0
  217. package/dist/server/registerArtifactComparisonTool.js +16 -9
  218. package/dist/server/registerBrowserTools.js +33 -8
  219. package/dist/server/registerBundleComparisonTool.js +17 -20
  220. package/dist/server/registerElectronTools.js +45 -3
  221. package/dist/server/registerEnhancedTools.js +67 -19
  222. package/dist/server/registerEvidenceTools.js +5 -1
  223. package/dist/server/registerFunctionComparisonTool.js +14 -7
  224. package/dist/server/registerInvestigationTools.js +21 -17
  225. package/dist/server/registerManagedTools.js +6 -0
  226. package/dist/server/registerManagedWorkflowTools.js +277 -0
  227. package/dist/server/registerOfficialTools.js +11 -8
  228. package/dist/server/registerProcessComparisonTool.js +25 -47
  229. package/dist/server/registerSessionStatusTool.js +7 -7
  230. package/dist/server/registerSessionTools.js +57 -87
  231. package/dist/server/sessionEvidence.js +14 -19
  232. package/dist/server/toolInputValidation.js +77 -0
  233. package/dist/server/toolRegistrationOptions.js +79 -2
  234. package/dist/server/toolResult.js +25 -17
  235. package/package.json +17 -7
  236. package/scripts/rea.mjs +36 -2
  237. package/skills/rea-analysis/SKILL.md +59 -3
  238. /package/dist/{application → process}/ProcessOwnership.js +0 -0
@@ -0,0 +1,160 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { browserAllowedOriginsSchema, browserEndpointSchema, webPageInspectionSchema, } from "./browserObservation.js";
5
+ import { electronFileRootsSchema, electronPageInspectionSchema, } from "./electronObservation.js";
6
+ import { parseEvidence } from "./evidence.js";
7
+ import { javascriptApplicationAnalysisResultSchema, } from "./javascriptApplicationAnalysis.js";
8
+ import { compareCodePoints, } from "./javascriptApplicationGraph.js";
9
+ /** Parse and semantically bind every static analysis Evidence layer. */
10
+ export const parseStaticLayers = (layers) => layers
11
+ .map((layer) => parseStaticLayer(layer))
12
+ .sort((left, right) => compareCodePoints(left.layerId, right.layerId));
13
+ /** Parse only supported passive web/Electron inspection Evidence. */
14
+ export const parseRuntimeCaptures = (observations) => observations
15
+ .map((observation) => parseRuntimeCapture(observation))
16
+ .sort((left, right) => compareCodePoints(left.evidence.evidence_id, right.evidence.evidence_id));
17
+ export const digestCanonical = (value) => {
18
+ const encoded = canonicalize(value);
19
+ if (encoded === undefined)
20
+ throw new TypeError("Runtime reconciliation could not canonicalize input");
21
+ return createHash("sha256").update(encoded).digest("hex");
22
+ };
23
+ const parseStaticLayer = (layer) => {
24
+ const evidence = parseEvidence(layer.analysis);
25
+ assertEvidenceIdentity(evidence, {
26
+ operation: "analyze_javascript_application",
27
+ predicate: "rea.javascript-application-analysis/v1",
28
+ providerId: "rea-javascript-application",
29
+ providerName: "REA JavaScript application analyzer",
30
+ providerVersion: "1",
31
+ authority: "shipped-artifact",
32
+ confidence: "derived",
33
+ });
34
+ const result = javascriptApplicationAnalysisResultSchema.parse(evidence.normalized_result);
35
+ if (evidence.subject === null ||
36
+ evidence.subject.digest.sha256 !== result.root_artifact_sha256 ||
37
+ evidence.subject.format !== result.format ||
38
+ evidence.subject.local_path !== result.input_path)
39
+ throw new TypeError("JavaScript application Evidence subject disagrees with its result");
40
+ const parameters = z
41
+ .object({
42
+ approved: z.literal(true),
43
+ source_map_read_approved: z.boolean(),
44
+ })
45
+ .passthrough()
46
+ .parse(evidence.parameters);
47
+ return {
48
+ layerId: `jrl_${digestCanonical({
49
+ role: layer.role,
50
+ evidence_id: evidence.evidence_id,
51
+ runtime_mappings: layer.runtime_mappings,
52
+ })}`,
53
+ role: layer.role,
54
+ evidence,
55
+ result,
56
+ graph: result.graph,
57
+ runtimeMappings: layer.runtime_mappings,
58
+ sourceMapReadApproved: parameters.source_map_read_approved,
59
+ };
60
+ };
61
+ const parseRuntimeCapture = (input) => {
62
+ const evidence = parseEvidence(input);
63
+ if (evidence.operation === "inspect_web_page") {
64
+ assertEvidenceIdentity(evidence, {
65
+ operation: "inspect_web_page",
66
+ predicate: "rea.web-page-inspection/v2",
67
+ providerId: "rea-cdp-browser",
68
+ providerName: "REA Chrome DevTools Protocol observation provider",
69
+ providerVersion: "2",
70
+ authority: "external-service",
71
+ confidence: "observed",
72
+ });
73
+ const inspection = webPageInspectionSchema.parse(evidence.normalized_result);
74
+ assertRuntimeParameters(evidence, {
75
+ kind: "browser",
76
+ targetId: inspection.target.target_id,
77
+ targetOrigin: inspection.target.origin,
78
+ sourceIncluded: inspection.scripts.items.some(({ source }) => source.included),
79
+ });
80
+ return {
81
+ kind: "browser",
82
+ evidence,
83
+ inspection,
84
+ captureSha256: digestCanonical(inspection),
85
+ scriptsCompleteWithinScope: scriptsComplete(inspection.completeness),
86
+ };
87
+ }
88
+ if (evidence.operation === "inspect_electron_page") {
89
+ assertEvidenceIdentity(evidence, {
90
+ operation: "inspect_electron_page",
91
+ predicate: "rea.electron-page-inspection/v1",
92
+ providerId: "rea-cdp-electron",
93
+ providerName: "REA Electron file-page CDP observation provider",
94
+ providerVersion: "1",
95
+ authority: "external-service",
96
+ confidence: "observed",
97
+ });
98
+ const inspection = electronPageInspectionSchema.parse(evidence.normalized_result);
99
+ assertRuntimeParameters(evidence, {
100
+ kind: "electron",
101
+ targetId: inspection.target.target_id,
102
+ sourceIncluded: inspection.scripts.items.some(({ source }) => source.included),
103
+ });
104
+ return {
105
+ kind: "electron",
106
+ evidence,
107
+ inspection,
108
+ captureSha256: digestCanonical(inspection),
109
+ scriptsCompleteWithinScope: scriptsComplete(inspection.completeness),
110
+ };
111
+ }
112
+ throw new TypeError("Runtime reconciliation requires inspect_web_page or inspect_electron_page Evidence");
113
+ };
114
+ const assertRuntimeParameters = (evidence, expected) => {
115
+ const common = {
116
+ target_id: z.string().trim().min(1).max(256),
117
+ cdp_endpoint: browserEndpointSchema,
118
+ include_script_sources: z.boolean(),
119
+ };
120
+ if (expected.kind === "browser") {
121
+ const parameters = z
122
+ .object({ ...common, allowed_origins: browserAllowedOriginsSchema })
123
+ .passthrough()
124
+ .parse(evidence.parameters);
125
+ if (parameters.target_id !== expected.targetId)
126
+ throw new TypeError("Runtime Evidence target disagrees with its captured result");
127
+ if (!parameters.allowed_origins.includes(expected.targetOrigin))
128
+ throw new TypeError("Browser Evidence target is outside its recorded origin scope");
129
+ if (expected.sourceIncluded && !parameters.include_script_sources)
130
+ throw new TypeError("Runtime Evidence contains source without source-capture approval");
131
+ return;
132
+ }
133
+ const parameters = z
134
+ .object({
135
+ ...common,
136
+ allowed_file_roots: electronFileRootsSchema,
137
+ source_capture_approved: z.boolean(),
138
+ })
139
+ .passthrough()
140
+ .parse(evidence.parameters);
141
+ if (parameters.target_id !== expected.targetId)
142
+ throw new TypeError("Runtime Evidence target disagrees with its captured result");
143
+ if (expected.sourceIncluded &&
144
+ (!parameters.include_script_sources ||
145
+ parameters.source_capture_approved !== true))
146
+ throw new TypeError("Runtime Evidence contains source without source-capture approval");
147
+ };
148
+ const scriptsComplete = (completeness) => !completeness.truncated_sections.includes("scripts") &&
149
+ !completeness.unavailable_sections.includes("scripts") &&
150
+ completeness.dropped_events.scripts === 0;
151
+ const assertEvidenceIdentity = (evidence, expected) => {
152
+ if (evidence.operation !== expected.operation ||
153
+ evidence.predicate_type !== expected.predicate ||
154
+ evidence.provider.id !== expected.providerId ||
155
+ evidence.provider.name !== expected.providerName ||
156
+ evidence.provider.version !== expected.providerVersion ||
157
+ evidence.authority !== expected.authority ||
158
+ evidence.confidence !== expected.confidence)
159
+ throw new TypeError(`Evidence does not match the supported ${expected.operation} contract`);
160
+ };
@@ -0,0 +1,119 @@
1
+ import { compareCodePoints, } from "./javascriptApplicationGraph.js";
2
+ import { digestCanonical, } from "./javascriptRuntimeReconciliationParsing.js";
3
+ import { javascriptRuntimeReconciliationResultSchema, } from "./javascriptRuntimeReconciliationSchemas.js";
4
+ /** Build and verify the deterministic caller-visible reconciliation result. */
5
+ export const createJavaScriptRuntimeReconciliationResult = (input) => {
6
+ const completion = completionFlags(input);
7
+ const semantic = {
8
+ schema_version: 1,
9
+ static_layers: staticLayerSummaries(input.layers),
10
+ runtime_captures: captureSummaries(input.captures, input.runtime),
11
+ graph: input.graph,
12
+ summary: reconciliationSummary(input),
13
+ reconciliations: input.matching.items,
14
+ static_load_states: input.loadStates.states,
15
+ source_map_authority: sourceMapAuthority(input),
16
+ coverage: reconciliationCoverage(input, completion),
17
+ evidence_links: uniqueSorted([
18
+ ...input.layers.map(({ evidence }) => evidence.evidence_id),
19
+ ...input.captures.map(({ evidence }) => evidence.evidence_id),
20
+ ]),
21
+ limitations: reconciliationLimitations(completion),
22
+ };
23
+ return javascriptRuntimeReconciliationResultSchema.parse({
24
+ reconciliation_id: `jrr_${digestCanonical(semantic)}`,
25
+ ...semantic,
26
+ });
27
+ };
28
+ const staticLayerSummaries = (layers) => layers.map((layer) => ({
29
+ layer_id: layer.layerId,
30
+ role: layer.role,
31
+ evidence_id: layer.evidence.evidence_id,
32
+ graph_id: layer.graph.graph_id,
33
+ root_artifact_sha256: layer.result.root_artifact_sha256,
34
+ input_path: layer.result.input_path,
35
+ format: layer.result.format,
36
+ runtime_mappings: [...layer.runtimeMappings],
37
+ }));
38
+ const captureSummaries = (captures, runtime) => captures.map((capture) => {
39
+ const targetNode = runtime.targetNodeByEvidenceId.get(capture.evidence.evidence_id);
40
+ if (targetNode === undefined)
41
+ throw new TypeError("Runtime projection omitted a required target node");
42
+ return {
43
+ evidence_id: capture.evidence.evidence_id,
44
+ capture_sha256: capture.captureSha256,
45
+ kind: capture.kind,
46
+ target_node_id: targetNode.node_id,
47
+ target_key: capture.inspection.target.target_id,
48
+ target_location: capture.kind === "browser"
49
+ ? capture.inspection.target.url.slice(0, 4_096)
50
+ : capture.inspection.target.file_path.slice(0, 4_096),
51
+ frames: capture.inspection.frames.length,
52
+ scripts: capture.inspection.scripts.items.length,
53
+ workers: capture.inspection.workers.length,
54
+ scripts_complete_within_scope: capture.scriptsCompleteWithinScope,
55
+ };
56
+ });
57
+ const reconciliationSummary = (input) => ({
58
+ runtime_targets: countBy(input.runtime.entities, "kind", "target"),
59
+ runtime_frames: countBy(input.runtime.entities, "kind", "frame"),
60
+ runtime_scripts: countBy(input.runtime.entities, "kind", "script"),
61
+ runtime_workers: countBy(input.runtime.entities, "kind", "worker"),
62
+ matched: countBy(input.matching.items, "status", "matched"),
63
+ ambiguous: countBy(input.matching.items, "status", "ambiguous"),
64
+ unmatched: countBy(input.matching.items, "status", "unmatched"),
65
+ unknown: countBy(input.matching.items, "status", "unknown"),
66
+ static_loaded: countBy(input.loadStates.states, "status", "loaded"),
67
+ static_resident: countBy(input.loadStates.states, "status", "resident-in-loaded-asset"),
68
+ static_not_observed: countBy(input.loadStates.states, "status", "not-observed-in-capture"),
69
+ static_unknown: countBy(input.loadStates.states, "status", "unknown"),
70
+ });
71
+ const sourceMapAuthority = (input) => ({
72
+ used_for_primary_matching: false,
73
+ static_layers_with_read_approval: input.layers.filter(({ sourceMapReadApproved }) => sourceMapReadApproved).length,
74
+ runtime_script_declarations: input.captures.reduce((total, capture) => total +
75
+ (capture.kind === "browser"
76
+ ? capture.inspection.scripts.items.filter(({ source_map_url: url }) => url !== null).length
77
+ : 0), 0),
78
+ limitation: "Source-map declarations and approved original-source reads retain separate static authority and are not used as primary runtime byte matches.",
79
+ });
80
+ const reconciliationCoverage = (input, completion) => ({
81
+ status: completion.outputTruncated || completion.inputTruncated
82
+ ? "truncated"
83
+ : completion.inputPartial
84
+ ? "partial"
85
+ : "complete-within-inputs",
86
+ truncated: completion.outputTruncated || completion.inputTruncated,
87
+ omitted_runtime_entities: input.runtime.omittedEntities,
88
+ omitted_reconciliation_items: input.matching.omittedItems,
89
+ omitted_static_load_states: input.loadStates.omittedStates,
90
+ omitted_graph_items: input.omittedGraphItems,
91
+ });
92
+ const completionFlags = (input) => ({
93
+ outputTruncated: input.runtime.omittedEntities > 0 ||
94
+ input.matching.omittedItems > 0 ||
95
+ input.loadStates.omittedStates > 0 ||
96
+ input.omittedGraphItems > 0,
97
+ inputTruncated: input.layers.some(({ graph }) => graph.coverage.truncated) ||
98
+ input.captures.some(({ inspection }) => inspection.completeness.conditions.includes("truncated")),
99
+ inputPartial: input.layers.some(({ graph }) => graph.coverage.status !== "complete") ||
100
+ input.captures.some(({ inspection }) => inspection.completeness.status !== "complete_within_window"),
101
+ });
102
+ const reconciliationLimitations = (completion) => uniqueSorted([
103
+ "Only caller-supplied, semantically verified Evidence records were reconciled.",
104
+ "Digest equality establishes byte identity but not code reachability, module initialization, or feature execution.",
105
+ "A static module resident in an observed bundle is not reported as executed.",
106
+ "Not-observed means absent from the bounded captures in scope, never globally unloaded.",
107
+ "Caller-declared file and URL mappings are inference inputs and do not expand CDP origin or filesystem-root authority.",
108
+ "Source-map authority remains separate from passive runtime authority.",
109
+ ...(completion.inputPartial
110
+ ? ["One or more static graphs or passive captures were incomplete."]
111
+ : []),
112
+ ...(completion.outputTruncated
113
+ ? [
114
+ "Caller limits omitted runtime entities or reconciliation classifications.",
115
+ ]
116
+ : []),
117
+ ]);
118
+ const countBy = (items, key, value) => items.filter((item) => item[key] === value).length;
119
+ const uniqueSorted = (values) => [...new Set(values)].sort(compareCodePoints);
@@ -0,0 +1,316 @@
1
+ import { createJavaScriptApplicationEdge, createJavaScriptApplicationNode, } from "./javascriptApplicationGraph.js";
2
+ /** Project authorized passive capture metadata into capture-scoped JAG nodes. */
3
+ export const projectRuntimeCaptures = (captures, maximumEntities) => {
4
+ if (maximumEntities < captures.length)
5
+ throw new TypeError("Runtime entity limit must admit one target for every capture");
6
+ const all = captures.flatMap(projectCapture);
7
+ const targets = all.filter(({ kind }) => kind === "target");
8
+ const nonTargets = all.filter(({ kind }) => kind !== "target");
9
+ const retained = [...targets, ...nonTargets].slice(0, maximumEntities);
10
+ const retainedIds = new Set(retained.map(({ node }) => node.node_id));
11
+ const nodes = retained.map(({ node }) => node);
12
+ const edges = captures
13
+ .flatMap((capture) => runtimeEdges(capture, all))
14
+ .filter(({ source_node_id: source, target_node_id: target }) => retainedIds.has(source) && retainedIds.has(target));
15
+ const targetNodeByEvidenceId = new Map(retained
16
+ .filter(({ kind }) => kind === "target")
17
+ .map(({ capture, node }) => [capture.evidence.evidence_id, node]));
18
+ return {
19
+ entities: retained,
20
+ nodes,
21
+ edges,
22
+ targetNodeByEvidenceId,
23
+ omittedEntities: all.length - retained.length,
24
+ };
25
+ };
26
+ const projectCapture = (capture) => {
27
+ const target = targetEntity(capture);
28
+ return [
29
+ target,
30
+ ...frameEntities(capture),
31
+ ...scriptEntities(capture),
32
+ ...workerEntities(capture),
33
+ ];
34
+ };
35
+ const targetEntity = (capture) => {
36
+ const target = capture.inspection.target;
37
+ const location = "url" in target
38
+ ? { kind: "url", value: target.url }
39
+ : { kind: "file", value: target.file_path };
40
+ return runtimeEntity(capture, {
41
+ kind: "target",
42
+ nodeKind: "target",
43
+ runtimeKey: target.target_id,
44
+ targetKey: target.target_id,
45
+ frameKey: null,
46
+ scriptKey: null,
47
+ location,
48
+ staticKind: "renderer",
49
+ sourceSha256: null,
50
+ label: target.title,
51
+ properties: {
52
+ runtime_type: target.type,
53
+ location: location.value.slice(0, 4_096),
54
+ attached: target.attached,
55
+ },
56
+ section: "target",
57
+ });
58
+ };
59
+ const frameEntities = (capture) => capture.inspection.frames.map((frame) => {
60
+ const location = "url" in frame
61
+ ? { kind: "url", value: frame.url }
62
+ : { kind: "file", value: frame.file_path };
63
+ return runtimeEntity(capture, {
64
+ kind: "frame",
65
+ nodeKind: "frame",
66
+ runtimeKey: frame.frame_id,
67
+ targetKey: capture.inspection.target.target_id,
68
+ frameKey: frame.frame_id,
69
+ scriptKey: null,
70
+ location,
71
+ staticKind: "renderer",
72
+ sourceSha256: null,
73
+ label: location.value,
74
+ properties: {
75
+ parent_frame_id: frame.parent_frame_id,
76
+ location: location.value.slice(0, 4_096),
77
+ },
78
+ section: "frames",
79
+ });
80
+ });
81
+ const scriptEntities = (capture) => capture.inspection.scripts.items.map((script) => {
82
+ const location = "url" in script
83
+ ? { kind: "url", value: script.url }
84
+ : { kind: "file", value: script.file_path };
85
+ return runtimeEntity(capture, {
86
+ kind: "script",
87
+ nodeKind: "runtime-script-instance",
88
+ runtimeKey: script.script_key,
89
+ targetKey: capture.inspection.target.target_id,
90
+ frameKey: script.frame_id,
91
+ scriptKey: script.script_key,
92
+ location,
93
+ staticKind: "javascript",
94
+ sourceSha256: script.source.included
95
+ ? script.source.artifact.sha256
96
+ : null,
97
+ label: location.value,
98
+ properties: {
99
+ location: location.value.slice(0, 4_096),
100
+ cdp_hash: script.cdp_hash,
101
+ length: script.length,
102
+ is_module: script.is_module,
103
+ language: script.language,
104
+ source_sha256: script.source.included
105
+ ? script.source.artifact.sha256
106
+ : null,
107
+ ...("source_map_url" in script
108
+ ? { source_map_url: script.source_map_url }
109
+ : {}),
110
+ },
111
+ section: "scripts",
112
+ ...(script.source.included
113
+ ? { artifactSha256: script.source.artifact.sha256 }
114
+ : {}),
115
+ });
116
+ });
117
+ const workerEntities = (capture) => capture.inspection.workers.map((worker) => {
118
+ const location = "url" in worker
119
+ ? { kind: "url", value: worker.url }
120
+ : { kind: "file", value: worker.file_path };
121
+ const serviceWorker = worker.type.includes("service");
122
+ return runtimeEntity(capture, {
123
+ kind: "worker",
124
+ nodeKind: serviceWorker ? "service-worker" : "worker",
125
+ runtimeKey: worker.target_id,
126
+ targetKey: worker.target_id,
127
+ frameKey: worker.parent_frame_id,
128
+ scriptKey: null,
129
+ location,
130
+ staticKind: serviceWorker ? "service-worker" : "worker",
131
+ sourceSha256: null,
132
+ label: location.value,
133
+ properties: {
134
+ runtime_type: worker.type,
135
+ location: location.value.slice(0, 4_096),
136
+ attached: worker.attached,
137
+ opener_target_id: worker.opener_target_id,
138
+ parent_frame_id: worker.parent_frame_id,
139
+ },
140
+ section: "workers",
141
+ });
142
+ });
143
+ const runtimeEntity = (capture, input) => ({
144
+ kind: input.kind,
145
+ section: input.section,
146
+ capture,
147
+ runtimeKey: input.runtimeKey,
148
+ location: input.location,
149
+ frameKey: input.frameKey,
150
+ sourceSha256: input.sourceSha256,
151
+ staticKind: input.staticKind,
152
+ node: createJavaScriptApplicationNode({
153
+ kind: input.nodeKind,
154
+ identity: {
155
+ strategy: "runtime-instance",
156
+ stability: "capture-only",
157
+ capture_sha256: capture.captureSha256,
158
+ runtime_key: input.runtimeKey,
159
+ },
160
+ observations: [
161
+ {
162
+ label: input.label.slice(0, 1_024) || null,
163
+ properties: input.properties,
164
+ evidence: runtimeEvidence(capture, input),
165
+ },
166
+ ],
167
+ }),
168
+ });
169
+ const runtimeEvidence = (capture, input) => ({
170
+ authority: "passive-cdp-runtime",
171
+ state: "observed",
172
+ confidence: "exact",
173
+ artifact: input.artifactSha256 === undefined
174
+ ? {
175
+ available: false,
176
+ reason: input.section === "scripts" ? "not-observed" : "not-applicable",
177
+ detail: input.section === "scripts"
178
+ ? "Script source bytes were not included in this passive capture."
179
+ : "This runtime entity has no standalone artifact bytes.",
180
+ }
181
+ : {
182
+ available: true,
183
+ artifact_id: `art_${input.artifactSha256}`,
184
+ sha256: input.artifactSha256,
185
+ },
186
+ location: {
187
+ available: true,
188
+ value: {
189
+ kind: "runtime",
190
+ capture_sha256: capture.captureSha256,
191
+ target_key: input.targetKey,
192
+ frame_key: input.frameKey,
193
+ script_key: input.scriptKey,
194
+ },
195
+ },
196
+ extractor: {
197
+ name: capture.evidence.provider.id,
198
+ version: capture.evidence.provider.version ??
199
+ (capture.kind === "browser" ? "2" : "1"),
200
+ operation: capture.kind === "browser" ? "inspect_web_page" : "inspect_electron_page",
201
+ executable_sha256: null,
202
+ },
203
+ coverage: runtimeSectionCoverage(capture, input.section),
204
+ limitations: [
205
+ "The entity was observed only within one bounded passive CDP capture.",
206
+ ...(runtimeSectionComplete(capture, input.section)
207
+ ? []
208
+ : [
209
+ `The runtime ${input.section} inventory was incomplete within this capture.`,
210
+ ]),
211
+ ],
212
+ evidence_ids: [capture.evidence.evidence_id],
213
+ });
214
+ const runtimeEdges = (capture, entities) => {
215
+ const local = entities.filter(({ capture: candidate }) => candidate.evidence.evidence_id === capture.evidence.evidence_id);
216
+ const target = local.find(({ kind }) => kind === "target");
217
+ if (target === undefined)
218
+ return [];
219
+ const frames = new Map(local
220
+ .filter(({ kind }) => kind === "frame")
221
+ .map((entity) => [entity.runtimeKey, entity]));
222
+ return local.flatMap((entity) => {
223
+ if (entity === target)
224
+ return [];
225
+ const parent = (entity.kind === "script" || entity.kind === "worker") &&
226
+ entity.frameKey !== null
227
+ ? (frames.get(entity.frameKey) ?? target)
228
+ : target;
229
+ return [
230
+ createJavaScriptApplicationEdge({
231
+ source_node_id: parent.node.node_id,
232
+ target_node_id: entity.node.node_id,
233
+ relation: entity.kind === "script" ? "loads" : "contains",
234
+ properties: { basis: "passive-cdp-observation" },
235
+ evidence: runtimeRelationshipEvidence(capture, entity),
236
+ }),
237
+ ];
238
+ });
239
+ };
240
+ const runtimeRelationshipEvidence = (capture, entity) => ({
241
+ authority: "passive-cdp-runtime",
242
+ state: "observed",
243
+ confidence: "exact",
244
+ artifact: {
245
+ available: false,
246
+ reason: "not-applicable",
247
+ detail: "The relationship is runtime metadata, not standalone bytes.",
248
+ },
249
+ location: {
250
+ available: true,
251
+ value: {
252
+ kind: "runtime",
253
+ capture_sha256: capture.captureSha256,
254
+ target_key: capture.inspection.target.target_id,
255
+ frame_key: entity.frameKey,
256
+ script_key: entity.kind === "script" ? entity.runtimeKey : null,
257
+ },
258
+ },
259
+ extractor: {
260
+ name: capture.evidence.provider.id,
261
+ version: capture.evidence.provider.version ??
262
+ (capture.kind === "browser" ? "2" : "1"),
263
+ operation: capture.kind === "browser" ? "inspect_web_page" : "inspect_electron_page",
264
+ executable_sha256: null,
265
+ },
266
+ coverage: runtimeSectionCoverage(capture, entity.section),
267
+ limitations: [
268
+ "The relationship was observed only within one bounded passive CDP capture.",
269
+ ],
270
+ evidence_ids: [capture.evidence.evidence_id],
271
+ });
272
+ const runtimeSectionComplete = (capture, section) => {
273
+ if (section === "target")
274
+ return true;
275
+ if (section === "scripts")
276
+ return capture.scriptsCompleteWithinScope;
277
+ return (!capture.inspection.completeness.truncated_sections.includes(section) &&
278
+ !capture.inspection.completeness.unavailable_sections.includes(section));
279
+ };
280
+ const runtimeSectionCoverage = (capture, section) => {
281
+ if (runtimeSectionComplete(capture, section))
282
+ return {
283
+ status: "complete",
284
+ truncated: false,
285
+ omitted_count: 0,
286
+ limits: [],
287
+ };
288
+ const truncated = section !== "target" &&
289
+ capture.inspection.completeness.truncated_sections.includes(section);
290
+ return {
291
+ status: "partial",
292
+ truncated,
293
+ omitted_count: null,
294
+ limits: truncated
295
+ ? [
296
+ {
297
+ name: `passive-cdp-${section}-limit`,
298
+ value: runtimeSectionCount(capture, section),
299
+ unit: "items",
300
+ },
301
+ ]
302
+ : [],
303
+ };
304
+ };
305
+ const runtimeSectionCount = (capture, section) => {
306
+ switch (section) {
307
+ case "target":
308
+ return 1;
309
+ case "frames":
310
+ return capture.inspection.frames.length;
311
+ case "scripts":
312
+ return capture.inspection.scripts.items.length;
313
+ case "workers":
314
+ return capture.inspection.workers.length;
315
+ }
316
+ };