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
@@ -12,25 +12,42 @@ import { PRODUCT_IDENTITY, SDK_IDENTITY } from "../identity.js";
12
12
  import { readClientRegistrationStatuses, } from "./ClientRegistrationStatus.js";
13
13
  import { inspectRuntimeExecutables, } from "./RuntimeExecutableDiagnostics.js";
14
14
  import { collectDoctorDiagnostics, doctorHealthy, } from "./DoctorDiagnostics.js";
15
+ import { normalizeDoctorScope, scopeDoctorChecks, } from "./DoctorScope.js";
15
16
  const execFileAsync = promisify(execFile);
17
+ /** Deep provider IDs whose readiness can be selected explicitly by doctor. */
18
+ export const DOCTOR_PROVIDER_IDS = ["hopper", "ghidra"];
16
19
  /**
17
20
  * Check requirements and an optional target without mutating the host.
18
21
  * Every failed check includes remediation suitable for either the one-shot CLI
19
22
  * or an agent consuming the structured result.
20
23
  */
21
- export const runDoctor = async (target, host = systemDoctorHost()) => {
24
+ export const runDoctor = async (target, host = systemDoctorHost(), requestedScope) => {
22
25
  const { checks: diagnosticChecks, hopperPath, providerInspections, runtimeExecutables, } = await collectDoctorDiagnostics(target, host);
23
26
  const identity = await collectDoctorIdentity(host, runtimeExecutables);
24
27
  const checks = [...diagnosticChecks, ...identity.checks];
28
+ const providers = {
29
+ hopperAvailable: hopperPath !== undefined &&
30
+ checks
31
+ .filter(({ name }) => name.startsWith("hopper"))
32
+ .every(({ ok }) => ok),
33
+ hopperConfigured: host.configuredHopperPath !== undefined,
34
+ providerInspections,
35
+ };
36
+ const environmentHealthy = doctorHealthy(checks, providers);
37
+ const scope = normalizeDoctorScope(requestedScope, target);
38
+ const scoped = scopeDoctorChecks({
39
+ checks,
40
+ registrations: identity.value.registrations,
41
+ skillState: identity.value.skill.state,
42
+ providers,
43
+ scope,
44
+ });
25
45
  return {
26
- healthy: doctorHealthy(checks, {
27
- hopperAvailable: hopperPath !== undefined &&
28
- checks
29
- .filter(({ name }) => name.startsWith("hopper"))
30
- .every(({ ok }) => ok),
31
- hopperConfigured: host.configuredHopperPath !== undefined,
32
- providerInspections,
33
- }),
46
+ healthy: scope.mode === "audit-wide" ? environmentHealthy : scoped.healthy,
47
+ environment_healthy: environmentHealthy,
48
+ scope,
49
+ scope_checks: scope.mode === "audit-wide" ? checks : scoped.scopeChecks,
50
+ informational_checks: scope.mode === "audit-wide" ? [] : scoped.informationalChecks,
34
51
  ...(hopperPath === undefined ? {} : { hopperPath }),
35
52
  ...(providerInspections === undefined ? {} : { providerInspections }),
36
53
  checks,
@@ -127,7 +144,7 @@ export const systemDoctorHost = (options = {}) => ({
127
144
  return (await parseBinaryTarget(path)).ok;
128
145
  },
129
146
  executable: executableAvailable,
130
- linuxDemoRuntimeReady,
147
+ linuxDemoRuntimeCheck: options.linuxDemoRuntimeCheck ?? uncomposedLinuxDemoRuntimeCheck,
131
148
  supportedLinuxHopper: linuxHopperBinarySupported,
132
149
  async brewHopperPath() {
133
150
  return probeHomebrew(async (command) => {
@@ -202,21 +219,13 @@ const executableAvailable = async (path) => {
202
219
  return false;
203
220
  }
204
221
  };
205
- const linuxDemoRuntimeReady = async () => {
206
- try {
207
- await access("/usr/bin/Xvfb", constants.X_OK);
208
- await access("/usr/bin/xauth", constants.X_OK);
209
- await access("/usr/bin/python3", constants.X_OK);
210
- await execFileAsync("/usr/bin/python3", [
211
- "-c",
212
- "import ctypes; ctypes.CDLL('libX11.so.6'); ctypes.CDLL('libXtst.so.6')",
213
- ]);
214
- return true;
215
- }
216
- catch {
217
- return false;
218
- }
219
- };
222
+ const uncomposedLinuxDemoRuntimeCheck = () => Promise.resolve({
223
+ name: "hopper-demo-runtime",
224
+ ok: false,
225
+ classification: "config_drift",
226
+ detail: "Linux Hopper private-display diagnostics were not composed",
227
+ remediation: "Run rea doctor through the production CLI adapter.",
228
+ });
220
229
  const manualHopperPaths = async () => {
221
230
  const paths = [];
222
231
  for (const root of ["/Applications", join(homedir(), "Applications")]) {
@@ -134,10 +134,7 @@ const optionalHopperVersionCheck = async (host, hopperPath) => {
134
134
  };
135
135
  const optionalHopperDemoCheck = async (host, hopperPath) => host.platform !== "linux" || hopperPath === undefined
136
136
  ? undefined
137
- : check("hopper-demo-runtime", await host.linuxDemoRuntimeReady(), undefined, {
138
- remediation: "Rerun rea setup to install the Xvfb, xauth, Python, X11, and XTEST packages required for Linux demo sessions.",
139
- classification: "missing_dependency",
140
- });
137
+ : host.linuxDemoRuntimeCheck();
141
138
  const optionalIlspyCheck = async (host) => {
142
139
  if (host.configuredIlspyCmdPath === undefined)
143
140
  return undefined;
@@ -0,0 +1,43 @@
1
+ /** Project doctor diagnostics for concise CLI output or explicit full detail. */
2
+ export const projectDoctorReport = (report, detail) => detail === "full" ? report : doctorSummary(report);
3
+ const doctorSummary = (report) => ({
4
+ healthy: report.healthy,
5
+ environment_healthy: report.environment_healthy,
6
+ scope: report.scope,
7
+ ...(report.hopperPath === undefined
8
+ ? {}
9
+ : { hopper_path: report.hopperPath }),
10
+ providers: (report.providerInspections ?? []).map((provider) => ({
11
+ id: provider.id,
12
+ configured: provider.configured,
13
+ available: provider.available,
14
+ provider_version: provider.providerVersion,
15
+ failed_checks: provider.checks.filter(({ ok }) => !ok).map(checkSummary),
16
+ })),
17
+ failed_scope_checks: report.scope_checks
18
+ .filter(({ ok }) => !ok)
19
+ .map(checkSummary),
20
+ informational_drift_count: report.informational_checks.filter(({ ok }) => !ok)
21
+ .length,
22
+ identity: report.identity === undefined
23
+ ? null
24
+ : {
25
+ cli_package_version: report.identity.cli_package_version,
26
+ expected_skill_version: report.identity.expected_skill_version,
27
+ catalog: {
28
+ counts: report.identity.catalog.counts,
29
+ digests: report.identity.catalog.digests,
30
+ },
31
+ installations: report.identity.installations,
32
+ skill: report.identity.skill,
33
+ registrations: report.identity.registrations,
34
+ },
35
+ });
36
+ const checkSummary = (check) => ({
37
+ name: check.name,
38
+ classification: check.classification,
39
+ ...(check.detail === undefined ? {} : { detail: check.detail }),
40
+ ...(check.remediation === undefined || check.remediation === null
41
+ ? {}
42
+ : { remediation: check.remediation }),
43
+ });
@@ -0,0 +1,102 @@
1
+ import { doctorHealthy } from "./DoctorDiagnostics.js";
2
+ /** Normalize a caller-selected doctor readiness boundary. */
3
+ export const normalizeDoctorScope = (requested, target) => ({
4
+ mode: requested === undefined ? "audit-wide" : "explicit",
5
+ clients: uniqueSorted(requested?.clients ?? []),
6
+ providers: uniqueSorted(requested?.providers ?? []),
7
+ skill: requested?.skill === true,
8
+ target: target ?? null,
9
+ });
10
+ /** Partition diagnostics into required and informational scope checks. */
11
+ export const scopeDoctorChecks = (input) => {
12
+ const providerChecks = input.checks.filter((candidate) => isProviderCheck(candidate.name, input.providers.providerInspections));
13
+ const coreChecks = input.checks.filter(({ name }) => !isProviderCheck(name, input.providers.providerInspections) &&
14
+ name !== "skill:identity" &&
15
+ !name.startsWith("registration:"));
16
+ const clientChecks = input.scope.clients.map((client) => selectedRegistrationCheck(client, input.registrations.find((candidate) => candidate.client === client)));
17
+ const skillChecks = input.scope.skill
18
+ ? [selectedSkillCheck(input.skillState, input.checks)]
19
+ : [];
20
+ const selectedProviderChecks = input.scope.providers.length === 0
21
+ ? configuredProviderChecks(providerChecks, input.providers)
22
+ : input.scope.providers.flatMap((providerId) => selectedProviderChecksFor(providerId, providerChecks, input.providers));
23
+ const scopeChecks = [
24
+ ...coreChecks,
25
+ ...selectedProviderChecks,
26
+ ...clientChecks,
27
+ ...skillChecks,
28
+ ];
29
+ const providerHealthy = input.scope.providers.length === 0
30
+ ? doctorHealthy([...coreChecks, ...providerChecks, ...clientChecks, ...skillChecks], input.providers)
31
+ : selectedProviderChecks.every(({ ok }) => ok);
32
+ const selectedNames = new Set(scopeChecks.map(({ name }) => name));
33
+ return {
34
+ healthy: coreChecks.every(({ ok }) => ok) &&
35
+ clientChecks.every(({ ok }) => ok) &&
36
+ skillChecks.every(({ ok }) => ok) &&
37
+ providerHealthy,
38
+ scopeChecks,
39
+ informationalChecks: input.checks.filter(({ name }) => !selectedNames.has(name)),
40
+ };
41
+ };
42
+ const isProviderCheck = (name, inspections) => name.startsWith("hopper") ||
43
+ inspections?.some(({ id }) => doctorProviderCheckName(id, name)) === true;
44
+ const configuredProviderChecks = (checks, providers) => checks.filter(({ name }) => name.startsWith("hopper")
45
+ ? providers.hopperConfigured
46
+ : providers.providerInspections?.some(({ id, configured }) => configured && doctorProviderCheckName(id, name)) === true);
47
+ const selectedProviderChecksFor = (providerId, checks, providers) => {
48
+ const selected = checks.filter(({ name }) => providerId === "hopper"
49
+ ? name.startsWith("hopper")
50
+ : doctorProviderCheckName(providerId, name));
51
+ const available = providerId === "hopper"
52
+ ? providers.hopperAvailable
53
+ : providers.providerInspections?.find(({ id }) => id === providerId)
54
+ ?.available === true;
55
+ if (available && selected.every(({ ok }) => ok))
56
+ return selected;
57
+ if (selected.some(({ ok }) => !ok))
58
+ return selected;
59
+ return [
60
+ ...selected,
61
+ {
62
+ name: `provider:${providerId}`,
63
+ ok: false,
64
+ classification: "missing_analysis_engine",
65
+ detail: `Requested provider ${providerId} is unavailable.`,
66
+ remediation: `Configure the ${providerId} provider, then rerun rea doctor.`,
67
+ },
68
+ ];
69
+ };
70
+ const selectedRegistrationCheck = (client, status) => status === undefined
71
+ ? {
72
+ name: `registration:${client}`,
73
+ ok: false,
74
+ classification: "config_drift",
75
+ detail: `Requested client ${client} was not detected.`,
76
+ remediation: `Install or select ${client}, then run rea setup --client ${client}.`,
77
+ }
78
+ : status.state === "aligned"
79
+ ? {
80
+ name: `registration:${client}`,
81
+ ok: true,
82
+ classification: "healthy",
83
+ detail: status.config_path,
84
+ }
85
+ : registrationCheck(status);
86
+ const registrationCheck = (status) => ({
87
+ name: `registration:${status.client}`,
88
+ ok: false,
89
+ classification: "config_drift",
90
+ detail: status.config_path,
91
+ remediation: status.remediation ?? "Run rea setup, then restart the affected client.",
92
+ });
93
+ const selectedSkillCheck = (state, checks) => checks.find(({ name }) => name === "skill:identity") ?? {
94
+ name: "skill:identity",
95
+ ok: state === "aligned",
96
+ classification: state === "aligned" ? "healthy" : "config_drift",
97
+ ...(state === "aligned"
98
+ ? { detail: "Installed REA skill identity is aligned." }
99
+ : { remediation: "Run rea setup to update the installed REA skill." }),
100
+ };
101
+ const uniqueSorted = (values) => [...new Set(values)].sort((left, right) => left.localeCompare(right));
102
+ const doctorProviderCheckName = (providerId, name) => name === providerId || name.startsWith(`${providerId}-`);
@@ -0,0 +1,26 @@
1
+ import { EvidenceReferenceError } from "../domain/errors.js";
2
+ import { err, ok } from "../domain/result.js";
3
+ /** Resolve IDs without accepting the digest-shaped identifier as proof itself. */
4
+ export const resolveEvidenceReferences = (lookup, evidenceIds, expected) => {
5
+ const records = [];
6
+ for (const evidenceId of evidenceIds) {
7
+ const record = lookup?.(evidenceId);
8
+ if (record === undefined)
9
+ return err(new EvidenceReferenceError(evidenceId, "missing", expectationDescription(expected), null));
10
+ if (expected !== undefined) {
11
+ const operations = expected.filter(({ operation }) => operation === record.operation);
12
+ if (operations.length === 0)
13
+ return err(new EvidenceReferenceError(evidenceId, "wrong_operation", unique(expected.map(({ operation }) => operation)).join(" | "), record.operation));
14
+ if (!operations.some(({ predicate }) => predicate === record.predicate_type))
15
+ return err(new EvidenceReferenceError(evidenceId, "wrong_predicate", unique(operations.map(({ predicate }) => predicate)).join(" | "), record.predicate_type));
16
+ }
17
+ records.push(record);
18
+ }
19
+ return ok(records);
20
+ };
21
+ const expectationDescription = (expected) => expected === undefined
22
+ ? "Evidence in the current authority"
23
+ : expected
24
+ .map(({ operation, predicate }) => `${operation}:${predicate}`)
25
+ .join(" | ");
26
+ const unique = (values) => [...new Set(values)];
@@ -7,7 +7,7 @@ export const createJavaScriptApplicationEvidence = (input, result) => createEvid
7
7
  sha256: result.root_artifact_sha256,
8
8
  format: result.format,
9
9
  }, JAVASCRIPT_APPLICATION_PROVIDER, {
10
- predicateType: "rea.javascript-application-analysis/v1",
10
+ predicateType: `rea.javascript-application-analysis/v${String(result.schema_version)}`,
11
11
  operation: "analyze_javascript_application",
12
12
  parameters: parameters(input),
13
13
  result: jsonValueSchema.parse(result),
@@ -7,7 +7,10 @@ import { managedApplicationGraphResultSchema } from "../domain/managedApplicatio
7
7
  export const parseApplicationGraphEvidence = (input) => {
8
8
  const evidence = parseEvidence(input);
9
9
  if (evidence.operation === "analyze_javascript_application" &&
10
- evidence.predicate_type === "rea.javascript-application-analysis/v1" &&
10
+ [
11
+ "rea.javascript-application-analysis/v1",
12
+ "rea.javascript-application-analysis/v2",
13
+ ].includes(evidence.predicate_type) &&
11
14
  providerMatches(evidence, JAVASCRIPT_APPLICATION_PROVIDER))
12
15
  return staticApplicationSource(evidence);
13
16
  if (evidence.operation === "reconcile_javascript_runtime" &&
@@ -25,6 +28,9 @@ const staticApplicationSource = (evidence) => {
25
28
  evidence.confidence !== "derived")
26
29
  throw new TypeError("JavaScript application Evidence authority or confidence is invalid");
27
30
  const result = javascriptApplicationAnalysisResultSchema.parse(evidence.normalized_result);
31
+ if (evidence.predicate_type !==
32
+ `rea.javascript-application-analysis/v${String(result.schema_version)}`)
33
+ throw new TypeError("JavaScript application Evidence predicate does not match its result schema version");
28
34
  analyzeJavaScriptApplicationInputSchema.parse({
29
35
  input_path: result.input_path,
30
36
  ...evidence.parameters,
@@ -35,6 +41,7 @@ const staticApplicationSource = (evidence) => {
35
41
  graph: result.graph,
36
42
  kind: "static-application",
37
43
  rootArtifactSha256: result.root_artifact_sha256,
44
+ semanticGraph: result.schema_version === 2 ? result.semantic_graph : null,
38
45
  };
39
46
  };
40
47
  const staticRuntimeSource = (evidence) => {
@@ -52,6 +59,7 @@ const staticRuntimeSource = (evidence) => {
52
59
  graph: result.graph,
53
60
  kind: "static-runtime-reconciliation",
54
61
  rootArtifactSha256: applicationLayer.root_artifact_sha256,
62
+ semanticGraph: null,
55
63
  };
56
64
  };
57
65
  const managedApplicationSource = (evidence) => {
@@ -66,6 +74,7 @@ const managedApplicationSource = (evidence) => {
66
74
  graph: result.graph,
67
75
  kind: "managed-application",
68
76
  rootArtifactSha256: result.root_artifact_sha256,
77
+ semanticGraph: null,
69
78
  };
70
79
  };
71
80
  const assertApplicationSubject = (evidence, result) => {
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
3
3
  import { analyzeJavaScriptApplicationInputSchema, javascriptApplicationAnalysisResultSchema, } from "../domain/javascriptApplicationAnalysis.js";
4
4
  import { AnalysisCapabilityUnavailableError, AnalysisInputError, AnalysisProtocolError, ArtifactOperationError, PermissionRequiredError, } from "../domain/errors.js";
5
+ import { projectInputIssues } from "../domain/inputIssueProjection.js";
5
6
  import { err, ok } from "../domain/result.js";
6
7
  import { createJavaScriptApplicationEvidence } from "./JavaScriptApplicationEvidence.js";
7
8
  import { reconstructJavaScriptArtifact } from "./JavaScriptArtifactReconstruction.js";
@@ -10,7 +11,7 @@ const OPERATION = "analyze_javascript_application";
10
11
  export const analyzeJavaScriptApplication = async (authority, rawInput, options = {}) => {
11
12
  const parsed = analyzeJavaScriptApplicationInputSchema.safeParse(rawInput);
12
13
  if (!parsed.success)
13
- return err(new AnalysisInputError(OPERATION));
14
+ return err(new AnalysisInputError(OPERATION, undefined, projectInputIssues(parsed.error.issues, rawInput)));
14
15
  return analyzeJavaScriptApplicationValidated(authority, parsed.data, options);
15
16
  };
16
17
  /** Analyze input already parsed by a trusted adapter boundary. */
@@ -50,7 +51,7 @@ export const analyzeJavaScriptApplicationValidated = async (authority, input, op
50
51
  }, options.signal);
51
52
  const { electron_summary: summary, ...application } = reconstructed;
52
53
  const result = javascriptApplicationAnalysisResultSchema.parse({
53
- schema_version: 1,
54
+ schema_version: 2,
54
55
  ...application,
55
56
  summary,
56
57
  limitations: reconstructed.graph.limitations,
@@ -14,6 +14,19 @@ export const createApplicationFeatureTraceEvidence = (parameters, result) => cre
14
14
  limitations: result.limitations,
15
15
  evidenceLinks: result.evidence_links,
16
16
  });
17
+ /** Create derived Evidence for one bounded JavaScript semantic trace. */
18
+ export const createJavaScriptSemanticTraceEvidence = (parameters, result) => createEvidence(undefined, JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER, {
19
+ predicateType: "rea.javascript-semantic-trace/v1",
20
+ operation: "trace_javascript_semantics",
21
+ parameters,
22
+ result: jsonValueSchema.parse(result),
23
+ rawResult: null,
24
+ confidence: "inferred",
25
+ authority: "analyst-inference",
26
+ environment: null,
27
+ limitations: result.limitations,
28
+ evidenceLinks: result.evidence_links,
29
+ });
17
30
  /** Create derived Evidence for one tiered application version comparison. */
18
31
  export const createApplicationVersionComparisonEvidence = (parameters, result) => createEvidence(undefined, JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER, {
19
32
  predicateType: "rea.application-version-comparison/v1",
@@ -27,3 +40,16 @@ export const createApplicationVersionComparisonEvidence = (parameters, result) =
27
40
  limitations: result.limitations,
28
41
  evidenceLinks: result.evidence_links,
29
42
  });
43
+ /** Create derived Evidence for one bounded static export-shape comparison. */
44
+ export const createJavaScriptExportShapeComparisonEvidence = (parameters, result) => createEvidence(undefined, JAVASCRIPT_APPLICATION_WORKFLOW_PROVIDER, {
45
+ predicateType: "rea.javascript-export-shape-comparison/v1",
46
+ operation: "compare_javascript_export_shapes",
47
+ parameters,
48
+ result: jsonValueSchema.parse(result),
49
+ rawResult: null,
50
+ confidence: "inferred",
51
+ authority: "analyst-inference",
52
+ environment: null,
53
+ limitations: result.limitations,
54
+ evidenceLinks: result.evidence_links,
55
+ });
@@ -3,11 +3,13 @@ import { AnalysisInputError, AnalysisProtocolError, } from "../domain/errors.js"
3
3
  import { projectInputIssues } from "../domain/inputIssueProjection.js";
4
4
  import { compareJavaScriptApplicationVersions } from "../domain/javascriptApplicationVersionComparison.js";
5
5
  import { compareApplicationVersionsInputSchema } from "../domain/javascriptApplicationVersionComparisonSchemas.js";
6
+ import { compareJavaScriptExportShapes } from "../domain/javascriptExportShapeComparison.js";
7
+ import { compareJavaScriptExportShapesInputSchema } from "../domain/javascriptExportShapeComparisonSchemas.js";
6
8
  import { traceApplicationFeature } from "../domain/javascriptFeatureTrace.js";
7
9
  import { traceApplicationFeatureInputSchema } from "../domain/javascriptFeatureTraceSchemas.js";
8
10
  import { err, ok } from "../domain/result.js";
9
11
  import { parseApplicationGraphEvidence, parseNativeApplicationEvidence, } from "./JavaScriptApplicationEvidenceGraph.js";
10
- import { createApplicationFeatureTraceEvidence, createApplicationVersionComparisonEvidence, } from "./JavaScriptApplicationWorkflowEvidence.js";
12
+ import { createApplicationFeatureTraceEvidence, createApplicationVersionComparisonEvidence, createJavaScriptExportShapeComparisonEvidence, } from "./JavaScriptApplicationWorkflowEvidence.js";
11
13
  /** Authenticate Evidence and derive one bounded cross-layer feature trace. */
12
14
  export const traceApplicationFeatureEvidence = (rawInput) => {
13
15
  const operation = "trace_application_feature";
@@ -85,6 +87,52 @@ export const compareApplicationVersionsEvidenceValidated = (input) => {
85
87
  return workflowFailure(operation, cause);
86
88
  }
87
89
  };
90
+ /**
91
+ * Authenticate both graphs and compare one exact export return shape.
92
+ * @public
93
+ */
94
+ export const compareJavaScriptExportShapesEvidence = (rawInput) => {
95
+ const operation = "compare_javascript_export_shapes";
96
+ const parsed = compareJavaScriptExportShapesInputSchema.safeParse(rawInput);
97
+ if (!parsed.success)
98
+ return err(new AnalysisInputError(operation, undefined, projectInputIssues(parsed.error.issues, rawInput)));
99
+ return compareJavaScriptExportShapesEvidenceValidated(parsed.data);
100
+ };
101
+ /** Compare exact export shapes from input parsed by a trusted adapter. */
102
+ export const compareJavaScriptExportShapesEvidenceValidated = (input) => {
103
+ const operation = "compare_javascript_export_shapes";
104
+ try {
105
+ const left = parseApplicationGraphEvidence(input.left);
106
+ const right = parseApplicationGraphEvidence(input.right);
107
+ const result = compareJavaScriptExportShapes({
108
+ left: {
109
+ evidenceId: left.evidence.evidence_id,
110
+ graph: left.graph,
111
+ modulePath: input.left_module_path,
112
+ exportName: input.left_export_name,
113
+ },
114
+ right: {
115
+ evidenceId: right.evidence.evidence_id,
116
+ graph: right.graph,
117
+ modulePath: input.right_module_path,
118
+ exportName: input.right_export_name,
119
+ },
120
+ limits: input.limits,
121
+ });
122
+ return ok(createJavaScriptExportShapeComparisonEvidence({
123
+ left_evidence_id: left.evidence.evidence_id,
124
+ right_evidence_id: right.evidence.evidence_id,
125
+ left_module_path: input.left_module_path,
126
+ left_export_name: input.left_export_name,
127
+ right_module_path: input.right_module_path,
128
+ right_export_name: input.right_export_name,
129
+ limits: input.limits,
130
+ }, result));
131
+ }
132
+ catch (cause) {
133
+ return workflowFailure(operation, cause);
134
+ }
135
+ };
88
136
  const workflowFailure = (operation, cause) => err(cause instanceof z.ZodError || cause instanceof TypeError
89
137
  ? new AnalysisInputError(operation)
90
138
  : new AnalysisProtocolError("JavaScript application workflow produced an invalid result", { cause }));
@@ -9,6 +9,7 @@ import { buildJavaScriptArtifactGraph } from "./JavaScriptArtifactGraphBuilder.j
9
9
  import { artifactLimitsForReconstruction, javascriptArtifactReconstructionInputSchema, } from "./JavaScriptArtifactReconstructionInput.js";
10
10
  import { scanCanonicalArtifactInventory } from "./ArtifactInventory.js";
11
11
  import { summarizeElectronBoundaries } from "./ElectronBoundaryAnalysis.js";
12
+ import { buildJavaScriptSemanticGraph } from "./JavaScriptSemanticGraphBuilder.js";
12
13
  /** Reconstruct one local ASAR or extracted directory without executing code. */
13
14
  export const reconstructJavaScriptArtifact = async (rawInput, signal) => {
14
15
  const input = javascriptArtifactReconstructionInputSchema.parse(rawInput);
@@ -24,6 +25,11 @@ export const reconstructJavaScriptArtifact = async (rawInput, signal) => {
24
25
  const analysis = analyzeJavaScriptArtifactFiles(files, input, () => performance.now());
25
26
  abortIfNeeded(signal);
26
27
  const graph = buildJavaScriptArtifactGraph(snapshot, files, analysis, input);
28
+ const semanticGraph = buildJavaScriptSemanticGraph({
29
+ rootArtifactSha256: snapshot.manifest.root_sha256,
30
+ applicationGraph: graph,
31
+ analysis,
32
+ });
27
33
  return {
28
34
  input_path: path,
29
35
  format,
@@ -31,6 +37,7 @@ export const reconstructJavaScriptArtifact = async (rawInput, signal) => {
31
37
  inventory_manifest_id: snapshot.manifest.manifest_id,
32
38
  inventory_graph_sha256: snapshot.manifest.graph_sha256,
33
39
  graph,
40
+ semantic_graph: semanticGraph,
34
41
  electron_summary: summarizeElectronBoundaries(analysis),
35
42
  statistics: {
36
43
  relevant_files: files.files.length,
@@ -2,6 +2,7 @@ import { posix } from "node:path";
2
2
  import { addAstContainsEdge, artifactLocalIdentity, } from "./JavaScriptArtifactGraphContext.js";
3
3
  import { astObservationEvidence, completeReconstructionCoverage, partialReconstructionCoverage, staticInferenceEvidence, } from "./JavaScriptArtifactGraphEvidence.js";
4
4
  import { resolveArtifactPathByContext } from "./JavaScriptArtifactPathResolution.js";
5
+ import { projectJavaScriptExportReturnShapes } from "./JavaScriptReturnShapeProjection.js";
5
6
  /** Create one path-scoped source-module identity for every analyzed source file. */
6
7
  export const addJavaScriptSourceModules = (context) => {
7
8
  for (const analyzed of context.analysis.files) {
@@ -76,6 +77,13 @@ const addExportRelationship = (input) => {
76
77
  const { context, file, semantic, source, link } = input;
77
78
  if (link.exportedName === null)
78
79
  return;
80
+ const baseCoverage = semanticCoverage(semantic);
81
+ const returnShapes = projectJavaScriptExportReturnShapes({
82
+ ir: semantic.ir,
83
+ link,
84
+ modulePath: file.path,
85
+ baseCoverage,
86
+ });
79
87
  const exported = context.accumulator.addNode({
80
88
  kind: "javascript-module",
81
89
  identity: artifactLocalIdentity(file.sha256, "module-export", `${file.container_sha256}:${file.path}:${link.exportedName}`),
@@ -96,10 +104,26 @@ const addExportRelationship = (input) => {
96
104
  path: file.path,
97
105
  range: link.location,
98
106
  operation: "recover-module-export",
99
- coverage: semanticCoverage(semantic),
107
+ coverage: baseCoverage,
100
108
  limitations: semantic.ir.limitations,
101
109
  }),
102
110
  },
111
+ ...(returnShapes === null
112
+ ? []
113
+ : [
114
+ {
115
+ label: `${file.path}:${link.exportedName}:return-shapes`,
116
+ properties: returnShapes.properties,
117
+ evidence: staticInferenceEvidence({
118
+ sha256: file.sha256,
119
+ path: file.path,
120
+ range: returnShapes.range,
121
+ operation: "recover-export-return-shapes",
122
+ coverage: returnShapes.coverage,
123
+ limitations: returnShapes.limitations,
124
+ }),
125
+ },
126
+ ]),
103
127
  ],
104
128
  });
105
129
  context.accumulator.addEdge({
@@ -0,0 +1,111 @@
1
+ import { jsonObjectSchema, jsonValueSchema } from "../domain/jsonValue.js";
2
+ import { flattenSemanticReturnValue } from "../domain/javascriptSemanticReturns.js";
3
+ import { partialReconstructionCoverage } from "./JavaScriptArtifactGraphEvidence.js";
4
+ const MAX_PROJECTED_RETURN_SHAPES = 32;
5
+ const MAX_PROJECTED_RETURN_FIELDS = 64;
6
+ const MAX_PROJECTED_PROPERTY_COVERAGE = 64;
7
+ /** Project one exact export/callable link into shallow bounded graph values. */
8
+ export const projectJavaScriptExportReturnShapes = (input) => {
9
+ if (input.link.callableId === null || input.link.exportedName === null)
10
+ return null;
11
+ const callable = input.ir.callables.find(({ callableId }) => callableId === input.link.callableId);
12
+ if (callable === undefined)
13
+ return null;
14
+ const projection = projectReturnSites(callable);
15
+ const projectionOmitted = projection.omittedSites +
16
+ projection.omittedFields +
17
+ projection.omittedCoverage;
18
+ const semanticOmitted = callable.returnCoverage.omittedCount;
19
+ const complete = input.baseCoverage.status === "complete" &&
20
+ callable.returnCoverage.status === "complete" &&
21
+ projectionOmitted === 0;
22
+ const coverage = complete
23
+ ? input.baseCoverage
24
+ : partialReconstructionCoverage([
25
+ ...input.baseCoverage.limits,
26
+ {
27
+ name: "return-shape-sites",
28
+ value: MAX_PROJECTED_RETURN_SHAPES,
29
+ unit: "items",
30
+ },
31
+ {
32
+ name: "return-shape-fields",
33
+ value: MAX_PROJECTED_RETURN_FIELDS,
34
+ unit: "items",
35
+ },
36
+ {
37
+ name: "return-shape-property-coverage",
38
+ value: MAX_PROJECTED_PROPERTY_COVERAGE,
39
+ unit: "items",
40
+ },
41
+ ], semanticOmitted === null ? null : semanticOmitted + projectionOmitted, callable.returnCoverage.status === "truncated" || projectionOmitted > 0);
42
+ const limitations = [
43
+ ...input.ir.limitations,
44
+ "Return shapes are inferred from inert syntax and do not prove runtime behavior.",
45
+ ...(callable.returnSites.length === 0
46
+ ? [
47
+ "No direct return value was retained; runtime return behavior remains unknown.",
48
+ ]
49
+ : []),
50
+ ...(projectionOmitted > 0
51
+ ? ["Return-shape graph projection reached explicit retention limits."]
52
+ : []),
53
+ ];
54
+ return {
55
+ properties: jsonObjectSchema.parse({
56
+ semantic_role: "export-return-shapes",
57
+ module_path: input.modulePath,
58
+ exported_name: input.link.exportedName,
59
+ callable_id: callable.callableId,
60
+ callable_kind: callable.kind,
61
+ static_return_shapes: projection.shapes,
62
+ return_shape_coverage: {
63
+ status: callable.returnCoverage.status,
64
+ retained_return_sites: projection.shapes.length,
65
+ omitted_return_sites: semanticOmitted === null
66
+ ? null
67
+ : semanticOmitted + projection.omittedSites,
68
+ omitted_fields: projection.omittedFields,
69
+ omitted_property_coverage: projection.omittedCoverage,
70
+ projection_complete: projectionOmitted === 0,
71
+ },
72
+ }),
73
+ range: callable.location,
74
+ coverage,
75
+ limitations,
76
+ };
77
+ };
78
+ const projectReturnSites = (callable) => {
79
+ let retainedFields = 0;
80
+ let retainedCoverage = 0;
81
+ let omittedFields = 0;
82
+ let omittedCoverage = 0;
83
+ const retainedSites = callable.returnSites.slice(0, MAX_PROJECTED_RETURN_SHAPES);
84
+ const shapes = retainedSites.map((site) => {
85
+ const flattened = flattenSemanticReturnValue(site.value);
86
+ const remainingFields = Math.max(0, MAX_PROJECTED_RETURN_FIELDS - retainedFields);
87
+ const fields = flattened.fields.slice(0, remainingFields).map((field) => ({
88
+ ...field,
89
+ value: jsonValueSchema.parse(field.value),
90
+ }));
91
+ retainedFields += fields.length;
92
+ omittedFields += flattened.fields.length - fields.length;
93
+ const remainingCoverage = Math.max(0, MAX_PROJECTED_PROPERTY_COVERAGE - retainedCoverage);
94
+ const propertyCoverage = flattened.propertyCoverage.slice(0, remainingCoverage);
95
+ retainedCoverage += propertyCoverage.length;
96
+ omittedCoverage +=
97
+ flattened.propertyCoverage.length - propertyCoverage.length;
98
+ return {
99
+ source_range: site.location,
100
+ value_status: site.value.status,
101
+ fields,
102
+ property_coverage: propertyCoverage,
103
+ };
104
+ });
105
+ return {
106
+ shapes,
107
+ omittedSites: callable.returnSites.length - retainedSites.length,
108
+ omittedFields,
109
+ omittedCoverage,
110
+ };
111
+ };