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,269 @@
1
+ import * as t from "@babel/types";
2
+ import { reachSemanticLimit, semanticCallableIdForNode, semanticReferenceRole, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
3
+ import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
4
+ import { parameterBindings, resolveLocalCallables, } from "./javascriptSemanticCallResolution.js";
5
+ import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
6
+ import { range } from "./javascriptStaticAnalysisHelpers.js";
7
+ /** Recover bounded direct-call and lexical-capture candidates from inert syntax. */
8
+ export const collectJavaScriptSemanticCalls = (program, state, callables) => {
9
+ const output = {
10
+ callSites: [],
11
+ argumentFlows: [],
12
+ callReturnFlows: [],
13
+ callResultFlows: [],
14
+ closureCaptures: [],
15
+ frontiers: [],
16
+ retainedArguments: 0,
17
+ };
18
+ const callableById = new Map(callables.map((callable) => [callable.callableId, callable]));
19
+ const context = {
20
+ state,
21
+ callableById,
22
+ output,
23
+ bindingResolutionCache: new Map(),
24
+ parameterBindingCache: new Map(),
25
+ };
26
+ const callableStack = [];
27
+ traverseJavaScriptAst(program, {
28
+ enter: (node, parent) => {
29
+ const callableId = semanticCallableIdForNode(node);
30
+ const callable = callableId === null ? undefined : callableById.get(callableId);
31
+ if (callable !== undefined)
32
+ callableStack.push(callable);
33
+ const owner = enclosingFunction(callableStack);
34
+ collectCapture(node, parent, owner, context);
35
+ collectDynamicProperty(node, owner, state, output);
36
+ if (t.isCallExpression(node) ||
37
+ t.isOptionalCallExpression(node) ||
38
+ t.isNewExpression(node))
39
+ collectCallSite(node, parent, owner, context);
40
+ },
41
+ exit: (node) => {
42
+ const callableId = semanticCallableIdForNode(node);
43
+ if (callableId !== null &&
44
+ callableStack.at(-1)?.callableId === callableId)
45
+ callableStack.pop();
46
+ },
47
+ });
48
+ return {
49
+ callSites: output.callSites,
50
+ argumentFlows: output.argumentFlows,
51
+ callReturnFlows: output.callReturnFlows,
52
+ callResultFlows: output.callResultFlows,
53
+ closureCaptures: output.closureCaptures,
54
+ frontiers: output.frontiers,
55
+ };
56
+ };
57
+ const collectCallSite = (node, parent, owner, context) => {
58
+ const { state, callableById, output } = context;
59
+ if (output.callSites.length >= state.limits.maxCallSites) {
60
+ reachSemanticLimit(state, "maxCallSites");
61
+ return;
62
+ }
63
+ const resolution = resolveLocalCallables({
64
+ node: node.callee,
65
+ state,
66
+ callableById,
67
+ seenBindings: new Set(),
68
+ depth: 0,
69
+ bindingCache: context.bindingResolutionCache,
70
+ });
71
+ const callSiteId = semanticCallSiteId(node);
72
+ const argumentsValue = retainedArguments(node.arguments, state, output);
73
+ const site = {
74
+ callSiteId,
75
+ kind: t.isNewExpression(node) ? "construct" : "call",
76
+ callerCallableId: owner?.callableId ?? null,
77
+ location: range(node),
78
+ calleeLocation: range(node.callee),
79
+ resolution: resolution.callableIds.length === 1 && resolution.complete
80
+ ? "exact"
81
+ : resolution.callableIds.length > 0
82
+ ? "ambiguous"
83
+ : "unresolved",
84
+ calleeCallableIds: resolution.callableIds,
85
+ arguments: argumentsValue,
86
+ };
87
+ output.callSites.push(site);
88
+ collectCallResultFlow(site, node, parent, context);
89
+ if (site.resolution !== "exact")
90
+ addFrontier({
91
+ kind: "dynamic-call",
92
+ callableId: owner?.callableId ?? null,
93
+ location: range(node.callee),
94
+ reason: resolution.reason,
95
+ }, state, output);
96
+ collectArgumentFlows(site, node, context);
97
+ if (site.kind === "call")
98
+ collectReturnFlows(site, callableById, state, output);
99
+ };
100
+ const collectCallResultFlow = (site, node, parent, context) => {
101
+ const identifier = t.isVariableDeclarator(parent) &&
102
+ parent.init === node &&
103
+ t.isIdentifier(parent.id)
104
+ ? parent.id
105
+ : t.isAssignmentExpression(parent) &&
106
+ parent.right === node &&
107
+ t.isIdentifier(parent.left)
108
+ ? parent.left
109
+ : null;
110
+ if (identifier === null)
111
+ return;
112
+ const binding = resolveSemanticBindingState(context.state, identifier, identifier.name);
113
+ const identifierRange = range(identifier);
114
+ const definition = binding?.definitions.find(({ location }) => location.start.line === identifierRange.start.line &&
115
+ location.start.column === identifierRange.start.column &&
116
+ location.end.line === identifierRange.end.line &&
117
+ location.end.column === identifierRange.end.column);
118
+ if (binding === undefined || definition === undefined)
119
+ return;
120
+ context.output.callResultFlows.push({
121
+ callSiteId: site.callSiteId,
122
+ bindingId: binding.bindingId,
123
+ definitionLocation: definition.location,
124
+ });
125
+ };
126
+ const retainedArguments = (nodes, state, output) => {
127
+ const retained = [];
128
+ for (const [index, node] of nodes.entries()) {
129
+ if (output.retainedArguments >= state.limits.maxCallArguments) {
130
+ reachSemanticLimit(state, "maxCallArguments");
131
+ break;
132
+ }
133
+ output.retainedArguments += 1;
134
+ retained.push({
135
+ index,
136
+ location: range(node),
137
+ spread: t.isSpreadElement(node),
138
+ });
139
+ }
140
+ return retained;
141
+ };
142
+ const collectArgumentFlows = (site, node, context) => {
143
+ const { state, callableById, output } = context;
144
+ const retainedIndexes = new Set(site.arguments.map(({ index }) => index));
145
+ let positionIsExact = true;
146
+ for (const [index, argument] of node.arguments.entries()) {
147
+ if (t.isSpreadElement(argument)) {
148
+ positionIsExact = false;
149
+ continue;
150
+ }
151
+ if (!positionIsExact ||
152
+ !retainedIndexes.has(index) ||
153
+ !t.isExpression(argument))
154
+ continue;
155
+ for (const callableId of site.calleeCallableIds) {
156
+ const callable = callableById.get(callableId);
157
+ if (callable === undefined)
158
+ continue;
159
+ for (const parameter of cachedParameterBindings(callable, index, context)) {
160
+ if (output.argumentFlows.length >= state.limits.maxArgumentFlows) {
161
+ reachSemanticLimit(state, "maxArgumentFlows");
162
+ return;
163
+ }
164
+ const definition = parameter.definitions.find(({ kind }) => kind === "parameter");
165
+ if (definition === undefined)
166
+ continue;
167
+ output.argumentFlows.push({
168
+ callSiteId: site.callSiteId,
169
+ argumentIndex: index,
170
+ argumentLocation: range(argument),
171
+ callableId,
172
+ parameterBindingId: parameter.bindingId,
173
+ parameterLocation: definition.location,
174
+ });
175
+ }
176
+ }
177
+ }
178
+ };
179
+ const cachedParameterBindings = (callable, index, context) => {
180
+ const key = `${callable.callableId}:${String(index)}`;
181
+ const cached = context.parameterBindingCache.get(key);
182
+ if (cached !== undefined)
183
+ return cached;
184
+ const bindings = parameterBindings(callable, index, context.state);
185
+ context.parameterBindingCache.set(key, bindings);
186
+ return bindings;
187
+ };
188
+ const collectReturnFlows = (site, callableById, state, output) => {
189
+ for (const callableId of site.calleeCallableIds) {
190
+ const callable = callableById.get(callableId);
191
+ if (callable === undefined)
192
+ continue;
193
+ for (const returnSite of callable.returnSites) {
194
+ if (output.callReturnFlows.length >= state.limits.maxCallReturnFlows) {
195
+ reachSemanticLimit(state, "maxCallReturnFlows");
196
+ return;
197
+ }
198
+ output.callReturnFlows.push({
199
+ callSiteId: site.callSiteId,
200
+ callableId,
201
+ returnSiteId: returnSite.returnSiteId,
202
+ returnLocation: returnSite.location,
203
+ });
204
+ }
205
+ }
206
+ };
207
+ const collectCapture = (node, parent, owner, context) => {
208
+ const { state, output } = context;
209
+ if (!t.isIdentifier(node) ||
210
+ parent === null ||
211
+ owner === undefined ||
212
+ owner.bodyScopeId === null)
213
+ return;
214
+ if (semanticReferenceRole(node, parent) === null)
215
+ return;
216
+ const binding = resolveSemanticBindingState(state, node, node.name);
217
+ if (binding === undefined ||
218
+ bindingIsWithinCallable(binding, owner.bodyScopeId, state))
219
+ return;
220
+ if (output.closureCaptures.length >= state.limits.maxClosureCaptures) {
221
+ reachSemanticLimit(state, "maxClosureCaptures");
222
+ return;
223
+ }
224
+ output.closureCaptures.push({
225
+ callableId: owner.callableId,
226
+ bindingId: binding.bindingId,
227
+ referenceLocation: range(node),
228
+ });
229
+ };
230
+ const collectDynamicProperty = (node, owner, state, output) => {
231
+ if ((t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) &&
232
+ semanticStaticPropertyName(node.property, node.computed) === "")
233
+ addFrontier({
234
+ kind: "dynamic-property",
235
+ callableId: owner?.callableId ?? null,
236
+ location: range(node.property),
237
+ reason: "Computed member property is not a static string or number.",
238
+ }, state, output);
239
+ else if ((t.isObjectProperty(node) || t.isObjectMethod(node)) &&
240
+ node.computed &&
241
+ semanticStaticPropertyName(node.key, true) === "")
242
+ addFrontier({
243
+ kind: "dynamic-property",
244
+ callableId: owner?.callableId ?? null,
245
+ location: range(node.key),
246
+ reason: "Computed object property is not a static string or number.",
247
+ }, state, output);
248
+ };
249
+ const addFrontier = (frontier, state, output) => {
250
+ if (output.frontiers.length >= state.limits.maxFrontiers) {
251
+ reachSemanticLimit(state, "maxFrontiers");
252
+ return;
253
+ }
254
+ output.frontiers.push(frontier);
255
+ };
256
+ const bindingIsWithinCallable = (binding, bodyScopeId, state) => {
257
+ let scope = state.scopesById.get(binding.scopeId);
258
+ while (scope !== undefined) {
259
+ if (scope.scopeId === bodyScopeId)
260
+ return true;
261
+ scope =
262
+ scope.parentScopeId === null
263
+ ? undefined
264
+ : state.scopesById.get(scope.parentScopeId);
265
+ }
266
+ return false;
267
+ };
268
+ const enclosingFunction = (stack) => stack.findLast(({ kind }) => kind !== "class");
269
+ const semanticCallSiteId = (node) => `call:${t.isNewExpression(node) ? "construct" : "call"}:${String(node.start ?? -1)}:${String(node.end ?? -1)}`;
@@ -0,0 +1,356 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { compareCodePoints } from "./javascriptApplicationGraph.js";
5
+ import { JAVASCRIPT_SEMANTIC_RELATION_FAMILIES, JAVASCRIPT_SEMANTIC_RELATION_FAMILY, javaScriptSemanticFingerprintInputSchema, javaScriptSemanticFingerprintSchema, javaScriptSemanticGraphInputSchema, javaScriptSemanticGraphRecordSchema, javaScriptSemanticNodeInputSchema, javaScriptSemanticNodeSchema, javaScriptSemanticRelationInputSchema, javaScriptSemanticRelationSchema, javaScriptSemanticUnknownInputSchema, javaScriptSemanticUnknownSchema, } from "./javascriptSemanticGraphSchemas.js";
6
+ const canonicalJson = (value) => {
7
+ const encoded = canonicalize(value);
8
+ if (encoded === undefined)
9
+ throw new TypeError("JavaScript semantic graph could not canonicalize data");
10
+ return encoded;
11
+ };
12
+ const digestCanonical = (value) => createHash("sha256").update(canonicalJson(value)).digest("hex");
13
+ const uniqueSorted = (values) => [...new Set(values)].sort(compareCodePoints);
14
+ const normalizeEvidence = (evidence) => ({
15
+ ...evidence,
16
+ coverage: {
17
+ ...evidence.coverage,
18
+ limits: [...evidence.coverage.limits].sort((left, right) => compareCodePoints(canonicalJson(left), canonicalJson(right))),
19
+ },
20
+ limitations: uniqueSorted(evidence.limitations),
21
+ evidence_ids: uniqueSorted(evidence.evidence_ids),
22
+ });
23
+ const nodeId = (node) => `jsrg_node_${digestCanonical({ kind: node.kind, identity: node.identity })}`;
24
+ /** Normalize one semantic entity and derive its artifact-version identifier. */
25
+ export const createJavaScriptSemanticGraphNode = (input) => {
26
+ const parsed = javaScriptSemanticNodeInputSchema.parse(input);
27
+ const semantic = {
28
+ ...parsed,
29
+ application_node_ids: uniqueSorted(parsed.application_node_ids),
30
+ evidence: normalizeEvidence(parsed.evidence),
31
+ identifier_strategy: {
32
+ strategy: "semantic-content-sha256",
33
+ stability: "artifact-version",
34
+ },
35
+ };
36
+ return javaScriptSemanticNodeSchema.parse({
37
+ ...semantic,
38
+ node_id: nodeId(semantic),
39
+ });
40
+ };
41
+ /** Normalize one semantic relationship and derive its exact identifier. */
42
+ export const createJavaScriptSemanticGraphRelation = (input) => {
43
+ const parsed = javaScriptSemanticRelationInputSchema.parse(input);
44
+ const semantic = {
45
+ ...parsed,
46
+ evidence: normalizeEvidence(parsed.evidence),
47
+ identifier_strategy: {
48
+ strategy: "semantic-content-sha256",
49
+ stability: "relationship-exact",
50
+ },
51
+ };
52
+ return javaScriptSemanticRelationSchema.parse({
53
+ ...semantic,
54
+ relation_id: `jsrg_relation_${digestCanonical(semantic)}`,
55
+ });
56
+ };
57
+ /** Normalize one unresolved semantic frontier and derive its identifier. */
58
+ export const createJavaScriptSemanticGraphUnknown = (input) => {
59
+ const parsed = javaScriptSemanticUnknownInputSchema.parse(input);
60
+ const semantic = {
61
+ ...parsed,
62
+ relation_kinds: uniqueSorted(parsed.relation_kinds),
63
+ candidate_node_ids: uniqueSorted(parsed.candidate_node_ids),
64
+ evidence: normalizeEvidence(parsed.evidence),
65
+ };
66
+ return javaScriptSemanticUnknownSchema.parse({
67
+ ...semantic,
68
+ unknown_id: `jsrg_unknown_${digestCanonical(semantic)}`,
69
+ });
70
+ };
71
+ /** Normalize one function fingerprint and derive its component commitment. */
72
+ export const createJavaScriptSemanticFingerprint = (input) => {
73
+ const parsed = javaScriptSemanticFingerprintInputSchema.parse(input);
74
+ const semantic = {
75
+ ...parsed,
76
+ components: {
77
+ ...parsed.components,
78
+ effects: uniqueSorted(parsed.components.effects),
79
+ },
80
+ limitations: uniqueSorted(parsed.limitations),
81
+ evidence: normalizeEvidence(parsed.evidence),
82
+ };
83
+ const fingerprintSha256 = digestCanonical(semantic.components);
84
+ return javaScriptSemanticFingerprintSchema.parse({
85
+ ...semantic,
86
+ fingerprint_sha256: fingerprintSha256,
87
+ fingerprint_id: `jsrg_fingerprint_${digestCanonical({
88
+ function_node_id: semantic.function_node_id,
89
+ algorithm: semantic.algorithm,
90
+ fingerprint_sha256: fingerprintSha256,
91
+ })}`,
92
+ });
93
+ };
94
+ const sortedUniqueIssue = (values, path, label, context) => {
95
+ for (let index = 1; index < values.length; index += 1) {
96
+ if (compareCodePoints(values[index - 1] ?? "", values[index] ?? "") < 0)
97
+ continue;
98
+ context.addIssue({
99
+ code: "custom",
100
+ path: [...path, index],
101
+ message: `${label} must be sorted and unique`,
102
+ });
103
+ return;
104
+ }
105
+ };
106
+ const checkCoverage = (graph, context) => {
107
+ const families = graph.coverage.families.map(({ family }) => family);
108
+ if (canonicalJson(families) !==
109
+ canonicalJson(JAVASCRIPT_SEMANTIC_RELATION_FAMILIES))
110
+ context.addIssue({
111
+ code: "custom",
112
+ path: ["coverage", "families"],
113
+ message: "Coverage must name every semantic relation family once in canonical order",
114
+ });
115
+ if (graph.coverage.status === "complete" &&
116
+ (graph.coverage.truncated ||
117
+ graph.coverage.omitted_nodes !== 0 ||
118
+ graph.coverage.omitted_relations !== 0 ||
119
+ graph.coverage.families.some(({ status }) => status !== "complete")))
120
+ context.addIssue({
121
+ code: "custom",
122
+ path: ["coverage"],
123
+ message: "Complete graph coverage cannot omit or truncate semantic facts",
124
+ });
125
+ if (graph.coverage.truncated && graph.coverage.limits.length === 0)
126
+ context.addIssue({
127
+ code: "custom",
128
+ path: ["coverage", "limits"],
129
+ message: "Truncated graph coverage must identify an effective limit",
130
+ });
131
+ const relationsByFamily = new Map(JAVASCRIPT_SEMANTIC_RELATION_FAMILIES.map((family) => [family, 0]));
132
+ for (const relation of graph.relations) {
133
+ const family = JAVASCRIPT_SEMANTIC_RELATION_FAMILY[relation.relation];
134
+ relationsByFamily.set(family, (relationsByFamily.get(family) ?? 0) + 1);
135
+ }
136
+ const unknownsById = new Map(graph.unknowns.map((unknown) => [unknown.unknown_id, unknown]));
137
+ for (const [index, family] of graph.coverage.families.entries()) {
138
+ if (family.retained_relations !== relationsByFamily.get(family.family))
139
+ context.addIssue({
140
+ code: "custom",
141
+ path: ["coverage", "families", index, "retained_relations"],
142
+ message: "Family retained relation count does not match graph content",
143
+ });
144
+ if (family.unknown_ids.some((identifier) => unknownsById.get(identifier)?.family !== family.family))
145
+ context.addIssue({
146
+ code: "custom",
147
+ path: ["coverage", "families", index, "unknown_ids"],
148
+ message: "Family coverage references an unknown from another family",
149
+ });
150
+ if (family.status === "complete" &&
151
+ (family.omitted_relations !== 0 || family.unknown_ids.length > 0))
152
+ context.addIssue({
153
+ code: "custom",
154
+ path: ["coverage", "families", index],
155
+ message: "Complete family coverage cannot omit or retain unknown facts",
156
+ });
157
+ }
158
+ };
159
+ const checkCanonicalOrder = (graph, context) => {
160
+ sortedUniqueIssue(graph.root_node_ids, ["root_node_ids"], "Root nodes", context);
161
+ sortedUniqueIssue(graph.nodes.map(({ node_id }) => node_id), ["nodes"], "Nodes", context);
162
+ sortedUniqueIssue(graph.relations.map(({ relation_id }) => relation_id), ["relations"], "Relations", context);
163
+ sortedUniqueIssue(graph.fingerprints.map(({ fingerprint_id }) => fingerprint_id), ["fingerprints"], "Fingerprints", context);
164
+ sortedUniqueIssue(graph.unknowns.map(({ unknown_id }) => unknown_id), ["unknowns"], "Unknown frontiers", context);
165
+ sortedUniqueIssue(graph.limitations, ["limitations"], "Limitations", context);
166
+ };
167
+ const checkNodes = (graph, nodes, context) => {
168
+ for (const [index, node] of graph.nodes.entries()) {
169
+ if (node.node_id !== nodeId(node))
170
+ context.addIssue({
171
+ code: "custom",
172
+ path: ["nodes", index, "node_id"],
173
+ message: "Node identifier is stale",
174
+ });
175
+ if (node.function_node_id !== null &&
176
+ nodes.get(node.function_node_id)?.kind !== "function")
177
+ context.addIssue({
178
+ code: "custom",
179
+ path: ["nodes", index, "function_node_id"],
180
+ message: "Function owner must name a function node",
181
+ });
182
+ }
183
+ };
184
+ const checkRoots = (graph, nodes, context) => {
185
+ for (const root of graph.root_node_ids)
186
+ if (!nodes.has(root))
187
+ context.addIssue({
188
+ code: "custom",
189
+ path: ["root_node_ids"],
190
+ message: "Root identifier must name a semantic node",
191
+ });
192
+ };
193
+ const checkRelations = (graph, nodes, context) => {
194
+ for (const [index, relation] of graph.relations.entries()) {
195
+ const { relation_id: identifier, ...semantic } = relation;
196
+ if (identifier !== "jsrg_relation_" + digestCanonical(semantic))
197
+ context.addIssue({
198
+ code: "custom",
199
+ path: ["relations", index, "relation_id"],
200
+ message: "Relation identifier is stale",
201
+ });
202
+ if (!nodes.has(relation.source_node_id) ||
203
+ !nodes.has(relation.target_node_id))
204
+ context.addIssue({
205
+ code: "custom",
206
+ path: ["relations", index],
207
+ message: "Relation endpoints must name semantic nodes",
208
+ });
209
+ if (relation.source_node_id === relation.target_node_id)
210
+ context.addIssue({
211
+ code: "custom",
212
+ path: ["relations", index],
213
+ message: "Semantic relations cannot be self-referential",
214
+ });
215
+ if (relation.resolution === "candidate" &&
216
+ relation.evidence.state === "observed")
217
+ context.addIssue({
218
+ code: "custom",
219
+ path: ["relations", index, "evidence", "state"],
220
+ message: "Candidate relations cannot claim observed resolution",
221
+ });
222
+ }
223
+ };
224
+ const checkUnknowns = (graph, nodes, context) => {
225
+ for (const [index, unknown] of graph.unknowns.entries()) {
226
+ const { unknown_id: identifier, ...semantic } = unknown;
227
+ if (identifier !== "jsrg_unknown_" + digestCanonical(semantic))
228
+ context.addIssue({
229
+ code: "custom",
230
+ path: ["unknowns", index, "unknown_id"],
231
+ message: "Unknown frontier identifier is stale",
232
+ });
233
+ if (unknown.node_id !== null && !nodes.has(unknown.node_id))
234
+ context.addIssue({
235
+ code: "custom",
236
+ path: ["unknowns", index, "node_id"],
237
+ message: "Unknown frontier node is absent",
238
+ });
239
+ if (unknown.candidate_node_ids.some((nodeId) => !nodes.has(nodeId)))
240
+ context.addIssue({
241
+ code: "custom",
242
+ path: ["unknowns", index, "candidate_node_ids"],
243
+ message: "Unknown frontier candidate node is absent",
244
+ });
245
+ if (unknown.relation_kinds.some((kind) => JAVASCRIPT_SEMANTIC_RELATION_FAMILY[kind] !== unknown.family))
246
+ context.addIssue({
247
+ code: "custom",
248
+ path: ["unknowns", index, "relation_kinds"],
249
+ message: "Unknown relation kinds must belong to their declared family",
250
+ });
251
+ if (!["unknown", "unavailable"].includes(unknown.evidence.state))
252
+ context.addIssue({
253
+ code: "custom",
254
+ path: ["unknowns", index, "evidence", "state"],
255
+ message: "Unknown frontiers require unknown or unavailable evidence",
256
+ });
257
+ }
258
+ };
259
+ const checkUnknownReferences = (graph, context) => {
260
+ const unknownIds = new Set(graph.unknowns.map(({ unknown_id }) => unknown_id));
261
+ for (const [index, family] of graph.coverage.families.entries())
262
+ if (family.unknown_ids.some((identifier) => !unknownIds.has(identifier)))
263
+ context.addIssue({
264
+ code: "custom",
265
+ path: ["coverage", "families", index, "unknown_ids"],
266
+ message: "Family coverage references an absent unknown frontier",
267
+ });
268
+ };
269
+ const checkFingerprints = (graph, nodes, context) => {
270
+ for (const [index, fingerprint] of graph.fingerprints.entries()) {
271
+ if (nodes.get(fingerprint.function_node_id)?.kind !== "function")
272
+ context.addIssue({
273
+ code: "custom",
274
+ path: ["fingerprints", index, "function_node_id"],
275
+ message: "Fingerprint must name a function node",
276
+ });
277
+ if (fingerprint.fingerprint_sha256 !== digestCanonical(fingerprint.components))
278
+ context.addIssue({
279
+ code: "custom",
280
+ path: ["fingerprints", index, "fingerprint_sha256"],
281
+ message: "Fingerprint component commitment is stale",
282
+ });
283
+ const expectedIdentifier = "jsrg_fingerprint_" +
284
+ digestCanonical({
285
+ function_node_id: fingerprint.function_node_id,
286
+ algorithm: fingerprint.algorithm,
287
+ fingerprint_sha256: fingerprint.fingerprint_sha256,
288
+ });
289
+ if (fingerprint.fingerprint_id !== expectedIdentifier)
290
+ context.addIssue({
291
+ code: "custom",
292
+ path: ["fingerprints", index, "fingerprint_id"],
293
+ message: "Fingerprint identifier is stale",
294
+ });
295
+ if (fingerprint.status !== "complete" &&
296
+ fingerprint.limitations.length === 0)
297
+ context.addIssue({
298
+ code: "custom",
299
+ path: ["fingerprints", index, "limitations"],
300
+ message: "Incomplete fingerprints require a limitation",
301
+ });
302
+ }
303
+ };
304
+ const checkGraph = (graph, context) => {
305
+ checkCanonicalOrder(graph, context);
306
+ const nodes = new Map(graph.nodes.map((node) => [node.node_id, node]));
307
+ checkNodes(graph, nodes, context);
308
+ checkRoots(graph, nodes, context);
309
+ checkRelations(graph, nodes, context);
310
+ checkUnknowns(graph, nodes, context);
311
+ checkUnknownReferences(graph, context);
312
+ checkFingerprints(graph, nodes, context);
313
+ checkCoverage(graph, context);
314
+ if (graph.coverage.status !== "complete" && graph.limitations.length === 0)
315
+ context.addIssue({
316
+ code: "custom",
317
+ path: ["limitations"],
318
+ message: "Non-complete graph coverage requires a limitation",
319
+ });
320
+ const { graph_id: identifier, ...semantic } = graph;
321
+ if (identifier !== "jsrg_" + digestCanonical(semantic))
322
+ context.addIssue({
323
+ code: "custom",
324
+ path: ["graph_id"],
325
+ message: "Graph identifier is stale",
326
+ });
327
+ };
328
+ /** Strict semantic graph schema with verified canonical commitments. */
329
+ export const javaScriptSemanticGraphSchema = javaScriptSemanticGraphRecordSchema.superRefine(checkGraph);
330
+ /** Normalize a complete companion graph and derive its graph ID. */
331
+ export const createJavaScriptSemanticGraph = (input) => {
332
+ const parsed = javaScriptSemanticGraphInputSchema.parse(input);
333
+ const semantic = {
334
+ ...parsed,
335
+ root_node_ids: uniqueSorted(parsed.root_node_ids),
336
+ nodes: [...parsed.nodes].sort((left, right) => compareCodePoints(left.node_id, right.node_id)),
337
+ relations: [...parsed.relations].sort((left, right) => compareCodePoints(left.relation_id, right.relation_id)),
338
+ fingerprints: [...parsed.fingerprints].sort((left, right) => compareCodePoints(left.fingerprint_id, right.fingerprint_id)),
339
+ unknowns: [...parsed.unknowns].sort((left, right) => compareCodePoints(left.unknown_id, right.unknown_id)),
340
+ coverage: {
341
+ ...parsed.coverage,
342
+ limits: [...parsed.coverage.limits].sort((left, right) => compareCodePoints(canonicalJson(left), canonicalJson(right))),
343
+ families: [...parsed.coverage.families]
344
+ .map((family) => ({
345
+ ...family,
346
+ unknown_ids: uniqueSorted(family.unknown_ids),
347
+ }))
348
+ .sort((left, right) => compareCodePoints(left.family, right.family)),
349
+ },
350
+ limitations: uniqueSorted(parsed.limitations),
351
+ };
352
+ return javaScriptSemanticGraphSchema.parse({
353
+ ...semantic,
354
+ graph_id: `jsrg_${digestCanonical(semantic)}`,
355
+ });
356
+ };