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
@@ -44,7 +44,7 @@ export const parseProcessEnvironment = (value) => Object.fromEntries(value
44
44
  return [entry.slice(0, separator), entry.slice(separator + 1)];
45
45
  }));
46
46
  const systemHost = {
47
- async listMembers(processGroupId) {
47
+ async listProcesses() {
48
48
  if (process.platform === "win32")
49
49
  return [];
50
50
  const { stdout } = await execFileAsync("ps", [
@@ -61,8 +61,7 @@ const systemHost = {
61
61
  processGroupId: Number(match[3]),
62
62
  state: match[4] ?? "",
63
63
  command: match[5] ?? "",
64
- }))
65
- .filter((member) => member.processGroupId === processGroupId);
64
+ }));
66
65
  },
67
66
  async environment(pid) {
68
67
  if (process.platform === "linux") {
@@ -95,73 +94,140 @@ const systemWindowsProcessTreeHost = {
95
94
  };
96
95
  /** Read the capture run token currently exposed by one live process. */
97
96
  export const readProcessRunId = async (pid, host = systemHost) => (await host.environment(pid)).REA_PROCESS_RUN_ID;
98
- /**
99
- * Verify run-token ownership before signaling a POSIX process group.
100
- *
101
- * Process IDs and group IDs can be reused. REA therefore re-reads every live
102
- * member's environment immediately before signaling and fails closed if any
103
- * member cannot be inspected or lacks the per-capture run token.
104
- */
97
+ /** Token-validate every rooted POSIX process group before signaling any. */
105
98
  export const cleanupOwnedProcessGroup = async (ownership, host = systemHost) => {
106
- let members;
99
+ let processes;
107
100
  try {
108
- members = await host.listMembers(ownership.processGroupId);
101
+ processes = liveProcesses(await host.listProcesses());
109
102
  }
110
103
  catch {
111
- return { cleaned: false, reason: "process group could not be inspected" };
104
+ return { cleaned: false, reason: "process table could not be inspected" };
112
105
  }
113
- const liveMembers = liveProcessGroupMembers(members);
114
- if (liveMembers.length === 0)
106
+ const plan = await createOwnedCleanupPlan(ownership, processes, host);
107
+ if ("cleaned" in plan)
108
+ return plan;
109
+ let signaled = false;
110
+ for (const processGroupId of plan.signalOrder) {
111
+ const revalidation = await revalidateOwnedProcessGroup(processGroupId, ownership, host);
112
+ if ("cleaned" in revalidation)
113
+ return revalidation;
114
+ if (revalidation.empty)
115
+ continue;
116
+ try {
117
+ host.signalGroup(processGroupId, "SIGKILL");
118
+ signaled = true;
119
+ }
120
+ catch (cause) {
121
+ const code = cause instanceof Error && "code" in cause ? cause.code : undefined;
122
+ if (code !== "ESRCH")
123
+ return { cleaned: false, reason: "owned process group signal failed" };
124
+ }
125
+ }
126
+ return { cleaned: true, signaled };
127
+ };
128
+ const createOwnedCleanupPlan = async (ownership, processes, host) => {
129
+ const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
130
+ const rootMembers = processes.filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
131
+ if (launcher === undefined && rootMembers.length === 0)
115
132
  return { cleaned: true, signaled: false };
116
- const leader = liveMembers.find((member) => member.pid === ownership.leaderPid);
117
- if (leader !== undefined) {
118
- if (ownership.expectedParentPid !== undefined &&
119
- leader.parentPid !== ownership.expectedParentPid)
120
- return {
121
- cleaned: false,
122
- reason: "owned launcher parent identity did not match",
123
- };
124
- if (ownership.expectedCommand !== undefined &&
125
- !commandMatches(leader.command, ownership.expectedCommand))
133
+ let descendants = [];
134
+ if (launcher !== undefined) {
135
+ const identityFailure = launcherIdentityFailure(launcher, ownership);
136
+ if (identityFailure !== null)
137
+ return { cleaned: false, reason: identityFailure };
138
+ descendants = descendantsOf(launcher.pid, processes);
139
+ }
140
+ const descendantPids = new Set(descendants.map(({ pid }) => pid));
141
+ const processGroupIds = new Set([ownership.processGroupId]);
142
+ for (const descendant of descendants)
143
+ processGroupIds.add(descendant.processGroupId);
144
+ for (const processGroupId of processGroupIds) {
145
+ if (processGroupId === ownership.processGroupId)
146
+ continue;
147
+ const groupLeader = processes.find(({ pid, processGroupId: observedGroupId }) => pid === processGroupId && observedGroupId === processGroupId);
148
+ if (groupLeader === undefined || !descendantPids.has(groupLeader.pid))
126
149
  return {
127
150
  cleaned: false,
128
- reason: "owned launcher command identity did not match",
151
+ reason: "descendant process-group leader identity could not be verified",
129
152
  };
130
153
  }
154
+ const liveMembers = processes.filter(({ processGroupId }) => processGroupIds.has(processGroupId));
155
+ const failures = await processOwnershipFailures(liveMembers, ownership.runId, host);
156
+ if (failures.length > 0)
157
+ return cleanupValidationFailure(failures);
158
+ return {
159
+ signalOrder: [ownership.processGroupId].concat([...processGroupIds]
160
+ .filter((processGroupId) => processGroupId !== ownership.processGroupId)
161
+ .sort((left, right) => left - right)),
162
+ };
163
+ };
164
+ const launcherIdentityFailure = (launcher, ownership) => {
165
+ if (launcher.processGroupId !== ownership.processGroupId)
166
+ return "owned launcher process-group identity did not match";
167
+ if (ownership.expectedParentPid !== undefined &&
168
+ launcher.parentPid !== ownership.expectedParentPid)
169
+ return "owned launcher parent identity did not match";
170
+ if (ownership.expectedCommand !== undefined &&
171
+ !commandMatches(launcher.command, ownership.expectedCommand))
172
+ return "owned launcher command identity did not match";
173
+ return null;
174
+ };
175
+ const revalidateOwnedProcessGroup = async (processGroupId, ownership, host) => {
176
+ let members;
177
+ try {
178
+ members = liveProcesses(await host.listProcesses()).filter(({ processGroupId: observedGroupId }) => observedGroupId === processGroupId);
179
+ }
180
+ catch {
181
+ return {
182
+ cleaned: false,
183
+ reason: "process ownership could not be revalidated",
184
+ };
185
+ }
186
+ const failures = await processOwnershipFailures(members, ownership.runId, host);
187
+ if (failures.length > 0)
188
+ return cleanupValidationFailure(failures);
189
+ if (processGroupId === ownership.processGroupId) {
190
+ const launcher = members.find(({ pid }) => pid === ownership.leaderPid);
191
+ if (launcher !== undefined) {
192
+ const identityFailure = launcherIdentityFailure(launcher, ownership);
193
+ if (identityFailure !== null)
194
+ return { cleaned: false, reason: identityFailure };
195
+ }
196
+ }
197
+ if (processGroupId !== ownership.processGroupId &&
198
+ members.length > 0 &&
199
+ !members.some(({ pid }) => pid === processGroupId))
200
+ return {
201
+ cleaned: false,
202
+ reason: "descendant process-group leader identity could not be revalidated",
203
+ };
204
+ return { empty: members.length === 0 };
205
+ };
206
+ const cleanupValidationFailure = (failures) => ({
207
+ cleaned: false,
208
+ reason: failures.some(({ reason }) => reason === "run-token-mismatch")
209
+ ? "process tree contains an unowned or PID-reused process"
210
+ : "process ownership could not be revalidated",
211
+ failures,
212
+ });
213
+ const processOwnershipFailures = async (members, runId, host) => {
131
214
  const failures = [];
132
- for (const member of liveMembers) {
215
+ for (const member of members) {
133
216
  try {
134
- const environment = await host.environment(member.pid);
135
- if (environment.REA_PROCESS_RUN_ID !== ownership.runId)
217
+ if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !== runId)
136
218
  failures.push({ pid: member.pid, reason: "run-token-mismatch" });
137
219
  }
138
220
  catch {
139
221
  failures.push({ pid: member.pid, reason: "environment-unreadable" });
140
222
  }
141
223
  }
142
- if (failures.length > 0)
143
- return {
144
- cleaned: false,
145
- reason: failures.some(({ reason }) => reason === "run-token-mismatch")
146
- ? "process group contains an unowned or PID-reused process"
147
- : "process ownership could not be revalidated",
148
- failures,
149
- };
150
- try {
151
- host.signalGroup(ownership.processGroupId, "SIGKILL");
152
- }
153
- catch (cause) {
154
- const code = cause instanceof Error && "code" in cause ? cause.code : undefined;
155
- if (code !== "ESRCH")
156
- return { cleaned: false, reason: "owned process group signal failed" };
157
- }
158
- return { cleaned: true, signaled: true };
224
+ return failures;
159
225
  };
160
226
  /** Observe one group without signaling it, failing closed on identity doubt. */
161
227
  export const observeOwnedProcessGroup = async (ownership, host = systemHost) => {
162
228
  let members;
163
229
  try {
164
- members = await host.listMembers(ownership.processGroupId);
230
+ members = (await host.listProcesses()).filter(({ processGroupId }) => processGroupId === ownership.processGroupId);
165
231
  }
166
232
  catch {
167
233
  return {
@@ -169,7 +235,7 @@ export const observeOwnedProcessGroup = async (ownership, host = systemHost) =>
169
235
  reason: "process group could not be inspected",
170
236
  };
171
237
  }
172
- const liveMembers = liveProcessGroupMembers(members);
238
+ const liveMembers = liveProcesses(members);
173
239
  if (liveMembers.length === 0)
174
240
  return { state: "empty" };
175
241
  for (const member of liveMembers) {
@@ -190,10 +256,91 @@ export const observeOwnedProcessGroup = async (ownership, host = systemHost) =>
190
256
  }
191
257
  return { state: "alive" };
192
258
  };
193
- const liveProcessGroupMembers = (members) => members.filter(({ state }) => !state.startsWith("Z"));
259
+ /**
260
+ * Record the live launcher and descendant lineage after run-token validation.
261
+ *
262
+ * The observation is intentionally point-in-time. A verified empty descendant
263
+ * list means no descendants were live during this observation, not that the
264
+ * run never created a short-lived child.
265
+ */
266
+ export const observeOwnedProcessLineage = async (ownership, host = systemHost) => {
267
+ let processes;
268
+ try {
269
+ processes = liveProcesses(await host.listProcesses());
270
+ }
271
+ catch {
272
+ return unavailableLineage(ownership, "process table could not be inspected");
273
+ }
274
+ const launcher = processes.find(({ pid }) => pid === ownership.leaderPid);
275
+ if (launcher === undefined)
276
+ return unavailableLineage(ownership, "owned launcher is not live");
277
+ if (launcher.processGroupId !== ownership.processGroupId)
278
+ return unavailableLineage(ownership, "owned launcher process-group identity did not match");
279
+ if (ownership.expectedParentPid !== undefined &&
280
+ launcher.parentPid !== ownership.expectedParentPid)
281
+ return unavailableLineage(ownership, "owned launcher parent identity did not match");
282
+ if (ownership.expectedCommand !== undefined &&
283
+ !commandMatches(launcher.command, ownership.expectedCommand))
284
+ return unavailableLineage(ownership, "owned launcher command identity did not match");
285
+ const descendants = descendantsOf(launcher.pid, processes);
286
+ for (const member of [launcher, ...descendants]) {
287
+ try {
288
+ if ((await host.environment(member.pid)).REA_PROCESS_RUN_ID !==
289
+ ownership.runId)
290
+ return unavailableLineage(ownership, "process lineage contains an unowned or PID-reused process");
291
+ }
292
+ catch {
293
+ return unavailableLineage(ownership, "process ownership could not be revalidated");
294
+ }
295
+ }
296
+ return {
297
+ status: "verified",
298
+ observedAt: new Date().toISOString(),
299
+ lineage: {
300
+ schemaVersion: 1,
301
+ runId: ownership.runId,
302
+ launcherPid: launcher.pid,
303
+ launcherParentPid: launcher.parentPid,
304
+ processGroupId: launcher.processGroupId,
305
+ descendants: descendants
306
+ .sort((left, right) => left.pid - right.pid)
307
+ .map(({ pid, parentPid, processGroupId }) => ({
308
+ pid,
309
+ parentPid,
310
+ processGroupId,
311
+ })),
312
+ },
313
+ };
314
+ };
315
+ const descendantsOf = (launcherPid, processes) => {
316
+ const childrenByParent = new Map();
317
+ for (const process of processes) {
318
+ const siblings = childrenByParent.get(process.parentPid) ?? [];
319
+ siblings.push(process);
320
+ childrenByParent.set(process.parentPid, siblings);
321
+ }
322
+ const descendants = [];
323
+ const pending = [...(childrenByParent.get(launcherPid) ?? [])];
324
+ const visited = new Set([launcherPid]);
325
+ for (const process of pending) {
326
+ if (visited.has(process.pid))
327
+ continue;
328
+ visited.add(process.pid);
329
+ descendants.push(process);
330
+ pending.push(...(childrenByParent.get(process.pid) ?? []));
331
+ }
332
+ return descendants;
333
+ };
334
+ const unavailableLineage = (ownership, reason) => ({
335
+ status: "unavailable",
336
+ observedAt: new Date().toISOString(),
337
+ runId: ownership.runId,
338
+ launcherPid: ownership.leaderPid,
339
+ processGroupId: ownership.processGroupId,
340
+ reason,
341
+ });
342
+ const liveProcesses = (members) => members.filter(({ state }) => !state.startsWith("Z"));
194
343
  const commandMatches = (actual, expected) => {
195
- const normalizedActual = actual.trim();
196
- const normalizedExpected = expected.trim();
197
- return (normalizedActual === normalizedExpected ||
198
- normalizedActual.startsWith(`${normalizedExpected} `));
344
+ return (actual.trim() === expected.trim() ||
345
+ actual.trim().startsWith(`${expected.trim()} `));
199
346
  };
@@ -0,0 +1,21 @@
1
+ import { observeOwnedProcessLineage } from "./ProcessOwnership.js";
2
+ /** Retain the latest truthful process-lineage observation for one client. */
3
+ export class ProviderRunLineage {
4
+ #observation = null;
5
+ /** Clear stale lineage before a new launch attempt. */
6
+ reset() {
7
+ this.#observation = null;
8
+ }
9
+ /** Observe a token-owned launcher without signaling it. */
10
+ async observe(launch) {
11
+ if (launch?.ownership === undefined)
12
+ return;
13
+ this.#observation = await observeOwnedProcessLineage(launch.ownership);
14
+ }
15
+ /** Return a detached copy suitable for caller-visible status. */
16
+ snapshot() {
17
+ return this.#observation === null
18
+ ? null
19
+ : structuredClone(this.#observation);
20
+ }
21
+ }
@@ -15,6 +15,8 @@ import { createServerIdentity } from "../serverIdentity.js";
15
15
  import { registerBrowserTools } from "./registerBrowserTools.js";
16
16
  import { registerElectronTools } from "./registerElectronTools.js";
17
17
  import { registerApplicationTools } from "./registerApplicationTools.js";
18
+ const TARGET_FREE_INSTRUCTIONS = "REA analyzes shipped artifacts. Route: ASAR/JavaScript -> analyze_javascript_application (configured root required); archive/package -> inventory_artifact; managed PE/CLI -> inspect_managed_artifact; browser/Electron runtime -> list_browser_targets/list_electron_targets; native binary/database -> open_binary, then binary_overview. Check binary_session for policy. Start with summaries and cite Evidence IDs. Never repeat identical analysis or read full Evidence without a specific need.";
19
+ const ACTIVE_TARGET_INSTRUCTIONS = "REA analyzes the active reverse-engineering target. Start native analysis with binary_overview, then narrow with analyze_function, literal search, callers, callees, and xrefs. Prefer summary views, never repeat an identical call, and read full Evidence only when the task requires it.";
18
20
  import { LinuxJavaScriptReplayRunner } from "../replay/LinuxJavaScriptReplayRunner.js";
19
21
  import { SystemJavaScriptReplayHost } from "../replay/SystemJavaScriptReplayHost.js";
20
22
  import { PROCESS_CAPTURE_ELICITATION_POLICY, } from "./ProcessCaptureElicitation.js";
@@ -45,8 +47,8 @@ export const createServer = (analysis, session, options = {}) => {
45
47
  },
46
48
  requestState: { verify: processCaptureStateCodec.verify },
47
49
  instructions: session === undefined
48
- ? "Reverse-engineering tools for an active analysis target. Start with binary_overview."
49
- : "Reverse-engineering tools for configured analysis providers. Open a target with open_binary, then start with binary_overview.",
50
+ ? ACTIVE_TARGET_INSTRUCTIONS
51
+ : TARGET_FREE_INSTRUCTIONS,
50
52
  });
51
53
  server.server.onclose = () => {
52
54
  permissionAuthority?.clearSessionGrants();
@@ -168,6 +170,7 @@ const registerBinaryAnalysisTools = ({ server, analysis, session, options, logge
168
170
  logger,
169
171
  activeTarget,
170
172
  recordEvidence,
173
+ session,
171
174
  });
172
175
  if (session !== undefined)
173
176
  registerManagedWorkflowTools(server, {
@@ -202,6 +205,9 @@ const registerObservationTools = ({ server, session, options, logger, permission
202
205
  logger,
203
206
  recordEvidence,
204
207
  recordEvidenceWithUnknown,
208
+ evidenceLookup: session === undefined
209
+ ? undefined
210
+ : (evidenceId) => session.evidenceById(evidenceId),
205
211
  evidenceFilePolicy: options.evidenceFilePolicy ?? {
206
212
  roots: [],
207
213
  maxBytes: 1,
@@ -0,0 +1,106 @@
1
+ import { javascriptApplicationAnalysisResultSchema } from "../domain/javascriptApplicationAnalysis.js";
2
+ import { compareCodePoints } from "../domain/javascriptApplicationGraph.js";
3
+ import { javascriptApplicationAnalysisSummarySchema } from "../contracts/electronToolContracts.js";
4
+ const DEFAULT_GRAPH_PAGE_LIMIT = 100;
5
+ const ROOT_SAMPLE_LIMIT = 10;
6
+ const TOP_FINDING_LIMIT = 20;
7
+ const FINDING_PRIORITY = {
8
+ "browser-window": 0,
9
+ "electron-preload": 1,
10
+ "context-bridge-api": 2,
11
+ "ipc-channel": 3,
12
+ "ipc-handler": 4,
13
+ "electron-utility": 5,
14
+ endpoint: 6,
15
+ storage: 7,
16
+ worker: 8,
17
+ "service-worker": 9,
18
+ "native-addon": 10,
19
+ "native-export": 11,
20
+ "source-map": 12,
21
+ };
22
+ /** Project complete JavaScript application Evidence into a bounded agent result. */
23
+ export const summarizeJavaScriptApplicationEvidence = (evidence) => {
24
+ const result = javascriptApplicationAnalysisResultSchema.parse(evidence.normalized_result);
25
+ const nodeKinds = new Map();
26
+ for (const node of result.graph.nodes)
27
+ nodeKinds.set(node.kind, (nodeKinds.get(node.kind) ?? 0) + 1);
28
+ const rootItems = result.graph.root_node_ids.slice(0, ROOT_SAMPLE_LIMIT);
29
+ const pageRoot = `rea://evidence/${evidence.evidence_id}/application-graph`;
30
+ const topFindings = [...result.graph.nodes]
31
+ .sort((left, right) => (FINDING_PRIORITY[left.kind] ?? Number.MAX_SAFE_INTEGER) -
32
+ (FINDING_PRIORITY[right.kind] ?? Number.MAX_SAFE_INTEGER) ||
33
+ compareCodePoints(left.node_id, right.node_id))
34
+ .slice(0, TOP_FINDING_LIMIT)
35
+ .map((node) => {
36
+ const observation = node.observations[0];
37
+ if (observation === undefined)
38
+ throw new TypeError("Application graph node has no observation");
39
+ return {
40
+ node_id: node.node_id,
41
+ kind: node.kind,
42
+ label: observation.label,
43
+ authority: observation.evidence.authority,
44
+ state: observation.evidence.state,
45
+ confidence: observation.evidence.confidence,
46
+ location: observation.evidence.location,
47
+ };
48
+ });
49
+ const summary = result.schema_version === 1 ? omitGraph(result) : omitGraphs(result);
50
+ const unknowns = new Set([
51
+ ...result.limitations,
52
+ ...result.graph.limitations,
53
+ ]);
54
+ if (result.statistics.parse_failures > 0)
55
+ unknowns.add(`${String(result.statistics.parse_failures)} JavaScript file(s) could not be parsed.`);
56
+ if (result.statistics.truncated_scopes > 0)
57
+ unknowns.add(`${String(result.statistics.truncated_scopes)} analysis scope(s) were truncated.`);
58
+ if (result.graph.coverage.status !== "complete")
59
+ unknowns.add(`Application graph coverage is ${result.graph.coverage.status}; omitted facts remain unknown.`);
60
+ return javascriptApplicationAnalysisSummarySchema.parse({
61
+ ...summary,
62
+ ...(result.schema_version === 1
63
+ ? {}
64
+ : {
65
+ semantic_graph: {
66
+ graph_id: result.semantic_graph.graph_id,
67
+ nodes: result.semantic_graph.nodes.length,
68
+ relations: result.semantic_graph.relations.length,
69
+ unknown_frontiers: result.semantic_graph.unknowns.length,
70
+ fingerprints: result.semantic_graph.fingerprints.length,
71
+ coverage: result.semantic_graph.coverage.status,
72
+ query_tool: "trace_javascript_semantics",
73
+ },
74
+ }),
75
+ unknowns: [...unknowns],
76
+ graph: {
77
+ graph_id: result.graph.graph_id,
78
+ node_count: result.graph.nodes.length,
79
+ edge_count: result.graph.edges.length,
80
+ roots: {
81
+ items: rootItems,
82
+ total: result.graph.root_node_ids.length,
83
+ truncated: rootItems.length < result.graph.root_node_ids.length,
84
+ },
85
+ node_kinds: [...nodeKinds]
86
+ .sort(([left], [right]) => compareCodePoints(left, right))
87
+ .map(([kind, count]) => ({ kind, count })),
88
+ top_findings: topFindings,
89
+ coverage: result.graph.coverage,
90
+ limitations: result.graph.limitations,
91
+ pages: {
92
+ nodes: `${pageRoot}/nodes/offset/0/limit/${String(DEFAULT_GRAPH_PAGE_LIMIT)}`,
93
+ edges: `${pageRoot}/edges/offset/0/limit/${String(DEFAULT_GRAPH_PAGE_LIMIT)}`,
94
+ page_limit: DEFAULT_GRAPH_PAGE_LIMIT,
95
+ },
96
+ },
97
+ });
98
+ };
99
+ const omitGraph = (result) => {
100
+ const { graph: _graph, ...summary } = result;
101
+ return summary;
102
+ };
103
+ const omitGraphs = (result) => {
104
+ const { graph: _graph, semantic_graph: _semanticGraph, ...summary } = result;
105
+ return summary;
106
+ };
@@ -1,5 +1,5 @@
1
1
  import { executeNodeCharacterization, prepareNodeCharacterization, } from "../../application/NodeRuntimeCharacterizationService.js";
2
- import { APPLICATION_TOOL_CONTRACTS } from "../../contracts/applicationToolContracts.js";
2
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
3
3
  import { nodeCharacterizationExecutionInputSchema, nodeCharacterizationExecutionOutputSchema, nodeCharacterizationPreparationInputSchema, nodeCharacterizationPreparationOutputSchema, } from "../../domain/nodeRuntimeCharacterization.js";
4
4
  import { parseEvidence } from "../../domain/evidence.js";
5
5
  import { logToolExecution } from "../toolLogging.js";
@@ -8,8 +8,8 @@ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
8
8
  import { safeParseToolInput } from "../toolInputValidation.js";
9
9
  import { mcpProgressReporter } from "../mcpProgress.js";
10
10
  import { recordSources } from "./helpers.js";
11
- const prepareContract = APPLICATION_TOOL_CONTRACTS[3];
12
- const executeContract = APPLICATION_TOOL_CONTRACTS[4];
11
+ const prepareContract = applicationToolContract("prepare_node_characterization");
12
+ const executeContract = applicationToolContract("execute_node_characterization");
13
13
  /** Register node runtime characterization prepare and execute tools. */
14
14
  export const registerCharacterizationTools = (server, options) => {
15
15
  server.registerTool(prepareContract.name, toolRegistrationOptions(prepareContract), async (input, context) => {
@@ -0,0 +1,37 @@
1
+ import { resolveCompareJavaScriptExportShapesRequestValidated } from "../../application/ApplicationWorkflowEvidenceResolver.js";
2
+ import { compareJavaScriptExportShapesEvidenceValidated } from "../../application/JavaScriptApplicationWorkflowService.js";
3
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
4
+ import { compareJavaScriptExportShapesRequestSchema } from "../../contracts/applicationWorkflowInputContracts.js";
5
+ import { javaScriptExportShapeComparisonResultSchema } from "../../domain/javascriptExportShapeComparisonSchemas.js";
6
+ import { safeParseToolInput } from "../toolInputValidation.js";
7
+ import { logToolExecution } from "../toolLogging.js";
8
+ import { toCallToolResult } from "../toolResult.js";
9
+ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
10
+ import { recordResult, recordSources } from "./helpers.js";
11
+ const contract = applicationToolContract("compare_javascript_export_shapes");
12
+ /** Register the execution-free exact JavaScript export-shape comparison. */
13
+ export const registerCompareJavaScriptExportShapesTool = (server, options) => {
14
+ server.registerTool(contract.name, toolRegistrationOptions(contract), async (input) => {
15
+ const parsedInput = safeParseToolInput(compareJavaScriptExportShapesRequestSchema, input, contract.name);
16
+ if (!parsedInput.ok)
17
+ return toCallToolResult(parsedInput, contract);
18
+ const resolved = resolveCompareJavaScriptExportShapesRequestValidated(parsedInput.value, options.evidenceLookup);
19
+ if (!resolved.ok)
20
+ return toCallToolResult(resolved, contract);
21
+ const parsed = resolved.value;
22
+ const result = await logToolExecution(options.logger, contract.name, () => Promise.resolve(compareJavaScriptExportShapesEvidenceValidated(parsed)));
23
+ if (!result.ok)
24
+ return toCallToolResult(result, contract);
25
+ const recorded = recordSources(options.recordEvidence, [
26
+ parsed.left,
27
+ parsed.right,
28
+ ]);
29
+ if (!recorded.ok)
30
+ return toCallToolResult(recorded, contract);
31
+ const comparison = javaScriptExportShapeComparisonResultSchema.parse(result.value.normalized_result);
32
+ const unknown = comparison.summary.unknown > 0;
33
+ return recordResult(options, contract, result.value, parsed.unknown_registry_approved === true && unknown
34
+ ? "javascript-export-shape"
35
+ : undefined);
36
+ });
37
+ };
@@ -1,20 +1,24 @@
1
1
  import { compareApplicationVersionsEvidenceValidated } from "../../application/JavaScriptApplicationWorkflowService.js";
2
- import { APPLICATION_TOOL_CONTRACTS } from "../../contracts/applicationToolContracts.js";
3
- import { compareApplicationVersionsInputSchema } from "../../domain/javascriptApplicationVersionComparisonSchemas.js";
2
+ import { resolveCompareApplicationVersionsRequestValidated } from "../../application/ApplicationWorkflowEvidenceResolver.js";
3
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
4
+ import { compareApplicationVersionsRequestSchema } from "../../contracts/applicationWorkflowInputContracts.js";
4
5
  import { applicationVersionComparisonResultSchema } from "../../domain/javascriptApplicationVersionComparisonSchemas.js";
5
6
  import { logToolExecution } from "../toolLogging.js";
6
7
  import { toCallToolResult } from "../toolResult.js";
7
8
  import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
8
9
  import { safeParseToolInput } from "../toolInputValidation.js";
9
10
  import { recordResult, recordSources } from "./helpers.js";
10
- const compareContract = APPLICATION_TOOL_CONTRACTS[1];
11
+ const compareContract = applicationToolContract("compare_application_versions");
11
12
  /** Register the provider-neutral JavaScript version comparison tool. */
12
13
  export const registerCompareApplicationVersionsTool = (server, options) => {
13
14
  server.registerTool(compareContract.name, toolRegistrationOptions(compareContract), async (input) => {
14
- const parsedInput = safeParseToolInput(compareApplicationVersionsInputSchema, input, compareContract.name);
15
+ const parsedInput = safeParseToolInput(compareApplicationVersionsRequestSchema, input, compareContract.name);
15
16
  if (!parsedInput.ok)
16
17
  return toCallToolResult(parsedInput, compareContract);
17
- const parsed = parsedInput.value;
18
+ const resolved = resolveCompareApplicationVersionsRequestValidated(parsedInput.value, options.evidenceLookup);
19
+ if (!resolved.ok)
20
+ return toCallToolResult(resolved, compareContract);
21
+ const parsed = resolved.value;
18
22
  const result = await logToolExecution(options.logger, compareContract.name, () => Promise.resolve(compareApplicationVersionsEvidenceValidated(parsed)));
19
23
  if (!result.ok)
20
24
  return toCallToolResult(result, compareContract);
@@ -29,6 +33,8 @@ export const registerCompareApplicationVersionsTool = (server, options) => {
29
33
  return toCallToolResult(recorded, compareContract);
30
34
  const comparison = applicationVersionComparisonResultSchema.parse(result.value.normalized_result);
31
35
  const unknown = comparison.summary.unknown > 0;
32
- return recordResult(options, compareContract, result.value, parsed.unknown_registry_approved === true && unknown);
36
+ return recordResult(options, compareContract, result.value, parsed.unknown_registry_approved === true && unknown
37
+ ? "application-version-comparison"
38
+ : undefined);
33
39
  });
34
40
  };
@@ -1,5 +1,5 @@
1
1
  import { runControlledReplayValidated } from "../../application/JavaScriptReplayService.js";
2
- import { APPLICATION_TOOL_CONTRACTS } from "../../contracts/applicationToolContracts.js";
2
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
3
3
  import { controlledReplayInputSchema, controlledReplayOutputSchema, } from "../../domain/javascriptReplay.js";
4
4
  import { parseEvidence } from "../../domain/evidence.js";
5
5
  import { logToolExecution } from "../toolLogging.js";
@@ -8,7 +8,7 @@ import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
8
8
  import { safeParseToolInput } from "../toolInputValidation.js";
9
9
  import { mcpProgressReporter } from "../mcpProgress.js";
10
10
  import { recordSources } from "./helpers.js";
11
- const replayContract = APPLICATION_TOOL_CONTRACTS[2];
11
+ const replayContract = applicationToolContract("run_controlled_replay");
12
12
  /** Register the controlled JavaScript replay tool. */
13
13
  export const registerControlledReplayTool = (server, options) => {
14
14
  server.registerTool(replayContract.name, toolRegistrationOptions(replayContract), async (input, context) => {
@@ -1,7 +1,7 @@
1
1
  import { commitReconstructionCoverage, queryReconstructionCoverage, } from "../../application/ReconstructionCoverageService.js";
2
2
  import { readReconstructionCoverageWorkspace } from "../../application/ReconstructionCoverageWorkspaceStore.js";
3
3
  import { authorizeFileReadWithDeferredWrite, authorizeRootPermission, } from "../../application/DeferredFileAuthorization.js";
4
- import { APPLICATION_TOOL_CONTRACTS } from "../../contracts/applicationToolContracts.js";
4
+ import { applicationToolContract } from "../../contracts/applicationToolContracts.js";
5
5
  import { reconstructionCoverageCommitInputSchema, reconstructionCoverageQueryInputSchema, } from "../../application/ReconstructionCoverageService.js";
6
6
  import { reconstructionClosureResultSchema } from "../../domain/reconstructionCoverage.js";
7
7
  import { AnalysisCapabilityUnavailableError, EvidenceIntegrityError, } from "../../domain/errors.js";
@@ -11,8 +11,8 @@ import { toCallToolResult } from "../toolResult.js";
11
11
  import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
12
12
  import { safeParseToolInput } from "../toolInputValidation.js";
13
13
  import { coverageWorkspaceUri } from "./helpers.js";
14
- const commitContract = APPLICATION_TOOL_CONTRACTS[5];
15
- const queryContract = APPLICATION_TOOL_CONTRACTS[6];
14
+ const commitContract = applicationToolContract("commit_reconstruction_coverage");
15
+ const queryContract = applicationToolContract("query_reconstruction_coverage");
16
16
  /** Register reconstruction coverage commit and query tools. */
17
17
  export const registerCoverageTools = (server, options) => {
18
18
  registerCommitTool(server, options);