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,136 @@
1
+ import { z } from "zod";
2
+ import { evidenceSchema, providerSchema } from "./evidence.js";
3
+ import { javascriptApplicationGraphSchema } from "./javascriptApplicationGraph.js";
4
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
5
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
6
+ const nodeIdSchema = z.string().regex(/^jag_node_[a-f0-9]{64}$/u);
7
+ const edgeIdSchema = z.string().regex(/^jag_edge_[a-f0-9]{64}$/u);
8
+ const boundedTextSchema = z.string().min(1).max(4_096);
9
+ /** Literal starting point for one bounded application trace. */
10
+ const applicationFeatureSeedSchema = z.strictObject({
11
+ kind: z.enum([
12
+ "node-id",
13
+ "route",
14
+ "string",
15
+ "api",
16
+ "channel",
17
+ "module",
18
+ "native-export",
19
+ ]),
20
+ value: boundedTextSchema,
21
+ match: z.enum(["exact", "contains"]).nullable().default(null),
22
+ case_sensitive: z.boolean().default(false),
23
+ });
24
+ const featureTraceLimitsSchema = z.strictObject({
25
+ max_seed_matches: z.number().int().min(1).max(1_000).default(25),
26
+ max_depth: z.number().int().min(0).max(64).default(12),
27
+ max_nodes: z.number().int().min(1).max(50_000).default(2_000),
28
+ max_edges: z.number().int().min(1).max(100_000).default(4_000),
29
+ max_paths: z.number().int().min(1).max(1_000).default(100),
30
+ });
31
+ /** Evidence-backed application graph and explicit trace bounds. */
32
+ export const traceApplicationFeatureInputSchema = z
33
+ .strictObject({
34
+ application: evidenceSchema,
35
+ native_observations: z.array(evidenceSchema).max(64).default([]),
36
+ seed: applicationFeatureSeedSchema,
37
+ direction: z.enum(["outgoing", "incoming", "both"]).default("both"),
38
+ limits: featureTraceLimitsSchema.default({
39
+ max_seed_matches: 25,
40
+ max_depth: 12,
41
+ max_nodes: 2_000,
42
+ max_edges: 4_000,
43
+ max_paths: 100,
44
+ }),
45
+ })
46
+ .superRefine((input, context) => {
47
+ const ids = input.native_observations.map(({ evidence_id: id }) => id);
48
+ if (new Set(ids).size !== ids.length)
49
+ context.addIssue({
50
+ code: "custom",
51
+ path: ["native_observations"],
52
+ message: "Native observations must be unique",
53
+ });
54
+ });
55
+ const seedMatchSchema = z.strictObject({
56
+ node_id: nodeIdSchema,
57
+ kind: z.string().min(1).max(128),
58
+ basis: z.enum(["node-id", "label", "identity", "property"]),
59
+ field: z.string().min(1).max(256),
60
+ });
61
+ const tracePathSchema = z.strictObject({
62
+ path_id: z.string().regex(/^jatp_[a-f0-9]{64}$/u),
63
+ start_node_id: nodeIdSchema,
64
+ end_node_id: nodeIdSchema,
65
+ end_kind: z.string().min(1).max(128),
66
+ node_ids: z.array(nodeIdSchema).min(1).max(65),
67
+ edge_ids: z.array(edgeIdSchema).max(64),
68
+ authorities: z.array(z.string().min(1).max(128)).max(16),
69
+ contains_inference: z.boolean(),
70
+ });
71
+ const nativeHandoffSchema = z.strictObject({
72
+ native_node_id: nodeIdSchema,
73
+ artifact_sha256: digestSchema,
74
+ artifact_path: z.string().min(1).max(4_096).nullable(),
75
+ export_node_ids: z.array(nodeIdSchema).max(1_000),
76
+ requested_exports: z.array(boundedTextSchema).max(1_000),
77
+ status: z.enum(["evidence-linked", "requires-provider-analysis"]),
78
+ providers: z.array(providerSchema).max(64),
79
+ evidence_ids: z.array(evidenceIdSchema).max(64),
80
+ recommended_tools: z
81
+ .array(z.enum([
82
+ "open_binary",
83
+ "binary_overview",
84
+ "search_procedures",
85
+ "analyze_function",
86
+ "xrefs",
87
+ ]))
88
+ .max(5),
89
+ });
90
+ /** Evidence-preserving bounded subgraph and native-analysis frontiers. */
91
+ export const applicationFeatureTraceResultSchema = z.strictObject({
92
+ schema_version: z.literal(1),
93
+ trace_id: z.string().regex(/^jatr_[a-f0-9]{64}$/u),
94
+ source_evidence_id: evidenceIdSchema,
95
+ source_graph_id: z.string().regex(/^jag_[a-f0-9]{64}$/u),
96
+ seed: applicationFeatureSeedSchema,
97
+ direction: z.enum(["outgoing", "incoming", "both"]),
98
+ seed_matches: z.array(seedMatchSchema).max(1_000),
99
+ graph: javascriptApplicationGraphSchema.nullable(),
100
+ paths: z.array(tracePathSchema).max(1_000),
101
+ native_handoffs: z.array(nativeHandoffSchema).max(10_000),
102
+ summary: z.strictObject({
103
+ matched_seeds: z.number().int().min(0),
104
+ traced_nodes: z.number().int().min(0),
105
+ traced_edges: z.number().int().min(0),
106
+ terminal_paths: z.number().int().min(0),
107
+ native_handoffs: z.number().int().min(0),
108
+ observed_facts: z.number().int().min(0),
109
+ inferred_facts: z.number().int().min(0),
110
+ unknown_facts: z.number().int().min(0),
111
+ unavailable_facts: z.number().int().min(0),
112
+ }),
113
+ coverage: z.strictObject({
114
+ status: z.enum([
115
+ "complete-within-source",
116
+ "partial",
117
+ "truncated",
118
+ "no-match",
119
+ ]),
120
+ source_graph_status: z.enum([
121
+ "complete",
122
+ "partial",
123
+ "unknown",
124
+ "unavailable",
125
+ ]),
126
+ scanned_nodes: z.number().int().min(0),
127
+ total_seed_matches: z.number().int().min(0),
128
+ omitted_seed_matches: z.number().int().min(0),
129
+ omitted_nodes: z.number().int().min(0),
130
+ omitted_edges: z.number().int().min(0),
131
+ omitted_paths: z.number().int().min(0),
132
+ frontier_node_ids: z.array(nodeIdSchema).max(50_000),
133
+ }),
134
+ evidence_links: z.array(evidenceIdSchema).min(1).max(65),
135
+ limitations: z.array(boundedTextSchema).max(1_000),
136
+ });
@@ -0,0 +1,88 @@
1
+ import { compareCodePoints, } from "./javascriptApplicationGraph.js";
2
+ /** Traverse a JAG without treating edge inference as observed reachability. */
3
+ export const traverseApplicationFeature = (graph, seedNodeIds, direction, limits) => {
4
+ const nodeById = new Map(graph.nodes.map((node) => [node.node_id, node]));
5
+ const adjacency = buildAdjacency(graph.edges, direction);
6
+ const visited = new Set(seedNodeIds);
7
+ const retainedEdges = new Map();
8
+ const omittedNodes = new Set();
9
+ const omittedEdges = new Set();
10
+ const frontier = new Set();
11
+ const depths = new Map(seedNodeIds.map((nodeId) => [nodeId, 0]));
12
+ const predecessors = new Map();
13
+ const queue = [...seedNodeIds];
14
+ for (let offset = 0; offset < queue.length; offset += 1) {
15
+ const current = queue[offset];
16
+ if (current === undefined)
17
+ continue;
18
+ const depth = depths.get(current) ?? 0;
19
+ for (const entry of adjacency.get(current) ?? []) {
20
+ if (depth >= limits.max_depth) {
21
+ recordOmission(entry, current, omittedNodes, omittedEdges, frontier);
22
+ continue;
23
+ }
24
+ if (!visited.has(entry.nextNodeId) && visited.size >= limits.max_nodes) {
25
+ recordOmission(entry, current, omittedNodes, omittedEdges, frontier);
26
+ continue;
27
+ }
28
+ if (!retainedEdges.has(entry.edge.edge_id) &&
29
+ retainedEdges.size >= limits.max_edges) {
30
+ omittedEdges.add(entry.edge.edge_id);
31
+ frontier.add(current);
32
+ frontier.add(entry.nextNodeId);
33
+ continue;
34
+ }
35
+ retainedEdges.set(entry.edge.edge_id, entry.edge);
36
+ if (visited.has(entry.nextNodeId))
37
+ continue;
38
+ if (!nodeById.has(entry.nextNodeId))
39
+ continue;
40
+ visited.add(entry.nextNodeId);
41
+ depths.set(entry.nextNodeId, depth + 1);
42
+ predecessors.set(entry.nextNodeId, {
43
+ previousNodeId: current,
44
+ edgeId: entry.edge.edge_id,
45
+ });
46
+ queue.push(entry.nextNodeId);
47
+ }
48
+ }
49
+ return {
50
+ nodes: [...visited]
51
+ .map((nodeId) => nodeById.get(nodeId))
52
+ .filter((node) => node !== undefined)
53
+ .sort((left, right) => compareCodePoints(left.node_id, right.node_id)),
54
+ edges: [...retainedEdges.values()].sort((left, right) => compareCodePoints(left.edge_id, right.edge_id)),
55
+ depths,
56
+ predecessors,
57
+ frontierNodeIds: [...frontier].sort(compareCodePoints),
58
+ omittedNodeCount: omittedNodes.size,
59
+ omittedEdgeCount: omittedEdges.size,
60
+ };
61
+ };
62
+ const buildAdjacency = (edges, direction) => {
63
+ const adjacency = new Map();
64
+ for (const edge of edges) {
65
+ if (direction !== "incoming")
66
+ addAdjacency(adjacency, edge.source_node_id, {
67
+ edge,
68
+ nextNodeId: edge.target_node_id,
69
+ });
70
+ if (direction !== "outgoing")
71
+ addAdjacency(adjacency, edge.target_node_id, {
72
+ edge,
73
+ nextNodeId: edge.source_node_id,
74
+ });
75
+ }
76
+ for (const entries of adjacency.values())
77
+ entries.sort((left, right) => compareCodePoints(`${left.edge.edge_id}\0${left.nextNodeId}`, `${right.edge.edge_id}\0${right.nextNodeId}`));
78
+ return adjacency;
79
+ };
80
+ const addAdjacency = (adjacency, nodeId, entry) => {
81
+ adjacency.set(nodeId, [...(adjacency.get(nodeId) ?? []), entry]);
82
+ };
83
+ const recordOmission = (entry, current, omittedNodes, omittedEdges, frontier) => {
84
+ omittedNodes.add(entry.nextNodeId);
85
+ omittedEdges.add(entry.edge.edge_id);
86
+ frontier.add(current);
87
+ frontier.add(entry.nextNodeId);
88
+ };
@@ -0,0 +1,85 @@
1
+ import { compareCodePoints, } from "./javascriptApplicationGraph.js";
2
+ /** Build provider-neutral addon targets and link exact-subject native Evidence. */
3
+ export const buildJavaScriptNativeHandoffs = (nodes, edges, nativeEvidence) => {
4
+ const nodeById = new Map(nodes.map((node) => [node.node_id, node]));
5
+ return nodes
6
+ .filter(({ kind }) => kind === "native-addon")
7
+ .flatMap((node) => {
8
+ const digest = artifactDigest(node);
9
+ if (digest === null)
10
+ return [];
11
+ const linked = nativeEvidence.filter(({ subject }) => subject?.digest.sha256 === digest);
12
+ const exportNodes = nativeExportNodes(node, edges, nodeById);
13
+ return [
14
+ {
15
+ native_node_id: node.node_id,
16
+ artifact_sha256: digest,
17
+ artifact_path: artifactPath(node),
18
+ export_node_ids: exportNodes.map(({ node_id: id }) => id),
19
+ requested_exports: requestedExports(exportNodes),
20
+ status: linked.length === 0
21
+ ? "requires-provider-analysis"
22
+ : "evidence-linked",
23
+ providers: uniqueProviders(linked),
24
+ evidence_ids: linked
25
+ .map(({ evidence_id: id }) => id)
26
+ .sort(compareCodePoints),
27
+ recommended_tools: [
28
+ "open_binary",
29
+ "binary_overview",
30
+ "search_procedures",
31
+ "analyze_function",
32
+ "xrefs",
33
+ ],
34
+ },
35
+ ];
36
+ })
37
+ .sort((left, right) => compareCodePoints(left.native_node_id, right.native_node_id));
38
+ };
39
+ const artifactDigest = (node) => {
40
+ if (node.identity.strategy === "content-digest")
41
+ return node.identity.sha256;
42
+ const observation = node.observations.find(({ evidence }) => evidence.artifact.available);
43
+ return observation?.evidence.artifact.available === true
44
+ ? observation.evidence.artifact.sha256
45
+ : null;
46
+ };
47
+ const artifactPath = (node) => {
48
+ for (const { evidence } of node.observations)
49
+ if (evidence.location.available &&
50
+ evidence.location.value.kind === "artifact-path")
51
+ return evidence.location.value.path;
52
+ return null;
53
+ };
54
+ const nativeExportNodes = (addon, edges, nodeById) => {
55
+ const ids = edges.flatMap((edge) => {
56
+ if (edge.source_node_id === addon.node_id &&
57
+ ["contains", "exposes"].includes(edge.relation))
58
+ return [edge.target_node_id];
59
+ if (edge.target_node_id === addon.node_id &&
60
+ ["imports", "loads"].includes(edge.relation))
61
+ return [edge.source_node_id];
62
+ return [];
63
+ });
64
+ return [...new Set(ids)]
65
+ .map((id) => nodeById.get(id))
66
+ .filter((node) => node?.kind === "native-export")
67
+ .sort((left, right) => compareCodePoints(left.node_id, right.node_id));
68
+ };
69
+ const requestedExports = (nodes) => {
70
+ const values = nodes.flatMap((node) => node.observations.flatMap((observation) => [
71
+ ...(observation.label === null ? [] : [observation.label]),
72
+ ...stringArray(observation.properties.requested_members),
73
+ ...stringArray(observation.properties.members),
74
+ ]));
75
+ return [...new Set(values)].sort(compareCodePoints).slice(0, 1_000);
76
+ };
77
+ const stringArray = (value) => Array.isArray(value)
78
+ ? value.filter((item) => typeof item === "string")
79
+ : [];
80
+ const uniqueProviders = (evidence) => [
81
+ ...new Map(evidence.map(({ provider }) => [
82
+ `${provider.id}\0${provider.name}\0${provider.version ?? ""}`,
83
+ provider,
84
+ ])).values(),
85
+ ].sort((left, right) => compareCodePoints(`${left.id}\0${left.name}\0${left.version ?? ""}`, `${right.id}\0${right.name}\0${right.version ?? ""}`));
@@ -0,0 +1,361 @@
1
+ import { z } from "zod";
2
+ import { evidenceEnvelopeSchema } from "./evidence.js";
3
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
4
+ const absolutePathSchema = z.string().startsWith("/").max(4096);
5
+ const aliasSchema = z
6
+ .string()
7
+ .min(1)
8
+ .max(256)
9
+ .regex(/^[A-Za-z0-9_./:@-]+$/u);
10
+ const replayModuleSchema = z
11
+ .object({
12
+ alias: aliasSchema,
13
+ path: absolutePathSchema,
14
+ format: z.enum(["esm", "commonjs-factory"]),
15
+ role: z.enum(["module", "stub"]).default("module"),
16
+ dependencies: z
17
+ .record(z.string().min(1).max(256), aliasSchema)
18
+ .refine((value) => Object.keys(value).length <= 128, {
19
+ message: "Replay modules may declare at most 128 dependencies",
20
+ })
21
+ .default({}),
22
+ })
23
+ .strict();
24
+ const replaySideSchema = z
25
+ .object({
26
+ modules: z.array(replayModuleSchema).min(1).max(128),
27
+ entry_alias: aliasSchema,
28
+ entry_export: z.string().min(1).max(256).default("default"),
29
+ })
30
+ .strict();
31
+ const explicitCaseSchema = z
32
+ .object({
33
+ case_id: z.string().min(1).max(128),
34
+ arguments: z.array(z.json()).max(16),
35
+ })
36
+ .strict();
37
+ const generatorSchema = z
38
+ .object({
39
+ preset: z.enum([
40
+ "parser-boundaries",
41
+ "sanitizer-boundaries",
42
+ "clipboard-boundaries",
43
+ ]),
44
+ seed: z.number().int().min(0).max(0xffff_ffff),
45
+ count: z.number().int().min(1).max(64),
46
+ })
47
+ .strict();
48
+ const replayLimitsSchema = z
49
+ .object({
50
+ wall_time_ms: z.number().int().min(100).max(10_000).default(3_000),
51
+ memory_bytes: z
52
+ .number()
53
+ .int()
54
+ .min(16 * 1024 * 1024)
55
+ .max(512 * 1024 * 1024)
56
+ .default(128 * 1024 * 1024),
57
+ tasks: z.number().int().min(1).max(32).default(8),
58
+ cpu_quota_percent: z.number().int().min(1).max(100).default(50),
59
+ tmpfs_bytes: z
60
+ .number()
61
+ .int()
62
+ .min(1024 * 1024)
63
+ .max(64 * 1024 * 1024)
64
+ .default(16 * 1024 * 1024),
65
+ module_bytes: z
66
+ .number()
67
+ .int()
68
+ .min(1)
69
+ .max(16 * 1024 * 1024)
70
+ .default(4 * 1024 * 1024),
71
+ input_bytes: z
72
+ .number()
73
+ .int()
74
+ .min(1)
75
+ .max(1024 * 1024)
76
+ .default(256 * 1024),
77
+ protocol_bytes: z
78
+ .number()
79
+ .int()
80
+ .min(1024 * 1024)
81
+ .max(64 * 1024 * 1024)
82
+ .default(16 * 1024 * 1024),
83
+ output_bytes: z
84
+ .number()
85
+ .int()
86
+ .min(1)
87
+ .max(4 * 1024 * 1024)
88
+ .default(512 * 1024),
89
+ stderr_bytes: z
90
+ .number()
91
+ .int()
92
+ .min(0)
93
+ .max(256 * 1024)
94
+ .default(32 * 1024),
95
+ result_depth: z.number().int().min(1).max(64).default(16),
96
+ result_nodes: z.number().int().min(1).max(100_000).default(10_000),
97
+ })
98
+ .strict();
99
+ const determinismSchema = z
100
+ .object({
101
+ clock_iso: z.iso.datetime().default("2000-01-01T00:00:00.000Z"),
102
+ random_seed: z.number().int().min(0).max(0xffff_ffff).default(0),
103
+ locale: z.literal("en-US").default("en-US"),
104
+ timezone: z.literal("UTC").default("UTC"),
105
+ platform: z.literal("linux").default("linux"),
106
+ })
107
+ .strict();
108
+ export const controlledReplayInputSchema = z
109
+ .object({
110
+ mode: z.enum(["plan", "execute"]),
111
+ left: replaySideSchema,
112
+ right: replaySideSchema.optional(),
113
+ cases: z.array(explicitCaseSchema).max(128).default([]),
114
+ generator: generatorSchema.optional(),
115
+ determinism: determinismSchema.default({
116
+ clock_iso: "2000-01-01T00:00:00.000Z",
117
+ random_seed: 0,
118
+ locale: "en-US",
119
+ timezone: "UTC",
120
+ platform: "linux",
121
+ }),
122
+ limits: replayLimitsSchema.default({
123
+ wall_time_ms: 3_000,
124
+ memory_bytes: 128 * 1024 * 1024,
125
+ tasks: 8,
126
+ cpu_quota_percent: 50,
127
+ tmpfs_bytes: 16 * 1024 * 1024,
128
+ module_bytes: 4 * 1024 * 1024,
129
+ input_bytes: 256 * 1024,
130
+ protocol_bytes: 16 * 1024 * 1024,
131
+ output_bytes: 512 * 1024,
132
+ stderr_bytes: 32 * 1024,
133
+ result_depth: 16,
134
+ result_nodes: 10_000,
135
+ }),
136
+ approved: z.boolean().default(false),
137
+ plan_digest: digestSchema.optional(),
138
+ reproducer_export: z
139
+ .object({
140
+ path: absolutePathSchema,
141
+ approved: z.boolean(),
142
+ include_sources: z.boolean().default(false),
143
+ })
144
+ .strict()
145
+ .optional(),
146
+ })
147
+ .strict()
148
+ .superRefine((value, context) => {
149
+ if (value.cases.length === 0 && value.generator === undefined)
150
+ context.addIssue({
151
+ code: "custom",
152
+ path: ["cases"],
153
+ message: "At least one explicit or generated case is required",
154
+ });
155
+ if (value.cases.length + (value.generator?.count ?? 0) > 128)
156
+ context.addIssue({
157
+ code: "custom",
158
+ path: ["generator", "count"],
159
+ message: "Explicit and generated replay cases must total at most 128",
160
+ });
161
+ if (value.mode === "execute" &&
162
+ (value.approved !== true || value.plan_digest === undefined))
163
+ context.addIssue({
164
+ code: "custom",
165
+ path: ["approved"],
166
+ message: "Execution requires literal approval and a plan digest",
167
+ });
168
+ });
169
+ const moduleCommitmentSchema = z
170
+ .object({
171
+ alias: aliasSchema,
172
+ canonical_path: z.string().min(1),
173
+ format: z.enum(["esm", "commonjs-factory"]),
174
+ role: z.enum(["module", "stub"]),
175
+ byte_count: z.number().int().min(0),
176
+ sha256: digestSchema,
177
+ dependencies: z.record(z.string(), aliasSchema),
178
+ })
179
+ .strict();
180
+ const runtimeIdentitySchema = z
181
+ .object({
182
+ path: z.string().min(1),
183
+ version: z.string().min(1),
184
+ sha256: digestSchema,
185
+ })
186
+ .strict();
187
+ const runtimeFileIdentitySchema = z
188
+ .object({
189
+ source_path: z.string().min(1),
190
+ destination_path: z.string().min(1),
191
+ sha256: digestSchema,
192
+ })
193
+ .strict();
194
+ const replayPlanSchema = z
195
+ .object({
196
+ schema_version: z.literal(1),
197
+ plan_digest: digestSchema,
198
+ policy_version: z.literal("linux-bwrap-systemd-v1"),
199
+ policy_sha256: digestSchema,
200
+ network: z.literal("none"),
201
+ filesystem: z
202
+ .object({
203
+ host_writes: z.literal(false),
204
+ private_tmpfs_bytes: z.number().int(),
205
+ })
206
+ .strict(),
207
+ runtime: z
208
+ .object({
209
+ executable: runtimeIdentitySchema,
210
+ worker: runtimeIdentitySchema,
211
+ read_only_files: z.array(runtimeFileIdentitySchema).min(1),
212
+ })
213
+ .strict(),
214
+ sandbox: z
215
+ .object({
216
+ bubblewrap: runtimeIdentitySchema,
217
+ systemd_run: runtimeIdentitySchema,
218
+ systemctl: runtimeIdentitySchema,
219
+ shell: runtimeIdentitySchema,
220
+ seccomp_sha256: digestSchema,
221
+ })
222
+ .strict(),
223
+ left: z
224
+ .object({
225
+ modules: z.array(moduleCommitmentSchema),
226
+ entry_alias: aliasSchema,
227
+ entry_export: z.string(),
228
+ })
229
+ .strict(),
230
+ right: z
231
+ .object({
232
+ modules: z.array(moduleCommitmentSchema),
233
+ entry_alias: aliasSchema,
234
+ entry_export: z.string(),
235
+ })
236
+ .strict()
237
+ .optional(),
238
+ cases: z.array(z
239
+ .object({
240
+ case_id: z.string(),
241
+ arguments: z.array(z.json()),
242
+ sha256: digestSchema,
243
+ })
244
+ .strict()),
245
+ determinism: determinismSchema,
246
+ limits: replayLimitsSchema,
247
+ effects: z.array(z.string()),
248
+ reproducer_export: z
249
+ .object({
250
+ path: z.string().min(1),
251
+ include_sources: z.boolean(),
252
+ authority: z.literal("evidence_write"),
253
+ })
254
+ .strict()
255
+ .nullable(),
256
+ })
257
+ .strict();
258
+ const replayOutcomeSchema = z
259
+ .object({
260
+ case_id: z.string(),
261
+ outcome: z.enum([
262
+ "return",
263
+ "exception",
264
+ "serialization_error",
265
+ "denied",
266
+ "timeout",
267
+ "oom",
268
+ "crash",
269
+ "cancelled",
270
+ "protocol_error",
271
+ ]),
272
+ value: z.json().optional(),
273
+ exception: z
274
+ .object({
275
+ name: z.string(),
276
+ message: z.string(),
277
+ stack: z.string().nullable(),
278
+ })
279
+ .strict()
280
+ .optional(),
281
+ input_sha256: digestSchema,
282
+ output_sha256: digestSchema.nullable(),
283
+ truncated: z.boolean(),
284
+ })
285
+ .strict();
286
+ export const replayExecutionResultSchema = z
287
+ .object({
288
+ schema_version: z.literal(1),
289
+ plan_digest: digestSchema,
290
+ outcomes: z.array(replayOutcomeSchema),
291
+ comparison: z
292
+ .array(z
293
+ .object({
294
+ case_id: z.string(),
295
+ status: z.enum(["equal", "changed", "unknown"]),
296
+ left_index: z.number().int(),
297
+ right_index: z.number().int(),
298
+ })
299
+ .strict())
300
+ .optional(),
301
+ stderr: z.string(),
302
+ termination: z.enum([
303
+ "completed",
304
+ "timeout",
305
+ "oom",
306
+ "crash",
307
+ "cancelled",
308
+ "protocol_error",
309
+ ]),
310
+ cleanup: z
311
+ .object({
312
+ state: z.enum(["complete", "incomplete"]),
313
+ residual_resources: z.array(z.string()),
314
+ })
315
+ .strict(),
316
+ limitations: z.array(z.string()),
317
+ reproducer: z
318
+ .discriminatedUnion("state", [
319
+ z
320
+ .object({
321
+ state: z.literal("written"),
322
+ path: z.string(),
323
+ sha256: digestSchema,
324
+ })
325
+ .strict(),
326
+ z
327
+ .object({
328
+ state: z.literal("failed"),
329
+ path: z.string(),
330
+ error: z.string(),
331
+ })
332
+ .strict(),
333
+ ])
334
+ .nullable(),
335
+ })
336
+ .strict();
337
+ export const replayEvidenceSchema = evidenceEnvelopeSchema
338
+ .omit({ normalized_result: true })
339
+ .extend({ normalized_result: replayExecutionResultSchema });
340
+ export const controlledReplayOutputSchema = z
341
+ .object({
342
+ phase: z.enum(["plan", "execute"]),
343
+ plan: replayPlanSchema.nullable(),
344
+ source_evidence: z.array(replayEvidenceSchema),
345
+ evidence: replayEvidenceSchema.nullable(),
346
+ })
347
+ .strict()
348
+ .superRefine((value, context) => {
349
+ if ((value.phase === "plan" &&
350
+ (value.plan === null ||
351
+ value.evidence !== null ||
352
+ value.source_evidence.length !== 0)) ||
353
+ (value.phase === "execute" &&
354
+ (value.plan !== null ||
355
+ value.evidence === null ||
356
+ value.source_evidence.length === 0)))
357
+ context.addIssue({
358
+ code: "custom",
359
+ message: "Replay output phase does not match its payload",
360
+ });
361
+ });