rea-agents 2.1.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 (193) hide show
  1. package/README.md +84 -18
  2. package/dist/application/AndroidApplicationService.js +17 -0
  3. package/dist/application/AppleApplicationService.js +17 -0
  4. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  5. package/dist/application/BinarySession.js +9 -2
  6. package/dist/application/BinarySessionStatus.js +39 -1
  7. package/dist/application/CapabilityInventory.js +15 -0
  8. package/dist/application/ClientRegistrationStatus.js +11 -4
  9. package/dist/application/CommandShimReplay.js +6 -3
  10. package/dist/application/CompositeProvider.js +5 -2
  11. package/dist/application/DirectAnalysis.js +1 -20
  12. package/dist/application/DirectAnalysisStatus.js +83 -0
  13. package/dist/application/Doctor.js +34 -25
  14. package/dist/application/DoctorDiagnostics.js +1 -4
  15. package/dist/application/DoctorProjection.js +43 -0
  16. package/dist/application/DoctorScope.js +102 -0
  17. package/dist/application/EvidenceReferenceResolver.js +26 -0
  18. package/dist/application/InventoryProjectionEvidence.js +41 -0
  19. package/dist/application/InvestigationProviders.js +12 -0
  20. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  21. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  22. package/dist/application/JavaScriptApplicationService.js +3 -2
  23. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  24. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  25. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  26. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  27. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  29. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  30. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  31. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  32. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  33. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  34. package/dist/application/LoopbackReplay.js +277 -133
  35. package/dist/application/LoopbackReplayRecorder.js +101 -0
  36. package/dist/application/PermissionAuthority.js +7 -3
  37. package/dist/application/ProcessCaptureJournal.js +95 -0
  38. package/dist/application/ProcessCaptureLifecycle.js +24 -13
  39. package/dist/application/ProcessCheckpoints.js +30 -4
  40. package/dist/application/ProcessCli.js +26 -4
  41. package/dist/application/ProcessHarness.js +20 -50
  42. package/dist/application/ProcessNormalization.js +3 -2
  43. package/dist/application/ProcessPairedExperiment.js +72 -0
  44. package/dist/application/ProcessReactiveCoordinator.js +218 -0
  45. package/dist/application/ProcessReactiveEffects.js +133 -0
  46. package/dist/application/ProcessReactiveObservations.js +20 -0
  47. package/dist/application/ProcessSampling.js +2 -0
  48. package/dist/application/SessionProviderRouter.js +7 -6
  49. package/dist/application/Setup.js +14 -13
  50. package/dist/application/SetupClientConfiguration.js +10 -6
  51. package/dist/application/SetupPlan.js +4 -3
  52. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  53. package/dist/application/SetupSkill.js +44 -40
  54. package/dist/application/TerminalRenderer.js +3 -1
  55. package/dist/application/Uninstall.js +7 -1
  56. package/dist/application/Upgrade.js +16 -1
  57. package/dist/browser/CdpEndpoint.js +37 -4
  58. package/dist/catalogIdentity.js +16 -7
  59. package/dist/cli/coreAnalysisCommands.js +34 -2
  60. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  61. package/dist/cli/setupCommands.js +84 -30
  62. package/dist/cli/utilityCommands.js +10 -2
  63. package/dist/cliApplicationCommands.js +79 -16
  64. package/dist/cliCommandNames.js +3 -0
  65. package/dist/cliElectronCommands.js +23 -33
  66. package/dist/cliObservationOptions.js +1 -1
  67. package/dist/cliOutput.js +56 -0
  68. package/dist/cliProcessCommands.js +32 -1
  69. package/dist/cliSetup.js +55 -29
  70. package/dist/contracts/applicationToolContracts.js +54 -7
  71. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  72. package/dist/contracts/electronToolContracts.js +73 -4
  73. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  74. package/dist/contracts/managedToolContracts.js +7 -2
  75. package/dist/contracts/processCaptureExample.js +1 -0
  76. package/dist/contracts/replayMachineExample.js +37 -0
  77. package/dist/contracts/sessionStatusContract.js +26 -0
  78. package/dist/contracts/toolContractExamples.js +2 -0
  79. package/dist/contracts/toolContractTypes.js +12 -1
  80. package/dist/contracts/toolContracts.js +8 -11
  81. package/dist/contracts/toolEffects.js +3 -0
  82. package/dist/contracts/toolOutputSchemaGroups.js +53 -9
  83. package/dist/contracts/toolOutputSchemaPrimitives.js +73 -32
  84. package/dist/doctorRuntime.js +41 -0
  85. package/dist/domain/analysisErrorPresentation.js +5 -3
  86. package/dist/domain/analysisErrorProjection.js +7 -1
  87. package/dist/domain/androidApplication.js +206 -0
  88. package/dist/domain/appleApplication.js +221 -0
  89. package/dist/domain/boundedCartesianProjection.js +15 -0
  90. package/dist/domain/changedBehavior.js +5 -4
  91. package/dist/domain/completionLedgerGeneration.js +190 -0
  92. package/dist/domain/errors.js +4 -2
  93. package/dist/domain/evidenceCompletionLedger.js +113 -0
  94. package/dist/domain/hopperStartupFailure.js +9 -0
  95. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  96. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  97. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  98. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  99. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  100. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  101. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  102. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  103. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  104. package/dist/domain/javascriptSemanticCalls.js +269 -0
  105. package/dist/domain/javascriptSemanticGraph.js +356 -0
  106. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  107. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  108. package/dist/domain/javascriptSemanticIr.js +14 -1
  109. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  110. package/dist/domain/javascriptSemanticProjection.js +36 -4
  111. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  112. package/dist/domain/javascriptSemanticQuery.js +294 -0
  113. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  114. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  115. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  116. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  117. package/dist/domain/javascriptSemanticReturns.js +187 -0
  118. package/dist/domain/javascriptSemanticState.js +32 -0
  119. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  120. package/dist/domain/javascriptSemanticValues.js +54 -70
  121. package/dist/domain/processCapture.js +36 -0
  122. package/dist/domain/processCaptureValidation.js +101 -12
  123. package/dist/domain/processComparison.js +96 -24
  124. package/dist/domain/processObservation.js +153 -0
  125. package/dist/domain/processPairedExperiment.js +128 -0
  126. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  127. package/dist/domain/processReactiveMatching.js +210 -0
  128. package/dist/domain/processReactiveRuntime.js +109 -0
  129. package/dist/domain/processReactiveScenario.js +361 -0
  130. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  131. package/dist/domain/processReactiveTransition.js +159 -0
  132. package/dist/domain/processScenario.js +12 -0
  133. package/dist/domain/processTraceComparison.js +43 -0
  134. package/dist/domain/processTraceDimensionProjection.js +50 -0
  135. package/dist/domain/processTraceEvaluation.js +312 -0
  136. package/dist/domain/processTraceSpecification.js +373 -0
  137. package/dist/domain/reconstructionVerification.js +5 -4
  138. package/dist/domain/replayMachine.js +332 -0
  139. package/dist/domain/replayMachineRun.js +216 -0
  140. package/dist/domain/replayMachineRuntime.js +294 -0
  141. package/dist/domain/runtimeIdentification.js +210 -0
  142. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  143. package/dist/evaluation/CodexAgentEval.js +156 -0
  144. package/dist/generatedPackageMetadata.js +2 -2
  145. package/dist/ghidra/GhidraClient.js +13 -2
  146. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  147. package/dist/ghidra/GhidraLauncher.js +7 -3
  148. package/dist/ghidra/GhidraProvider.js +8 -1
  149. package/dist/hopper/BridgeLauncher.js +56 -14
  150. package/dist/hopper/HopperClient.js +23 -10
  151. package/dist/hopper/HopperDiagnostics.js +14 -0
  152. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  153. package/dist/hopper/HopperProvider.js +8 -1
  154. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  155. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  156. package/dist/identity.js +2 -1
  157. package/dist/main/transport.js +2 -0
  158. package/dist/mcpDoctor.js +310 -0
  159. package/dist/process/ProcessOwnership.js +200 -53
  160. package/dist/process/ProviderRunLineage.js +21 -0
  161. package/dist/server/createServer.js +8 -2
  162. package/dist/server/javascriptApplicationResult.js +106 -0
  163. package/dist/server/registerApplicationTools/characterization.js +3 -3
  164. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  165. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  166. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  167. package/dist/server/registerApplicationTools/coverage.js +3 -3
  168. package/dist/server/registerApplicationTools/helpers.js +37 -25
  169. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  170. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  171. package/dist/server/registerApplicationTools.js +4 -0
  172. package/dist/server/registerElectronTools.js +19 -5
  173. package/dist/server/registerEvidenceResources.js +7 -1
  174. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  175. package/dist/server/registerManagedTools.js +18 -1
  176. package/dist/server/registerProcessComparisonTool.js +17 -7
  177. package/dist/server/registerReplayMachineTool.js +20 -0
  178. package/dist/server/registerSessionStatusTool.js +95 -25
  179. package/dist/server/registerSessionTools.js +12 -7
  180. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  181. package/dist/server/toolResult.js +10 -4
  182. package/dist/serverIdentity.js +19 -0
  183. package/package.json +62 -29
  184. package/scripts/hopper-demo-x11.py +459 -61
  185. package/scripts/prepack.mjs +55 -0
  186. package/scripts/prepare.mjs +28 -0
  187. package/scripts/rea.mjs +26 -5
  188. package/skills/reverse-engineer-anything/SKILL.md +69 -228
  189. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  190. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  191. package/skills/reverse-engineer-anything/references/javascript-applications.md +35 -0
  192. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  193. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
@@ -64,11 +64,50 @@ const providerCapability = z.object({
64
64
  }),
65
65
  limitations: z.array(z.string()),
66
66
  });
67
- const providerIdentity = z.object({
67
+ export const providerIdentity = z.object({
68
68
  id: z.string(),
69
69
  name: z.string(),
70
70
  version: z.string().nullable(),
71
71
  });
72
+ const processCoordinates = {
73
+ launcher_pid: z.number().int().min(1),
74
+ process_group_id: z.number().int().min(1),
75
+ };
76
+ const processLineageObservation = z.discriminatedUnion("status", [
77
+ z.object({
78
+ status: z.literal("unavailable"),
79
+ observed_at: z.iso.datetime(),
80
+ ...processCoordinates,
81
+ reason: z.string().min(1),
82
+ }),
83
+ z.object({
84
+ status: z.literal("verified"),
85
+ observed_at: z.iso.datetime(),
86
+ schema_version: z.literal(1),
87
+ ...processCoordinates,
88
+ launcher_parent_pid: z.number().int().min(0),
89
+ descendants: z.array(z.object({
90
+ pid: z.number().int().min(1),
91
+ parent_pid: z.number().int().min(0),
92
+ process_group_id: z.number().int().min(1),
93
+ })),
94
+ }),
95
+ ]);
96
+ const analysisRun = z
97
+ .object({
98
+ run_id: z.string().uuid(),
99
+ process_lineage: z.discriminatedUnion("status", [
100
+ z.object({ status: z.literal("not_observed") }),
101
+ z.object({
102
+ status: z.literal("snapshots"),
103
+ snapshots: z.array(z.object({
104
+ provider: providerIdentity,
105
+ observation: processLineageObservation,
106
+ })),
107
+ }),
108
+ ]),
109
+ })
110
+ .nullable();
72
111
  const providerRejectionCode = z.enum(PROVIDER_REJECTION_CODES);
73
112
  const providerDiagnostics = z.record(z.string(), z.json());
74
113
  const providerAvailability = z.discriminatedUnion("status", [
@@ -111,10 +150,42 @@ const providerTargetSupport = z.discriminatedUnion("status", [
111
150
  diagnostics: providerDiagnostics,
112
151
  }),
113
152
  ]);
153
+ export const toolAvailability = z.object({
154
+ name: z.string(),
155
+ surface: z.string(),
156
+ available: z.boolean(),
157
+ reason: z.enum([
158
+ "available",
159
+ "target_required",
160
+ "provider_missing",
161
+ "provider_unavailable",
162
+ "target_unsupported",
163
+ "unsupported_host",
164
+ "policy_disabled",
165
+ ]),
166
+ remediation: z.string().nullable(),
167
+ effects: z.strictObject({
168
+ mutatesTarget: z.boolean(),
169
+ mutatesSession: z.boolean(),
170
+ writesFilesystem: z.boolean(),
171
+ launchesProcess: z.boolean(),
172
+ accessesNetwork: z.boolean(),
173
+ changesUiState: z.boolean(),
174
+ mayDiscardData: z.boolean(),
175
+ idempotent: z.boolean(),
176
+ }),
177
+ annotations: z.object({
178
+ read_only: z.boolean(),
179
+ destructive: z.boolean(),
180
+ idempotent: z.boolean(),
181
+ open_world: z.boolean(),
182
+ }),
183
+ });
114
184
  export const sessionProvider = z.object({
115
185
  provider: providerIdentity,
116
186
  providers: z.array(providerIdentity),
117
187
  capabilities: z.array(providerCapability),
188
+ analysis_run: analysisRun,
118
189
  analysis_provider_binding: z
119
190
  .object({
120
191
  provider: providerIdentity,
@@ -133,37 +204,7 @@ export const sessionProvider = z.object({
133
204
  selected: z.boolean(),
134
205
  capabilities: z.array(providerCapability),
135
206
  })),
136
- tool_availability: z.array(z.object({
137
- name: z.string(),
138
- surface: z.string(),
139
- available: z.boolean(),
140
- reason: z.enum([
141
- "available",
142
- "target_required",
143
- "provider_missing",
144
- "provider_unavailable",
145
- "target_unsupported",
146
- "unsupported_host",
147
- "policy_disabled",
148
- ]),
149
- remediation: z.string().nullable(),
150
- effects: z.strictObject({
151
- mutatesTarget: z.boolean(),
152
- mutatesSession: z.boolean(),
153
- writesFilesystem: z.boolean(),
154
- launchesProcess: z.boolean(),
155
- accessesNetwork: z.boolean(),
156
- changesUiState: z.boolean(),
157
- mayDiscardData: z.boolean(),
158
- idempotent: z.boolean(),
159
- }),
160
- annotations: z.object({
161
- read_only: z.boolean(),
162
- destructive: z.boolean(),
163
- idempotent: z.boolean(),
164
- open_world: z.boolean(),
165
- }),
166
- })),
207
+ tool_availability: z.array(toolAvailability),
167
208
  client_features: z.object({
168
209
  elicitation_form: z.boolean(),
169
210
  elicitation_url: z.boolean(),
@@ -1,12 +1,53 @@
1
1
  import { systemDoctorHost, } from "./application/Doctor.js";
2
+ import { fileURLToPath } from "node:url";
2
3
  import { inspectSystemGhidraProvider } from "./ghidra/GhidraDoctor.js";
4
+ import { selectLinuxPrivateDisplayStrategy } from "./hopper/LinuxPrivateDisplayProbe.js";
3
5
  import { parseConfig } from "./config.js";
4
6
  import { SystemJavaScriptReplayHost } from "./replay/SystemJavaScriptReplayHost.js";
5
7
  /** Compose provider diagnostics at the outer CLI adapter boundary. */
6
8
  export const createSystemDoctorHost = () => systemDoctorHost({
7
9
  providerInspections: async () => [await inspectSystemGhidraProvider()],
8
10
  javascriptReplayCheck: inspectJavaScriptReplay,
11
+ linuxDemoRuntimeCheck: inspectLinuxPrivateDisplay,
9
12
  });
13
+ const HOPPER_DEMO_HELPER_PATH = fileURLToPath(new URL("../scripts/hopper-demo-x11.py", import.meta.url));
14
+ const inspectLinuxPrivateDisplay = async () => {
15
+ const selection = await selectLinuxPrivateDisplayStrategy({
16
+ helperPath: HOPPER_DEMO_HELPER_PATH,
17
+ });
18
+ const value = selection.diagnostic;
19
+ const detail = [
20
+ `strategy=${selection.strategy}`,
21
+ `socket_directory=${value.socket_directory}`,
22
+ `mode=${value.socket_directory_mode ?? "unknown"}`,
23
+ `mount_read_only=${String(value.mount_read_only)}`,
24
+ `effective_mode=${value.effective_socket_directory_mode ?? "unknown"}`,
25
+ `effective_mount_read_only=${String(value.effective_mount_read_only)}`,
26
+ `wsl=${String(value.wsl)}`,
27
+ ].join("; ");
28
+ if (selection.ok)
29
+ return {
30
+ name: "hopper-demo-runtime",
31
+ ok: true,
32
+ classification: "healthy",
33
+ detail,
34
+ details: { private_display: { ...value } },
35
+ };
36
+ const missingDependency = value.failure_code === "runtime_dependency_unavailable" ||
37
+ value.failure_code === "x11_authorization_failed";
38
+ return {
39
+ name: "hopper-demo-runtime",
40
+ ok: false,
41
+ classification: missingDependency ? "missing_dependency" : "config_drift",
42
+ detail,
43
+ details: { private_display: { ...value } },
44
+ remediation: value.failure_code === "x11_socket_directory_unusable"
45
+ ? "Enable unprivileged user and mount namespaces, or provide a writable mode-1777 /tmp/.X11-unix directory. Do not mutate the current host mount with sudo; then rerun rea doctor --provider hopper."
46
+ : missingDependency
47
+ ? "Install Xvfb, xauth, Python 3, libX11, and libXtst, then rerun rea doctor --provider hopper."
48
+ : "Inspect the private-display diagnostic, correct the reported local condition, and rerun rea doctor --provider hopper.",
49
+ };
50
+ };
10
51
  const inspectJavaScriptReplay = async () => {
11
52
  const config = parseConfig(process.env);
12
53
  if (!config.ok)
@@ -5,9 +5,11 @@ export const analysisErrorRemediationAction = (error) => {
5
5
  if (error instanceof ReplayPlanStaleError)
6
6
  return "Review the rebuilt replay plan and explicitly approve its new digest.";
7
7
  if (error instanceof PermissionRequiredError)
8
- return error.elicitationSupported
9
- ? "Approve the exact missing scope, then retry the operation."
10
- : "Add the exact missing scope beneath the administrator ceiling, then retry.";
8
+ return error.restartRequired
9
+ ? "Add the exact missing scope to the administrator configuration, then restart the registered MCP server or client."
10
+ : error.elicitationSupported
11
+ ? "Approve the exact missing scope, then retry the operation."
12
+ : "Add the exact missing scope beneath the administrator ceiling, then retry.";
11
13
  return analysisErrorUserMessage(error);
12
14
  };
13
15
  export const analysisErrorCategory = (error) => {
@@ -230,7 +230,13 @@ const providerErrorDetails = (error) => {
230
230
  if (error instanceof HopperRemoteError)
231
231
  return { provider_code: error.code, diagnostic_type: error.diagnosticType };
232
232
  if (error instanceof HopperProcessError && error.failureCode !== undefined)
233
- return { failure_code: error.failureCode, exit_code: error.exitCode };
233
+ return {
234
+ failure_code: error.failureCode,
235
+ exit_code: error.exitCode,
236
+ ...(error.diagnostic === undefined
237
+ ? {}
238
+ : { diagnostics: { ...error.diagnostic } }),
239
+ };
234
240
  return undefined;
235
241
  };
236
242
  const lifecycleErrorDetails = (error) => {
@@ -0,0 +1,206 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
5
+ import { projectBoundedCartesian } from "./boundedCartesianProjection.js";
6
+ import { evidenceSchema } from "./evidence.js";
7
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
8
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
9
+ const pathSchema = z.string().min(1).max(4_096);
10
+ const componentSchema = z.strictObject({
11
+ path: pathSchema,
12
+ artifact_id: z.string().regex(/^art_[a-f0-9]{64}$/u),
13
+ sha256: digestSchema,
14
+ format: z.string().min(1).max(100),
15
+ });
16
+ /** Authenticated APK inventory pages projected as one Android application. */
17
+ export const androidApplicationProjectionInputSchema = z.strictObject({
18
+ inventory_evidence: z.array(evidenceSchema).min(1).max(100),
19
+ limits: z
20
+ .strictObject({
21
+ max_components: z.number().int().min(1).max(10_000).default(1_000),
22
+ })
23
+ .default({ max_components: 1_000 }),
24
+ });
25
+ /** Deterministic, execution-free Android application inventory projection. */
26
+ export const androidApplicationProjectionResultSchema = z.strictObject({
27
+ schema_version: z.literal(1),
28
+ projection_id: z.string().regex(/^adp_[a-f0-9]{64}$/u),
29
+ root_sha256: digestSchema,
30
+ root_format: z.literal("apk"),
31
+ source_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
32
+ components: z.strictObject({
33
+ manifests: z.array(componentSchema),
34
+ resources: z.array(componentSchema),
35
+ dex: z.array(componentSchema),
36
+ jvm_classes: z.array(componentSchema),
37
+ native_libraries: z.array(componentSchema),
38
+ javascript: z.array(componentSchema),
39
+ signing: z.array(componentSchema),
40
+ }),
41
+ runtime_families: z.array(z.enum([
42
+ "dalvik-art",
43
+ "java-kotlin",
44
+ "native",
45
+ "javascript",
46
+ "react-native",
47
+ "flutter",
48
+ "unity",
49
+ ])),
50
+ bridge_candidates: z.array(z.strictObject({
51
+ managed_path: pathSchema,
52
+ native_path: pathSchema,
53
+ basis: z.enum([
54
+ "managed-and-native-content",
55
+ "jni-library-convention",
56
+ "react-native-convention",
57
+ "flutter-convention",
58
+ "unity-convention",
59
+ ]),
60
+ })),
61
+ coverage: z.strictObject({
62
+ status: z.enum(["complete-within-inventory", "partial", "truncated"]),
63
+ inventory_complete: z.boolean(),
64
+ omitted_components: z.number().int().min(0),
65
+ omitted_bridge_candidates: z.number().int().min(0),
66
+ }),
67
+ limitations: z.array(z.string().min(1).max(4_096)).max(100),
68
+ });
69
+ /** Project exact APK paths and hashes without decoding or executing target code. */
70
+ export const projectAndroidApplication = (input) => {
71
+ const parsed = androidApplicationProjectionInputSchema.parse(input);
72
+ const { evidence, inventory } = parseArtifactInventoryEvidence(parsed.inventory_evidence);
73
+ if (inventory.manifest.root_format !== "apk")
74
+ throw new TypeError("Android application projection requires APK Evidence");
75
+ const nodes = new Map(inventory.nodes.map((node) => [node.artifact_id, node]));
76
+ const all = inventory.occurrences
77
+ .filter((occurrence) => occurrence.artifact_id !== null && occurrence.logical_path !== ".")
78
+ .map((occurrence) => {
79
+ const node = nodes.get(occurrence.artifact_id ?? "");
80
+ if (node === undefined)
81
+ throw new TypeError("Android application occurrence has no artifact node");
82
+ return {
83
+ path: occurrence.logical_path,
84
+ artifact_id: node.artifact_id,
85
+ sha256: node.sha256,
86
+ format: node.format,
87
+ };
88
+ });
89
+ const classified = classify(all);
90
+ const components = retain(classified, parsed.limits.max_components);
91
+ const omittedComponents = count(classified) - count(components);
92
+ const bridgeProjection = bridgeCandidates([...components.dex, ...components.jvm_classes], components.native_libraries, parsed.limits.max_components);
93
+ const limitations = [
94
+ ...(!inventory.complete
95
+ ? ["Source inventory pages are incomplete; absence is unknown."]
96
+ : []),
97
+ ...(omittedComponents > 0
98
+ ? [
99
+ `${String(omittedComponents)} component observations were omitted by the projection limit.`,
100
+ ]
101
+ : []),
102
+ ...(bridgeProjection.omitted > 0
103
+ ? [
104
+ `${String(bridgeProjection.omitted)} bridge hypotheses were omitted by the projection limit.`,
105
+ ]
106
+ : []),
107
+ "Manifest, resource, signing, and bytecode semantics require a dedicated Android provider; this projection reports exact inventory paths and hashes only.",
108
+ "Bridge candidates are path-based hypotheses, not decoded JNI declarations or observed runtime calls.",
109
+ ];
110
+ const withoutId = {
111
+ schema_version: 1,
112
+ root_sha256: inventory.manifest.root_sha256,
113
+ root_format: "apk",
114
+ source_evidence_ids: evidence
115
+ .map(({ evidence_id: id }) => id)
116
+ .sort(compare),
117
+ components,
118
+ runtime_families: runtimeFamilies(all),
119
+ bridge_candidates: bridgeProjection.candidates,
120
+ coverage: {
121
+ status: omittedComponents > 0 || bridgeProjection.omitted > 0
122
+ ? "truncated"
123
+ : inventory.complete
124
+ ? "complete-within-inventory"
125
+ : "partial",
126
+ inventory_complete: inventory.complete,
127
+ omitted_components: omittedComponents,
128
+ omitted_bridge_candidates: bridgeProjection.omitted,
129
+ },
130
+ limitations,
131
+ };
132
+ return androidApplicationProjectionResultSchema.parse({
133
+ ...withoutId,
134
+ projection_id: `adp_${digest(withoutId)}`,
135
+ });
136
+ };
137
+ const classify = (all) => ({
138
+ manifests: all.filter(({ path, format }) => format === "android-manifest" ||
139
+ /(?:^|\/)AndroidManifest\.xml$/u.test(path)),
140
+ resources: all.filter(({ path, format }) => format === "android-resources" || /(?:^|\/)resources\.arsc$/u.test(path)),
141
+ dex: all.filter(({ path, format }) => format === "dex" || /(?:^|\/)[^/]+\.dex$/iu.test(path)),
142
+ jvm_classes: all.filter(({ path, format }) => format === "jvm-class" || /(?:^|\/)[^/]+\.class$/iu.test(path)),
143
+ native_libraries: all.filter(({ path, format }) => format === "elf" || /(?:^|\/)lib\/[^/]+\/[^/]+\.so$/iu.test(path)),
144
+ javascript: all.filter(({ format }) => format === "javascript-bundle"),
145
+ signing: all.filter(({ path }) => /^META-INF\/[^/]+\.(?:MF|RSA|DSA|EC|SF)$/iu.test(path)),
146
+ });
147
+ const retain = (groups, maximum) => {
148
+ let remaining = maximum;
149
+ return Object.fromEntries(Object.entries(groups).map(([key, values]) => {
150
+ const selected = [...values]
151
+ .sort((left, right) => compare(left.path, right.path))
152
+ .slice(0, remaining);
153
+ remaining -= selected.length;
154
+ return [key, selected];
155
+ }));
156
+ };
157
+ const count = (groups) => Object.values(groups).reduce((total, values) => total + values.length, 0);
158
+ const runtimeFamilies = (all) => {
159
+ const paths = all.map(({ path }) => path.toLowerCase());
160
+ const families = new Set();
161
+ if (all.some(({ path, format }) => format === "dex" || path.endsWith(".dex")))
162
+ families.add("dalvik-art");
163
+ if (all.some(({ path, format }) => format === "jvm-class" || path.endsWith(".class")))
164
+ families.add("java-kotlin");
165
+ if (all.some(({ format }) => format === "elf"))
166
+ families.add("native");
167
+ if (all.some(({ format }) => format === "javascript-bundle"))
168
+ families.add("javascript");
169
+ if (paths.some((path) => path.includes("reactnative") || path.includes("hermes")))
170
+ families.add("react-native");
171
+ if (paths.some((path) => path.includes("libflutter.so")))
172
+ families.add("flutter");
173
+ if (paths.some((path) => path.includes("libunity.so")))
174
+ families.add("unity");
175
+ return [...families].sort(compare);
176
+ };
177
+ const bridgeCandidates = (managed, native, maximum) => {
178
+ const projection = projectBoundedCartesian(managed, native, maximum, (source, target) => ({
179
+ managed_path: source.path,
180
+ native_path: target.path,
181
+ basis: bridgeBasis(target.path),
182
+ }));
183
+ return {
184
+ candidates: projection.values,
185
+ omitted: projection.omitted,
186
+ };
187
+ };
188
+ const bridgeBasis = (path) => {
189
+ const lower = path.toLowerCase();
190
+ if (lower.includes("react") || lower.includes("hermes"))
191
+ return "react-native-convention";
192
+ if (lower.includes("flutter"))
193
+ return "flutter-convention";
194
+ if (lower.includes("unity"))
195
+ return "unity-convention";
196
+ if (/lib\/[^/]+\/lib[^/]+\.so$/u.test(lower))
197
+ return "jni-library-convention";
198
+ return "managed-and-native-content";
199
+ };
200
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
201
+ const digest = (value) => {
202
+ const encoded = canonicalize(value);
203
+ if (encoded === undefined)
204
+ throw new TypeError("Android application projection is not canonical JSON");
205
+ return createHash("sha256").update(encoded).digest("hex");
206
+ };
@@ -0,0 +1,221 @@
1
+ import { createHash } from "node:crypto";
2
+ import canonicalize from "canonicalize";
3
+ import { z } from "zod";
4
+ import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
5
+ import { projectBoundedCartesian } from "./boundedCartesianProjection.js";
6
+ import { evidenceSchema } from "./evidence.js";
7
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
8
+ const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
9
+ const boundedPathSchema = z.string().min(1).max(4_096);
10
+ const componentSchema = z.strictObject({
11
+ path: boundedPathSchema,
12
+ artifact_id: z.string().regex(/^art_[a-f0-9]{64}$/u),
13
+ sha256: digestSchema,
14
+ format: z.string().min(1).max(100),
15
+ });
16
+ /** Authenticated artifact inventory pages projected as one Apple application. */
17
+ export const appleApplicationProjectionInputSchema = z.strictObject({
18
+ inventory_evidence: z.array(evidenceSchema).min(1).max(100),
19
+ limits: z
20
+ .strictObject({
21
+ max_components: z.number().int().min(1).max(10_000).default(1_000),
22
+ })
23
+ .default({ max_components: 1_000 }),
24
+ });
25
+ /** Deterministic, execution-free Apple application inventory projection. */
26
+ export const appleApplicationProjectionResultSchema = z.strictObject({
27
+ schema_version: z.literal(1),
28
+ projection_id: z.string().regex(/^aap_[a-f0-9]{64}$/u),
29
+ root_sha256: digestSchema,
30
+ root_format: z.literal("ipa"),
31
+ source_evidence_ids: z.array(evidenceIdSchema).min(1).max(100),
32
+ application_roots: z.array(boundedPathSchema),
33
+ components: z.strictObject({
34
+ bundle_metadata: z.array(componentSchema),
35
+ executables: z.array(componentSchema),
36
+ frameworks: z.array(componentSchema),
37
+ native_libraries: z.array(componentSchema),
38
+ javascript: z.array(componentSchema),
39
+ signing: z.array(componentSchema),
40
+ }),
41
+ runtime_families: z.array(z.enum([
42
+ "native",
43
+ "swift-objective-c",
44
+ "javascript",
45
+ "react-native",
46
+ "flutter",
47
+ "unity",
48
+ ])),
49
+ bridge_candidates: z.array(z.strictObject({
50
+ source_path: boundedPathSchema,
51
+ native_path: boundedPathSchema,
52
+ basis: z.enum([
53
+ "javascript-and-native-content",
54
+ "react-native-convention",
55
+ "flutter-convention",
56
+ "unity-convention",
57
+ ]),
58
+ })),
59
+ coverage: z.strictObject({
60
+ status: z.enum(["complete-within-inventory", "partial", "truncated"]),
61
+ inventory_complete: z.boolean(),
62
+ omitted_components: z.number().int().min(0),
63
+ omitted_bridge_candidates: z.number().int().min(0),
64
+ }),
65
+ limitations: z.array(z.string().min(1).max(4_096)).max(100),
66
+ });
67
+ /** Project exact IPA paths and hashes without parsing or executing target code. */
68
+ export const projectAppleApplication = (input) => {
69
+ const parsed = appleApplicationProjectionInputSchema.parse(input);
70
+ const { evidence, inventory } = parseArtifactInventoryEvidence(parsed.inventory_evidence);
71
+ if (inventory.manifest.root_format !== "ipa")
72
+ throw new TypeError("Apple application projection requires IPA Evidence");
73
+ const nodes = new Map(inventory.nodes.map((node) => [node.artifact_id, node]));
74
+ const all = inventory.occurrences
75
+ .filter((occurrence) => occurrence.artifact_id !== null && occurrence.logical_path !== ".")
76
+ .map((occurrence) => {
77
+ const node = nodes.get(occurrence.artifact_id ?? "");
78
+ if (node === undefined)
79
+ throw new TypeError("Apple application occurrence has no artifact node");
80
+ return {
81
+ path: occurrence.logical_path,
82
+ artifact_id: node.artifact_id,
83
+ sha256: node.sha256,
84
+ format: node.format,
85
+ };
86
+ });
87
+ const roots = [
88
+ ...new Set(inventory.occurrences.flatMap(({ logical_path: path }) => {
89
+ const match = /^(Payload\/[^/]+\.app)(?:\/|$)/u.exec(path);
90
+ return match?.[1] === undefined ? [] : [match[1]];
91
+ })),
92
+ ].sort(compare);
93
+ const classified = classifyComponents(all, roots);
94
+ const retained = retainComponents(classified, parsed.limits.max_components);
95
+ const omitted = componentCount(classified) - componentCount(retained);
96
+ const runtimeFamilies = identifyRuntimeFamilies(all);
97
+ const bridgeProjection = identifyBridgeCandidates(retained.javascript, deduplicateComponents([
98
+ ...retained.executables,
99
+ ...retained.native_libraries,
100
+ ]), parsed.limits.max_components);
101
+ const limitations = projectionLimitations(inventory.complete, omitted, bridgeProjection.omitted, roots);
102
+ const withoutId = {
103
+ schema_version: 1,
104
+ root_sha256: inventory.manifest.root_sha256,
105
+ root_format: "ipa",
106
+ source_evidence_ids: evidence
107
+ .map(({ evidence_id: id }) => id)
108
+ .sort(compare),
109
+ application_roots: roots,
110
+ components: retained,
111
+ runtime_families: runtimeFamilies,
112
+ bridge_candidates: bridgeProjection.candidates,
113
+ coverage: {
114
+ status: omitted > 0 || bridgeProjection.omitted > 0
115
+ ? "truncated"
116
+ : inventory.complete
117
+ ? "complete-within-inventory"
118
+ : "partial",
119
+ inventory_complete: inventory.complete,
120
+ omitted_components: omitted,
121
+ omitted_bridge_candidates: bridgeProjection.omitted,
122
+ },
123
+ limitations,
124
+ };
125
+ return appleApplicationProjectionResultSchema.parse({
126
+ ...withoutId,
127
+ projection_id: `aap_${digest(withoutId)}`,
128
+ });
129
+ };
130
+ const classifyComponents = (all, roots) => {
131
+ const withinApp = (path) => roots.some((root) => path.startsWith(`${root}/`));
132
+ return {
133
+ bundle_metadata: all.filter(({ path }) => /(?:^|\/)Info\.plist$/u.test(path)),
134
+ executables: all.filter(({ path, format }) => withinApp(path) && ["mach-o", "mach-o-universal"].includes(format)),
135
+ frameworks: all.filter(({ path }) => /\.framework\//u.test(path)),
136
+ native_libraries: all.filter(({ path }) => /\.(?:dylib|so)$/iu.test(path)),
137
+ javascript: all.filter(({ format }) => format === "javascript-bundle"),
138
+ signing: all.filter(({ path }) => /(?:^|\/)(?:embedded\.mobileprovision|_CodeSignature\/CodeResources)$/u.test(path)),
139
+ };
140
+ };
141
+ const retainComponents = (groups, maximum) => {
142
+ let remaining = maximum;
143
+ return Object.fromEntries(Object.entries(groups).map(([key, values]) => {
144
+ const retained = [...values]
145
+ .sort((left, right) => compare(left.path, right.path))
146
+ .slice(0, remaining);
147
+ remaining -= retained.length;
148
+ return [key, retained];
149
+ }));
150
+ };
151
+ const componentCount = (groups) => Object.values(groups).reduce((total, values) => total + values.length, 0);
152
+ const deduplicateComponents = (values) => [...new Map(values.map((value) => [value.path, value])).values()].sort((left, right) => compare(left.path, right.path));
153
+ const identifyRuntimeFamilies = (all) => {
154
+ const paths = all.map(({ path }) => path.toLowerCase());
155
+ const families = new Set();
156
+ if (all.some(({ format }) => ["mach-o", "mach-o-universal"].includes(format)))
157
+ families.add("native");
158
+ if (paths.some((path) => /\.(?:dylib|framework\/[^/]+)$/u.test(path)))
159
+ families.add("swift-objective-c");
160
+ if (all.some(({ format }) => format === "javascript-bundle"))
161
+ families.add("javascript");
162
+ if (paths.some((path) => path.includes("reactnative") ||
163
+ path.includes("react.framework") ||
164
+ path.includes("hermes")))
165
+ families.add("react-native");
166
+ if (paths.some((path) => path.includes("flutter.framework") || path.includes("app.framework")))
167
+ families.add("flutter");
168
+ if (paths.some((path) => path.includes("unityframework.framework")))
169
+ families.add("unity");
170
+ return [...families].sort(compare);
171
+ };
172
+ const identifyBridgeCandidates = (scripts, native, maximum) => {
173
+ const projection = projectBoundedCartesian(scripts, native, maximum, (script, item) => ({
174
+ source_path: script.path,
175
+ native_path: item.path,
176
+ basis: bridgeBasis(item.path),
177
+ }));
178
+ return {
179
+ candidates: projection.values,
180
+ omitted: projection.omitted,
181
+ };
182
+ };
183
+ const bridgeBasis = (path) => {
184
+ const lower = path.toLowerCase();
185
+ if (lower.includes("react"))
186
+ return "react-native-convention";
187
+ if (lower.includes("flutter") || lower.includes("app.framework"))
188
+ return "flutter-convention";
189
+ if (lower.includes("unity"))
190
+ return "unity-convention";
191
+ return "javascript-and-native-content";
192
+ };
193
+ const projectionLimitations = (complete, omitted, omittedBridges, roots) => [
194
+ ...(!complete
195
+ ? ["Source inventory pages are incomplete; absence is unknown."]
196
+ : []),
197
+ ...(omitted > 0
198
+ ? [
199
+ `${String(omitted)} component observations were omitted by the projection limit.`,
200
+ ]
201
+ : []),
202
+ ...(omittedBridges > 0
203
+ ? [
204
+ `${String(omittedBridges)} bridge hypotheses were omitted by the projection limit.`,
205
+ ]
206
+ : []),
207
+ ...(roots.length === 0
208
+ ? [
209
+ "No Payload/*.app directory was present in the supplied inventory pages.",
210
+ ]
211
+ : []),
212
+ "Bundle identifiers and signing claims require dedicated plist and CMS parsing; this projection reports only exact paths and hashes.",
213
+ "Bridge candidates are path-based hypotheses, not observed runtime calls.",
214
+ ];
215
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
216
+ const digest = (value) => {
217
+ const encoded = canonicalize(value);
218
+ if (encoded === undefined)
219
+ throw new TypeError("Apple application projection is not canonical JSON");
220
+ return createHash("sha256").update(encoded).digest("hex");
221
+ };
@@ -0,0 +1,15 @@
1
+ /** Project a Cartesian product without materializing values beyond the output limit. */
2
+ export const projectBoundedCartesian = (left, right, maximum, project) => {
3
+ const values = [];
4
+ for (const leftValue of left) {
5
+ for (const rightValue of right) {
6
+ if (values.length === maximum)
7
+ return {
8
+ values,
9
+ omitted: left.length * right.length - values.length,
10
+ };
11
+ values.push(project(leftValue, rightValue));
12
+ }
13
+ }
14
+ return { values, omitted: 0 };
15
+ };