rea-agents 2.2.0 → 2.3.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 (165) hide show
  1. package/README.md +56 -9
  2. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  3. package/dist/application/BinarySession.js +9 -2
  4. package/dist/application/BinarySessionStatus.js +39 -1
  5. package/dist/application/CapabilityInventory.js +15 -0
  6. package/dist/application/CommandShimReplay.js +6 -3
  7. package/dist/application/CompositeProvider.js +5 -2
  8. package/dist/application/DirectAnalysis.js +1 -20
  9. package/dist/application/DirectAnalysisStatus.js +83 -0
  10. package/dist/application/Doctor.js +34 -25
  11. package/dist/application/DoctorDiagnostics.js +1 -4
  12. package/dist/application/DoctorProjection.js +43 -0
  13. package/dist/application/DoctorScope.js +102 -0
  14. package/dist/application/EvidenceReferenceResolver.js +26 -0
  15. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  16. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  17. package/dist/application/JavaScriptApplicationService.js +3 -2
  18. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  19. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  20. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  21. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  22. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  23. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  24. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  25. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  26. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  27. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  28. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  29. package/dist/application/LoopbackReplay.js +277 -133
  30. package/dist/application/LoopbackReplayRecorder.js +101 -0
  31. package/dist/application/PermissionAuthority.js +7 -3
  32. package/dist/application/ProcessCaptureJournal.js +95 -0
  33. package/dist/application/ProcessCaptureLifecycle.js +24 -13
  34. package/dist/application/ProcessCheckpoints.js +30 -4
  35. package/dist/application/ProcessCli.js +26 -4
  36. package/dist/application/ProcessHarness.js +20 -50
  37. package/dist/application/ProcessNormalization.js +3 -2
  38. package/dist/application/ProcessReactiveCoordinator.js +218 -0
  39. package/dist/application/ProcessReactiveEffects.js +133 -0
  40. package/dist/application/ProcessReactiveObservations.js +20 -0
  41. package/dist/application/ProcessSampling.js +2 -0
  42. package/dist/application/SessionProviderRouter.js +7 -6
  43. package/dist/application/Setup.js +11 -10
  44. package/dist/application/SetupPlan.js +1 -1
  45. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  46. package/dist/application/TerminalRenderer.js +3 -1
  47. package/dist/browser/CdpEndpoint.js +37 -4
  48. package/dist/catalogIdentity.js +16 -7
  49. package/dist/cli/coreAnalysisCommands.js +34 -2
  50. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  51. package/dist/cli/setupCommands.js +81 -32
  52. package/dist/cli/utilityCommands.js +10 -2
  53. package/dist/cliApplicationCommands.js +79 -16
  54. package/dist/cliCommandNames.js +3 -0
  55. package/dist/cliElectronCommands.js +23 -33
  56. package/dist/cliObservationOptions.js +1 -1
  57. package/dist/cliOutput.js +56 -0
  58. package/dist/cliProcessCommands.js +32 -1
  59. package/dist/cliSetup.js +30 -21
  60. package/dist/contracts/applicationToolContracts.js +54 -7
  61. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  62. package/dist/contracts/electronToolContracts.js +53 -32
  63. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  64. package/dist/contracts/processCaptureExample.js +1 -0
  65. package/dist/contracts/replayMachineExample.js +37 -0
  66. package/dist/contracts/toolContractExamples.js +2 -0
  67. package/dist/contracts/toolContracts.js +5 -1
  68. package/dist/contracts/toolEffects.js +3 -0
  69. package/dist/contracts/toolOutputSchemaGroups.js +2 -0
  70. package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
  71. package/dist/doctorRuntime.js +41 -0
  72. package/dist/domain/analysisErrorPresentation.js +5 -3
  73. package/dist/domain/analysisErrorProjection.js +7 -1
  74. package/dist/domain/changedBehavior.js +5 -4
  75. package/dist/domain/completionLedgerGeneration.js +190 -0
  76. package/dist/domain/errors.js +4 -2
  77. package/dist/domain/evidenceCompletionLedger.js +113 -0
  78. package/dist/domain/hopperStartupFailure.js +9 -0
  79. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  80. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  81. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  82. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  83. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  84. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  85. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  86. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  87. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  88. package/dist/domain/javascriptSemanticCalls.js +269 -0
  89. package/dist/domain/javascriptSemanticGraph.js +356 -0
  90. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  91. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  92. package/dist/domain/javascriptSemanticIr.js +14 -1
  93. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  94. package/dist/domain/javascriptSemanticProjection.js +36 -4
  95. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  96. package/dist/domain/javascriptSemanticQuery.js +294 -0
  97. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  98. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  99. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  100. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  101. package/dist/domain/javascriptSemanticReturns.js +187 -0
  102. package/dist/domain/javascriptSemanticState.js +32 -0
  103. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  104. package/dist/domain/javascriptSemanticValues.js +54 -70
  105. package/dist/domain/processCapture.js +36 -0
  106. package/dist/domain/processCaptureValidation.js +101 -12
  107. package/dist/domain/processComparison.js +78 -25
  108. package/dist/domain/processObservation.js +153 -0
  109. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  110. package/dist/domain/processReactiveMatching.js +210 -0
  111. package/dist/domain/processReactiveRuntime.js +109 -0
  112. package/dist/domain/processReactiveScenario.js +361 -0
  113. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  114. package/dist/domain/processReactiveTransition.js +159 -0
  115. package/dist/domain/processScenario.js +12 -0
  116. package/dist/domain/processTraceComparison.js +43 -0
  117. package/dist/domain/processTraceDimensionProjection.js +50 -0
  118. package/dist/domain/processTraceEvaluation.js +312 -0
  119. package/dist/domain/processTraceSpecification.js +373 -0
  120. package/dist/domain/reconstructionVerification.js +5 -4
  121. package/dist/domain/replayMachine.js +18 -2
  122. package/dist/domain/replayMachineRun.js +216 -0
  123. package/dist/domain/replayMachineRuntime.js +88 -9
  124. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  125. package/dist/evaluation/CodexAgentEval.js +24 -0
  126. package/dist/generatedPackageMetadata.js +1 -1
  127. package/dist/ghidra/GhidraClient.js +13 -2
  128. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  129. package/dist/ghidra/GhidraLauncher.js +7 -3
  130. package/dist/ghidra/GhidraProvider.js +8 -1
  131. package/dist/hopper/BridgeLauncher.js +56 -14
  132. package/dist/hopper/HopperClient.js +23 -10
  133. package/dist/hopper/HopperDiagnostics.js +14 -0
  134. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  135. package/dist/hopper/HopperProvider.js +8 -1
  136. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  137. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  138. package/dist/main/transport.js +2 -0
  139. package/dist/mcpDoctor.js +310 -0
  140. package/dist/process/ProcessOwnership.js +200 -53
  141. package/dist/process/ProviderRunLineage.js +21 -0
  142. package/dist/server/createServer.js +4 -1
  143. package/dist/server/javascriptApplicationResult.js +22 -1
  144. package/dist/server/registerApplicationTools/characterization.js +3 -3
  145. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  146. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  147. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  148. package/dist/server/registerApplicationTools/coverage.js +3 -3
  149. package/dist/server/registerApplicationTools/helpers.js +37 -25
  150. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  151. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  152. package/dist/server/registerApplicationTools.js +4 -0
  153. package/dist/server/registerEvidenceResources.js +5 -1
  154. package/dist/server/registerProcessComparisonTool.js +17 -7
  155. package/dist/server/registerReplayMachineTool.js +20 -0
  156. package/dist/server/registerSessionTools.js +12 -7
  157. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  158. package/dist/serverIdentity.js +19 -0
  159. package/package.json +62 -30
  160. package/scripts/hopper-demo-x11.py +459 -61
  161. package/scripts/prepack.mjs +55 -0
  162. package/scripts/prepare.mjs +28 -0
  163. package/scripts/rea.mjs +26 -5
  164. package/skills/reverse-engineer-anything/SKILL.md +2 -2
  165. package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
@@ -0,0 +1,294 @@
1
+ import { compareCodePoints } from "./javascriptApplicationGraph.js";
2
+ import { isJavaScriptSemanticOwnershipRelation as ownershipRelation } from "./javascriptSemanticQueryRelations.js";
3
+ import { javaScriptSemanticQueryInputSchema, javaScriptSemanticQueryResultSchema, } from "./javascriptSemanticQuerySchemas.js";
4
+ import { assessJavaScriptSemanticQuery, } from "./javascriptSemanticQueryAssessment.js";
5
+ import { canonicalJavaScriptSemanticQueryJson as canonicalJson, createJavaScriptSemanticQueryCursor as createCursor, JavaScriptSemanticQueryCursorError, javaScriptSemanticQueryIdentifier as queryIdentifier, parseJavaScriptSemanticQueryCursor as parseCursor, resolveJavaScriptSemanticQuerySeeds as resolveSeeds, } from "./javascriptSemanticQueryIdentity.js";
6
+ /** Run one deterministic bounded traversal over a verified semantic graph. */
7
+ export const queryJavaScriptSemanticGraph = (graph, rawInput) => {
8
+ const input = javaScriptSemanticQueryInputSchema.parse(rawInput);
9
+ const queryId = queryIdentifier(graph, input);
10
+ const offset = parseCursor(input.cursor, queryId);
11
+ const seeds = resolveSeeds(graph, input);
12
+ const admission = admitSeeds(graph, seeds, input);
13
+ const retainedSeeds = admission.nodeIds;
14
+ const adjacency = buildAdjacency(graph.relations, input);
15
+ const traversal = traverse(graph, retainedSeeds, adjacency, input);
16
+ const frontier = uniqueFrontier([
17
+ ...traversal.frontier,
18
+ ...admission.frontier,
19
+ ]);
20
+ const page = createQueryPage({
21
+ graph,
22
+ traversal,
23
+ query: input,
24
+ queryId,
25
+ offset,
26
+ retainedSeeds,
27
+ });
28
+ const allRelevantUnknowns = relevantUnknownFrontiers(graph, traversal.nodeIds, input);
29
+ const relevantUnknowns = allRelevantUnknowns.slice(0, input.limits.max_unknowns);
30
+ const candidateRelations = relevantCandidateRelationCount(graph, traversal.nodeIds, input);
31
+ const expectedMatches = expectedMatchesFor(page.retainedNodes, page.retainedRelations, input);
32
+ const assessment = assessJavaScriptSemanticQuery({
33
+ graph,
34
+ totalSeeds: seeds.length,
35
+ retainedSeeds: retainedSeeds.length,
36
+ expectedMatches: expectedMatches.length,
37
+ hasExpectation: input.expected !== null,
38
+ frontier,
39
+ unknowns: relevantUnknowns,
40
+ candidateRelations,
41
+ unknownsTruncated: allRelevantUnknowns.length > relevantUnknowns.length,
42
+ });
43
+ return javaScriptSemanticQueryResultSchema.parse({
44
+ schema_version: 1,
45
+ query_id: queryId,
46
+ source_graph_id: graph.graph_id,
47
+ seed: input.seed,
48
+ direction: input.direction,
49
+ status: assessment.status,
50
+ seed_node_ids: retainedSeeds,
51
+ nodes: page.pageNodes,
52
+ relations: page.pageRelations,
53
+ unknowns: relevantUnknowns,
54
+ expected_match_node_ids: expectedMatches.map(({ node_id }) => node_id),
55
+ summary: {
56
+ total_seed_matches: seeds.length,
57
+ retained_seed_matches: retainedSeeds.length,
58
+ traversed_nodes: traversal.nodeIds.size,
59
+ traversed_relations: traversal.relationIds.size,
60
+ traversed_functions: traversal.functionIds.size,
61
+ traversed_modules: traversal.modules.size,
62
+ relevant_unknowns: allRelevantUnknowns.length,
63
+ retained_unknowns: relevantUnknowns.length,
64
+ },
65
+ coverage: assessment.coverage,
66
+ page: {
67
+ offset,
68
+ size: page.pageRelations.length,
69
+ next_cursor: page.nextCursor,
70
+ },
71
+ applied_limits: input.limits,
72
+ accepted_limit_ranges: assessment.acceptedLimitRanges,
73
+ limitations: assessment.limitations,
74
+ });
75
+ };
76
+ const createQueryPage = ({ graph, traversal, query, queryId, offset, retainedSeeds, }) => {
77
+ const retainedNodes = graph.nodes.filter(({ node_id }) => traversal.nodeIds.has(node_id));
78
+ const retainedRelations = graph.relations.filter(({ relation_id }) => traversal.relationIds.has(relation_id));
79
+ if (offset > retainedRelations.length)
80
+ throw new JavaScriptSemanticQueryCursorError("Semantic query cursor is beyond the retained result");
81
+ const pageRelations = retainedRelations.slice(offset, offset + query.limits.page_size);
82
+ const pageNodeIds = new Set([
83
+ ...retainedSeeds,
84
+ ...pageRelations.flatMap(({ source_node_id, target_node_id }) => [
85
+ source_node_id,
86
+ target_node_id,
87
+ ]),
88
+ ]);
89
+ const pageNodes = graph.nodes.filter(({ node_id }) => pageNodeIds.has(node_id));
90
+ const nextOffset = offset + pageRelations.length;
91
+ return {
92
+ retainedNodes,
93
+ retainedRelations,
94
+ pageRelations,
95
+ pageNodes,
96
+ nextCursor: nextOffset < retainedRelations.length
97
+ ? createCursor(queryId, nextOffset)
98
+ : null,
99
+ };
100
+ };
101
+ const admitSeeds = (graph, seeds, input) => {
102
+ const nodes = new Map(graph.nodes.map((node) => [node.node_id, node]));
103
+ const retained = [];
104
+ const functions = new Set();
105
+ const modules = new Set();
106
+ const frontier = [];
107
+ for (const nodeId of seeds) {
108
+ const node = nodes.get(nodeId);
109
+ if (node === undefined)
110
+ continue;
111
+ const reason = seedBlockedReason(node, {
112
+ retained,
113
+ functions,
114
+ modules,
115
+ input,
116
+ });
117
+ if (reason !== null) {
118
+ frontier.push({ node_id: nodeId, depth: 0, reason });
119
+ continue;
120
+ }
121
+ retained.push(nodeId);
122
+ retainOwners(node, functions, modules);
123
+ }
124
+ return { nodeIds: retained, frontier: uniqueFrontier(frontier) };
125
+ };
126
+ const seedBlockedReason = (node, bounds) => {
127
+ if (bounds.retained.length >= bounds.input.limits.max_seed_matches)
128
+ return "max-seed-matches";
129
+ if (bounds.retained.length >= bounds.input.limits.max_nodes)
130
+ return "max-nodes";
131
+ const functionId = functionOwner(node);
132
+ if (functionId !== null &&
133
+ !bounds.functions.has(functionId) &&
134
+ bounds.functions.size >= bounds.input.limits.max_functions)
135
+ return "max-functions";
136
+ if (!bounds.modules.has(node.identity.module_path) &&
137
+ bounds.modules.size >= bounds.input.limits.max_modules)
138
+ return "max-modules";
139
+ return null;
140
+ };
141
+ const buildAdjacency = (relations, input) => {
142
+ const adjacency = new Map();
143
+ const allowed = input.allowed_relations === undefined
144
+ ? null
145
+ : new Set(input.allowed_relations);
146
+ for (const relation of relations) {
147
+ if (allowed !== null && !allowed.has(relation.relation))
148
+ continue;
149
+ if (relation.resolution === "candidate" &&
150
+ !input.include_ambiguous_dynamic_edges)
151
+ continue;
152
+ addDirectedEntries(adjacency, relation, input.direction);
153
+ }
154
+ for (const entries of adjacency.values())
155
+ entries.sort((left, right) => compareCodePoints(`${left.relation.relation_id}\0${left.nextNodeId}`, `${right.relation.relation_id}\0${right.nextNodeId}`));
156
+ return adjacency;
157
+ };
158
+ const addDirectedEntries = (adjacency, relation, direction) => {
159
+ if (direction === "callers") {
160
+ if (relation.relation === "calls")
161
+ addEntry(adjacency, relation.target_node_id, relation.source_node_id, relation);
162
+ return;
163
+ }
164
+ if (direction === "ownership") {
165
+ if (!ownershipRelation(relation.relation))
166
+ return;
167
+ addEntry(adjacency, relation.source_node_id, relation.target_node_id, relation);
168
+ addEntry(adjacency, relation.target_node_id, relation.source_node_id, relation);
169
+ return;
170
+ }
171
+ if (direction === "backward-provenance")
172
+ addEntry(adjacency, relation.target_node_id, relation.source_node_id, relation);
173
+ else
174
+ addEntry(adjacency, relation.source_node_id, relation.target_node_id, relation);
175
+ };
176
+ const addEntry = (adjacency, from, nextNodeId, relation) => {
177
+ const entries = adjacency.get(from) ?? [];
178
+ entries.push({ relation, nextNodeId });
179
+ adjacency.set(from, entries);
180
+ };
181
+ const traverse = (graph, seeds, adjacency, input) => {
182
+ const nodes = new Map(graph.nodes.map((node) => [node.node_id, node]));
183
+ const nodeIds = new Set(seeds);
184
+ const relationIds = new Set();
185
+ const functionIds = new Set();
186
+ const modules = new Set();
187
+ const frontier = [];
188
+ for (const nodeId of nodeIds)
189
+ retainOwners(nodes.get(nodeId), functionIds, modules);
190
+ const depths = new Map([...nodeIds].map((nodeId) => [nodeId, 0]));
191
+ const queue = [...nodeIds];
192
+ const bounds = {
193
+ nodes,
194
+ nodeIds,
195
+ relationIds,
196
+ functionIds,
197
+ modules,
198
+ input,
199
+ };
200
+ for (let offset = 0; offset < queue.length; offset += 1) {
201
+ const current = queue[offset];
202
+ if (current === undefined)
203
+ continue;
204
+ const depth = depths.get(current) ?? 0;
205
+ for (const entry of adjacency.get(current) ?? []) {
206
+ const reason = blockedReason(entry, depth, bounds);
207
+ if (reason !== null) {
208
+ frontier.push({ node_id: current, depth, reason });
209
+ continue;
210
+ }
211
+ relationIds.add(entry.relation.relation_id);
212
+ if (nodeIds.has(entry.nextNodeId))
213
+ continue;
214
+ const node = nodes.get(entry.nextNodeId);
215
+ if (node === undefined)
216
+ continue;
217
+ nodeIds.add(entry.nextNodeId);
218
+ retainOwners(node, functionIds, modules);
219
+ depths.set(entry.nextNodeId, depth + 1);
220
+ queue.push(entry.nextNodeId);
221
+ }
222
+ }
223
+ return {
224
+ nodeIds,
225
+ relationIds,
226
+ functionIds,
227
+ modules,
228
+ frontier: uniqueFrontier(frontier),
229
+ };
230
+ };
231
+ const blockedReason = (entry, depth, bounds) => {
232
+ if (depth >= bounds.input.limits.max_depth)
233
+ return "max-depth";
234
+ if (!bounds.relationIds.has(entry.relation.relation_id) &&
235
+ bounds.relationIds.size >= bounds.input.limits.max_edges)
236
+ return "max-edges";
237
+ if (bounds.nodeIds.has(entry.nextNodeId))
238
+ return null;
239
+ if (bounds.nodeIds.size >= bounds.input.limits.max_nodes)
240
+ return "max-nodes";
241
+ const node = bounds.nodes.get(entry.nextNodeId);
242
+ if (node === undefined)
243
+ return null;
244
+ const functionId = functionOwner(node);
245
+ if (functionId !== null &&
246
+ !bounds.functionIds.has(functionId) &&
247
+ bounds.functionIds.size >= bounds.input.limits.max_functions)
248
+ return "max-functions";
249
+ if (!bounds.modules.has(node.identity.module_path) &&
250
+ bounds.modules.size >= bounds.input.limits.max_modules)
251
+ return "max-modules";
252
+ return null;
253
+ };
254
+ const retainOwners = (node, functions, modules) => {
255
+ if (node === undefined)
256
+ return;
257
+ const functionId = functionOwner(node);
258
+ if (functionId !== null)
259
+ functions.add(functionId);
260
+ modules.add(node.identity.module_path);
261
+ };
262
+ const functionOwner = (node) => node.kind === "function" ? node.node_id : node.function_node_id;
263
+ const uniqueFrontier = (frontier) => [
264
+ ...new Map(frontier.map((item) => [
265
+ `${item.node_id}\0${String(item.depth)}\0${item.reason}`,
266
+ item,
267
+ ])).values(),
268
+ ].sort((left, right) => compareCodePoints(canonicalJson(left), canonicalJson(right)));
269
+ const relevantUnknownFrontiers = (graph, nodeIds, input) => {
270
+ const allowed = input.allowed_relations === undefined
271
+ ? null
272
+ : new Set(input.allowed_relations);
273
+ return graph.unknowns.filter((unknown) => (unknown.node_id === null || nodeIds.has(unknown.node_id)) &&
274
+ (allowed === null ||
275
+ unknown.relation_kinds.some((relation) => allowed.has(relation))));
276
+ };
277
+ const relevantCandidateRelationCount = (graph, nodeIds, input) => {
278
+ const allowed = input.allowed_relations === undefined
279
+ ? null
280
+ : new Set(input.allowed_relations);
281
+ return graph.relations.filter((relation) => relation.resolution === "candidate" &&
282
+ (allowed === null || allowed.has(relation.relation)) &&
283
+ (nodeIds.has(relation.source_node_id) ||
284
+ nodeIds.has(relation.target_node_id))).length;
285
+ };
286
+ const expectedMatchesFor = (nodes, relations, input) => {
287
+ if (input.expected === null)
288
+ return [];
289
+ const classes = new Set(input.expected.classes);
290
+ const connected = new Set(relations.map((relation) => input.expected?.role === "source"
291
+ ? relation.target_node_id
292
+ : relation.source_node_id));
293
+ return nodes.filter(({ kind, node_id }) => classes.has(kind) && !connected.has(node_id));
294
+ };
@@ -0,0 +1,74 @@
1
+ import { compareCodePoints } from "./javascriptApplicationGraph.js";
2
+ /** Classify a bounded semantic traversal without promoting unknowns to absence. */
3
+ export const assessJavaScriptSemanticQuery = (input) => ({
4
+ status: queryStatus(input),
5
+ coverage: queryCoverage(input),
6
+ limitations: queryLimitations(input),
7
+ acceptedLimitRanges: {
8
+ max_seed_matches: { minimum: 1, maximum: 1_000 },
9
+ max_nodes: { minimum: 1, maximum: 50_000 },
10
+ max_edges: { minimum: 1, maximum: 100_000 },
11
+ max_depth: { minimum: 0, maximum: 64 },
12
+ max_functions: { minimum: 1, maximum: 10_000 },
13
+ max_modules: { minimum: 1, maximum: 10_000 },
14
+ max_unknowns: { minimum: 0, maximum: 10_000 },
15
+ page_size: { minimum: 1, maximum: 1_000 },
16
+ },
17
+ });
18
+ const queryCoverage = (input) => ({
19
+ status: input.graph.coverage.status === "unavailable"
20
+ ? "unavailable"
21
+ : input.frontier.length > 0 || input.unknownsTruncated
22
+ ? "truncated"
23
+ : input.unknowns.length > 0 ||
24
+ input.candidateRelations > 0 ||
25
+ input.graph.coverage.status !== "complete"
26
+ ? "partial"
27
+ : "complete",
28
+ frontier: [...input.frontier],
29
+ });
30
+ const queryStatus = (input) => {
31
+ if (input.graph.coverage.status === "unavailable")
32
+ return "unsupported";
33
+ if (input.frontier.length > 0 || input.unknownsTruncated)
34
+ return "truncated";
35
+ if (input.candidateRelations > 0)
36
+ return "ambiguous";
37
+ if (input.totalSeeds === 0 ||
38
+ (input.hasExpectation && input.expectedMatches === 0))
39
+ return input.unknowns.length > 0 ||
40
+ input.graph.coverage.status !== "complete"
41
+ ? "partial"
42
+ : "no-match";
43
+ return input.totalSeeds > 1 ? "ambiguous" : "found";
44
+ };
45
+ const queryLimitations = (input) => uniqueSorted([
46
+ "Semantic graph reachability is a static inference and does not prove runtime execution.",
47
+ ...input.graph.limitations,
48
+ ...(input.totalSeeds === input.retainedSeeds
49
+ ? []
50
+ : [
51
+ "Seed matches exceeded caller bounds; omitted starts remain unexplored.",
52
+ ]),
53
+ ...(input.frontier.length === 0
54
+ ? []
55
+ : [
56
+ "Traversal stopped at explicit caller limits; frontier facts remain unknown.",
57
+ ]),
58
+ ...(input.unknowns.length === 0
59
+ ? []
60
+ : [
61
+ "Relevant dynamic, unsupported, incomplete, or ambiguous semantics remain unknown.",
62
+ ]),
63
+ ...(input.unknownsTruncated
64
+ ? [
65
+ "Relevant unknown frontiers exceeded the caller limit; omitted unknowns remain unresolved.",
66
+ ]
67
+ : []),
68
+ ...(input.candidateRelations === 0
69
+ ? []
70
+ : [
71
+ "Candidate relations remain ambiguous and do not establish a resolved semantic path.",
72
+ ]),
73
+ ]);
74
+ const uniqueSorted = (values) => [...new Set(values)].sort(compareCodePoints);
@@ -0,0 +1,86 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { compareCodePoints } from "./javascriptApplicationGraph.js";
4
+ /** Caller cursor that cannot identify a page of the current semantic query. */
5
+ export class JavaScriptSemanticQueryCursorError extends TypeError {
6
+ }
7
+ /** Canonical JSON used by semantic query commitments and ordering. */
8
+ export const canonicalJavaScriptSemanticQueryJson = (value) => {
9
+ const encoded = canonicalize(value);
10
+ if (encoded === undefined)
11
+ throw new TypeError("JavaScript semantic query could not canonicalize data");
12
+ return encoded;
13
+ };
14
+ const digest = (value) => createHash("sha256")
15
+ .update(canonicalJavaScriptSemanticQueryJson(value))
16
+ .digest("hex");
17
+ const uniqueSorted = (values) => [...new Set(values)].sort(compareCodePoints);
18
+ /** Derive the stable identity for one graph-bound query without its cursor. */
19
+ export const javaScriptSemanticQueryIdentifier = (graph, input) => {
20
+ const { cursor: _cursor, ...semantic } = input;
21
+ return `jsrq_${digest({
22
+ source_graph_id: graph.graph_id,
23
+ ...semantic,
24
+ allowed_relations: semantic.allowed_relations === undefined
25
+ ? undefined
26
+ : uniqueSorted(semantic.allowed_relations),
27
+ expected: semantic.expected === null
28
+ ? null
29
+ : {
30
+ ...semantic.expected,
31
+ classes: uniqueSorted(semantic.expected.classes),
32
+ },
33
+ })}`;
34
+ };
35
+ /** Commit one deterministic relation-page offset to a query identity. */
36
+ export const createJavaScriptSemanticQueryCursor = (queryId, offset) => `jsrqc_${String(offset)}_${digest({ query_id: queryId, offset })}`;
37
+ /** Parse a query-bound cursor and reject stale, malformed, or foreign values. */
38
+ export const parseJavaScriptSemanticQueryCursor = (cursor, queryId) => {
39
+ if (cursor === null)
40
+ return 0;
41
+ const match = /^jsrqc_([0-9]+)_([a-f0-9]{64})$/u.exec(cursor);
42
+ const offsetText = match?.[1];
43
+ const commitment = match?.[2];
44
+ if (offsetText === undefined || commitment === undefined)
45
+ throw new JavaScriptSemanticQueryCursorError("Semantic query cursor is malformed");
46
+ const offset = Number(offsetText);
47
+ if (!Number.isSafeInteger(offset) ||
48
+ offset < 0 ||
49
+ commitment !== digest({ query_id: queryId, offset }))
50
+ throw new JavaScriptSemanticQueryCursorError("Semantic query cursor does not match this query");
51
+ return offset;
52
+ };
53
+ /** Resolve deterministic seed candidates without applying traversal limits. */
54
+ export const resolveJavaScriptSemanticQuerySeeds = (graph, input) => {
55
+ const fingerprintFunctions = new Set(input.seed.kind === "function"
56
+ ? graph.fingerprints
57
+ .filter(({ fingerprint_sha256 }) => input.seed.kind === "function" &&
58
+ fingerprint_sha256 === input.seed.fingerprint_sha256)
59
+ .map(({ function_node_id }) => function_node_id)
60
+ : []);
61
+ return graph.nodes
62
+ .filter((node) => seedMatches(node, input, fingerprintFunctions))
63
+ .map(({ node_id }) => node_id)
64
+ .sort(compareCodePoints);
65
+ };
66
+ const seedMatches = (node, input, fingerprintFunctions) => {
67
+ const seed = input.seed;
68
+ if (seed.kind === "semantic-node")
69
+ return node.node_id === seed.node_id;
70
+ if (seed.kind === "application-node")
71
+ return node.application_node_ids.includes(seed.node_id);
72
+ if (seed.kind === "function")
73
+ return fingerprintFunctions.has(node.node_id);
74
+ if (seed.kind === "literal")
75
+ return (node.kind === "literal" &&
76
+ canonicalJavaScriptSemanticQueryJson(node.properties.value) ===
77
+ canonicalJavaScriptSemanticQueryJson(seed.value));
78
+ if (seed.kind === "property")
79
+ return node.kind === "property-slot" && node.properties.name === seed.name;
80
+ if (seed.kind === "endpoint")
81
+ return node.kind === "request" && node.properties.endpoint === seed.value;
82
+ if (seed.kind === "event")
83
+ return (["event", "listener"].includes(node.kind) &&
84
+ node.properties.event_name === seed.name);
85
+ return node.kind === "boundary" && node.properties.field === seed.field;
86
+ };
@@ -0,0 +1,15 @@
1
+ const OWNERSHIP_RELATIONS = new Set([
2
+ "acquires",
3
+ "aggregates",
4
+ "awaits",
5
+ "chains",
6
+ "connects-stdio",
7
+ "creates-promise",
8
+ "detaches-task",
9
+ "owns",
10
+ "releases",
11
+ "returns-task",
12
+ "spawns",
13
+ ]);
14
+ /** Whether one relation participates in bidirectional ownership traversal. */
15
+ export const isJavaScriptSemanticOwnershipRelation = (relation) => OWNERSHIP_RELATIONS.has(relation);
@@ -0,0 +1,188 @@
1
+ import { z } from "zod";
2
+ import { JAVASCRIPT_SEMANTIC_NODE_KINDS, JAVASCRIPT_SEMANTIC_RELATIONS, javaScriptSemanticNodeSchema, javaScriptSemanticRelationSchema, javaScriptSemanticUnknownSchema, } from "./javascriptSemanticGraphSchemas.js";
3
+ import { jsonValueSchema } from "./jsonValue.js";
4
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
5
+ const semanticNodeIdSchema = z.string().regex(/^jsrg_node_[a-f0-9]{64}$/u);
6
+ const literalSeedSchema = z.strictObject({
7
+ kind: z.literal("literal"),
8
+ value: jsonValueSchema.refine((value) => value === null ||
9
+ typeof value === "string" ||
10
+ typeof value === "number" ||
11
+ typeof value === "boolean", "Literal seeds accept only JSON primitive values"),
12
+ });
13
+ /** Authenticated starting points accepted by semantic tracing. */
14
+ export const javaScriptSemanticQuerySeedSchema = z.discriminatedUnion("kind", [
15
+ z.strictObject({
16
+ kind: z.literal("semantic-node"),
17
+ node_id: semanticNodeIdSchema,
18
+ }),
19
+ z.strictObject({
20
+ kind: z.literal("application-node"),
21
+ node_id: z.string().regex(/^jag_node_[a-f0-9]{64}$/u),
22
+ }),
23
+ literalSeedSchema,
24
+ z.strictObject({
25
+ kind: z.literal("function"),
26
+ fingerprint_sha256: digestSchema,
27
+ }),
28
+ z.strictObject({
29
+ kind: z.literal("property"),
30
+ name: z.string().min(1).max(4_096),
31
+ }),
32
+ z.strictObject({
33
+ kind: z.literal("endpoint"),
34
+ value: z.string().min(1).max(16_384),
35
+ }),
36
+ z.strictObject({
37
+ kind: z.literal("event"),
38
+ name: z.string().min(1).max(4_096),
39
+ }),
40
+ z.strictObject({
41
+ kind: z.literal("boundary-field"),
42
+ field: z.string().min(1).max(4_096),
43
+ }),
44
+ ]);
45
+ /** Strict caller limits for deterministic bounded traversal. */
46
+ export const javaScriptSemanticQueryLimitsSchema = z.strictObject({
47
+ max_seed_matches: z.number().int().min(1).max(1_000).default(25),
48
+ max_nodes: z.number().int().min(1).max(50_000).default(2_000),
49
+ max_edges: z.number().int().min(1).max(100_000).default(4_000),
50
+ max_depth: z.number().int().min(0).max(64).default(12),
51
+ max_functions: z.number().int().min(1).max(10_000).default(500),
52
+ max_modules: z.number().int().min(1).max(10_000).default(500),
53
+ max_unknowns: z.number().int().min(0).max(10_000).default(1_000),
54
+ page_size: z.number().int().min(1).max(1_000).default(200),
55
+ });
56
+ const sourceMapAuthoritySchema = z.strictObject({
57
+ authority: z.literal("none"),
58
+ });
59
+ /** Parsed pure-domain query over one authenticated companion graph. */
60
+ export const javaScriptSemanticQueryInputSchema = z.strictObject({
61
+ seed: javaScriptSemanticQuerySeedSchema,
62
+ direction: z.enum([
63
+ "backward-provenance",
64
+ "forward-influence",
65
+ "callers",
66
+ "ownership",
67
+ ]),
68
+ allowed_relations: z
69
+ .array(z.enum(JAVASCRIPT_SEMANTIC_RELATIONS))
70
+ .min(1)
71
+ .max(JAVASCRIPT_SEMANTIC_RELATIONS.length)
72
+ .optional(),
73
+ include_ambiguous_dynamic_edges: z.boolean().default(false),
74
+ expected: z
75
+ .strictObject({
76
+ role: z.enum(["source", "sink"]),
77
+ classes: z.array(z.enum(JAVASCRIPT_SEMANTIC_NODE_KINDS)).min(1).max(24),
78
+ })
79
+ .nullable()
80
+ .default(null),
81
+ source_map_authority: sourceMapAuthoritySchema.default({ authority: "none" }),
82
+ limits: javaScriptSemanticQueryLimitsSchema.default({
83
+ max_seed_matches: 25,
84
+ max_nodes: 2_000,
85
+ max_edges: 4_000,
86
+ max_depth: 12,
87
+ max_functions: 500,
88
+ max_modules: 500,
89
+ max_unknowns: 1_000,
90
+ page_size: 200,
91
+ }),
92
+ cursor: z
93
+ .string()
94
+ .regex(/^jsrqc_[0-9]+_[a-f0-9]{64}$/u)
95
+ .nullable()
96
+ .default(null),
97
+ });
98
+ const queryFrontierSchema = z.strictObject({
99
+ node_id: semanticNodeIdSchema,
100
+ depth: z.number().int().min(0).max(65),
101
+ reason: z.enum([
102
+ "max-depth",
103
+ "max-edges",
104
+ "max-functions",
105
+ "max-modules",
106
+ "max-nodes",
107
+ "max-seed-matches",
108
+ ]),
109
+ });
110
+ /** Deterministic, pageable semantic trace result. */
111
+ export const javaScriptSemanticQueryResultSchema = z.strictObject({
112
+ schema_version: z.literal(1),
113
+ query_id: z.string().regex(/^jsrq_[a-f0-9]{64}$/u),
114
+ source_graph_id: z.string().regex(/^jsrg_[a-f0-9]{64}$/u),
115
+ seed: javaScriptSemanticQuerySeedSchema,
116
+ direction: javaScriptSemanticQueryInputSchema.shape.direction,
117
+ status: z.enum([
118
+ "found",
119
+ "no-match",
120
+ "ambiguous",
121
+ "partial",
122
+ "truncated",
123
+ "unsupported",
124
+ ]),
125
+ seed_node_ids: z.array(semanticNodeIdSchema).max(1_000),
126
+ nodes: z.array(javaScriptSemanticNodeSchema).max(3_000),
127
+ relations: z.array(javaScriptSemanticRelationSchema).max(1_000),
128
+ unknowns: z.array(javaScriptSemanticUnknownSchema).max(100_000),
129
+ expected_match_node_ids: z.array(semanticNodeIdSchema).max(50_000),
130
+ summary: z.strictObject({
131
+ total_seed_matches: z.number().int().min(0),
132
+ retained_seed_matches: z.number().int().min(0),
133
+ traversed_nodes: z.number().int().min(0),
134
+ traversed_relations: z.number().int().min(0),
135
+ traversed_functions: z.number().int().min(0),
136
+ traversed_modules: z.number().int().min(0),
137
+ relevant_unknowns: z.number().int().min(0),
138
+ retained_unknowns: z.number().int().min(0).max(10_000),
139
+ }),
140
+ coverage: z.strictObject({
141
+ status: z.enum(["complete", "partial", "truncated", "unavailable"]),
142
+ frontier: z.array(queryFrontierSchema).max(100_000),
143
+ }),
144
+ page: z.strictObject({
145
+ offset: z.number().int().min(0),
146
+ size: z.number().int().min(0).max(1_000),
147
+ next_cursor: z
148
+ .string()
149
+ .regex(/^jsrqc_[0-9]+_[a-f0-9]{64}$/u)
150
+ .nullable(),
151
+ }),
152
+ applied_limits: javaScriptSemanticQueryLimitsSchema,
153
+ accepted_limit_ranges: z.strictObject({
154
+ max_seed_matches: z.strictObject({
155
+ minimum: z.literal(1),
156
+ maximum: z.literal(1_000),
157
+ }),
158
+ max_nodes: z.strictObject({
159
+ minimum: z.literal(1),
160
+ maximum: z.literal(50_000),
161
+ }),
162
+ max_edges: z.strictObject({
163
+ minimum: z.literal(1),
164
+ maximum: z.literal(100_000),
165
+ }),
166
+ max_depth: z.strictObject({
167
+ minimum: z.literal(0),
168
+ maximum: z.literal(64),
169
+ }),
170
+ max_functions: z.strictObject({
171
+ minimum: z.literal(1),
172
+ maximum: z.literal(10_000),
173
+ }),
174
+ max_modules: z.strictObject({
175
+ minimum: z.literal(1),
176
+ maximum: z.literal(10_000),
177
+ }),
178
+ max_unknowns: z.strictObject({
179
+ minimum: z.literal(0),
180
+ maximum: z.literal(10_000),
181
+ }),
182
+ page_size: z.strictObject({
183
+ minimum: z.literal(1),
184
+ maximum: z.literal(1_000),
185
+ }),
186
+ }),
187
+ limitations: z.array(z.string().min(1).max(4_096)).max(1_000),
188
+ });