rea-agents 3.0.0 → 3.1.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 (219) hide show
  1. package/README.md +14 -16
  2. package/dist/application/AnalysisContextQueries.js +21 -9
  3. package/dist/application/ApplicationWorkflowEvidenceResolver.js +19 -11
  4. package/dist/application/ArtifactInventory/policy.js +17 -0
  5. package/dist/application/ArtifactInventory/reader.js +1 -4
  6. package/dist/application/ArtifactInventory/scanCanonical.js +0 -3
  7. package/dist/application/ArtifactInventory/scanReader.js +7 -7
  8. package/dist/application/ArtifactInventory/types.js +1 -5
  9. package/dist/application/BinarySession.js +14 -16
  10. package/dist/application/BinarySessionRecords.js +31 -5
  11. package/dist/application/BinarySessionStatus.js +11 -5
  12. package/dist/application/BrowserEvidence.js +3 -5
  13. package/dist/application/CapabilityClientRequirements.js +26 -0
  14. package/dist/application/CapabilityInventory.js +134 -63
  15. package/dist/application/CrossVersionInventory.js +7 -6
  16. package/dist/application/ElectronActiveEvidence.js +55 -0
  17. package/dist/application/ElectronActiveObservationPort.js +1 -0
  18. package/dist/application/ElectronActiveObservationService.js +81 -0
  19. package/dist/application/EnhancedTools.js +22 -20
  20. package/dist/application/EvidenceBundleCommands.js +5 -1
  21. package/dist/application/EvidenceLedger.js +8 -1
  22. package/dist/application/JavaScriptReplayPermission.js +1 -1
  23. package/dist/application/JavaScriptReplayService.js +23 -15
  24. package/dist/application/ManagedRuntimeCorrelationService.js +9 -5
  25. package/dist/application/NodeRuntimeCharacterizationService.js +8 -9
  26. package/dist/application/ProcessCaptureLifecycle.js +5 -0
  27. package/dist/application/ProcessCli.js +2 -1
  28. package/dist/application/ProcessHarness.js +8 -3
  29. package/dist/application/ProcessNormalization.js +2 -1
  30. package/dist/application/ProjectPermissionStore.js +1 -0
  31. package/dist/application/ReferenceSourceImport.js +2 -2
  32. package/dist/application/SetupClientConfiguration.js +6 -1
  33. package/dist/application/WindowsCapabilities.js +5 -4
  34. package/dist/artifacts/ArtifactProvider.js +8 -10
  35. package/dist/artifacts/SafeOutputTree.js +7 -8
  36. package/dist/browser/CdpCaptureStorageFingerprints.js +10 -7
  37. package/dist/browser/CdpElectronInspection.js +3 -1
  38. package/dist/browser/JavaScriptRuntimeScope.js +5 -0
  39. package/dist/browser/PlaywrightElectronActiveActions.js +324 -0
  40. package/dist/browser/PlaywrightElectronActiveProvider.js +294 -0
  41. package/dist/browser/PlaywrightScenarioBrowser.js +4 -4
  42. package/dist/browser/PlaywrightScenarioSession.js +4 -4
  43. package/dist/browser/SensitiveTextCapture.js +1 -1
  44. package/dist/browser/V8InspectorCaptureProjection.js +1 -0
  45. package/dist/browser/V8InspectorProvider.js +3 -3
  46. package/dist/catalogIdentity.js +12 -6
  47. package/dist/cli/managedCommands.js +1 -1
  48. package/dist/cliApplicationCommands.js +2 -2
  49. package/dist/cliBrowserAdvancedCommands.js +11 -5
  50. package/dist/cliBrowserCommands.js +14 -7
  51. package/dist/cliCommandNames.js +1 -0
  52. package/dist/cliElectronCommands.js +46 -4
  53. package/dist/cliJavaScriptRuntimeCommands.js +14 -6
  54. package/dist/cliObservationOptions.js +4 -0
  55. package/dist/cliPolicyCommands.js +1 -0
  56. package/dist/config/browserScenario.js +77 -0
  57. package/dist/config/electronAutomation.js +35 -0
  58. package/dist/config/environment.js +6 -0
  59. package/dist/config/javascriptReplay.js +41 -0
  60. package/dist/config/managedRuntime.js +25 -0
  61. package/dist/config/parseConfig.js +56 -200
  62. package/dist/config/parsers.js +10 -1
  63. package/dist/config/passiveObservation.js +157 -0
  64. package/dist/config/processCapture.js +47 -0
  65. package/dist/contracts/applicationToolContracts.js +2 -5
  66. package/dist/contracts/applicationWorkflowInputContracts.js +86 -96
  67. package/dist/contracts/artifactToolContracts.js +27 -34
  68. package/dist/contracts/browserToolContracts.js +2 -2
  69. package/dist/contracts/electronToolContracts.js +30 -1
  70. package/dist/contracts/enhancedInputs.js +13 -29
  71. package/dist/contracts/functionWorkflowToolContracts.js +2 -3
  72. package/dist/contracts/managedToolContracts.js +4 -9
  73. package/dist/contracts/managedWorkflowExamples.js +12 -9
  74. package/dist/contracts/managedWorkflowToolContracts.js +30 -27
  75. package/dist/contracts/nativeToolContracts.js +2 -3
  76. package/dist/contracts/sessionToolSchemas.js +61 -0
  77. package/dist/contracts/toolContractExamples.js +1 -0
  78. package/dist/contracts/toolContractHelpers.js +23 -0
  79. package/dist/contracts/toolContracts.js +5 -77
  80. package/dist/contracts/toolEffects.js +15 -0
  81. package/dist/contracts/toolOutputSchemaGroups.js +17 -2
  82. package/dist/contracts/toolOutputSchemaPrimitives.js +76 -26
  83. package/dist/contracts/toolSchemaMetadata.js +128 -0
  84. package/dist/doctorRuntime.js +1 -1
  85. package/dist/domain/androidApplication.js +2 -0
  86. package/dist/domain/appleApplication.js +2 -0
  87. package/dist/domain/binaryTarget.js +7 -5
  88. package/dist/domain/binaryTargetTypes.js +1 -0
  89. package/dist/domain/browserCaptureComparison.js +21 -69
  90. package/dist/domain/browserObservation.js +22 -9
  91. package/dist/domain/bytecodeProvider.js +33 -35
  92. package/dist/domain/callPath.js +3 -2
  93. package/dist/domain/conformanceReplay.js +3 -3
  94. package/dist/domain/conformanceTrustGate.js +151 -16
  95. package/dist/domain/customProtocolCapture.js +3 -3
  96. package/dist/domain/electronActiveObservation.js +277 -0
  97. package/dist/domain/electronObservation.js +15 -11
  98. package/dist/domain/electronStaticAnalysisValues.js +4 -1
  99. package/dist/domain/eventProcessTree.js +82 -79
  100. package/dist/domain/functionComparisonNormalization.js +15 -8
  101. package/dist/domain/functionDossierEvidence.js +9 -4
  102. package/dist/domain/hopperValues.js +68 -71
  103. package/dist/domain/inputIssueProjection.js +4 -1
  104. package/dist/domain/investigationWorkspace.js +16 -15
  105. package/dist/domain/javascriptExportShapeComparisonSchemas.js +2 -2
  106. package/dist/domain/javascriptReplay.js +79 -63
  107. package/dist/domain/javascriptRuntimeObservation.js +1 -0
  108. package/dist/domain/javascriptRuntimeReconciliationActive.js +89 -0
  109. package/dist/domain/javascriptRuntimeReconciliationGraph.js +11 -10
  110. package/dist/domain/javascriptRuntimeReconciliationMatching.js +57 -49
  111. package/dist/domain/javascriptRuntimeReconciliationParsing.js +5 -1
  112. package/dist/domain/javascriptRuntimeReconciliationResult.js +21 -14
  113. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  114. package/dist/domain/knownPageCoverage.js +15 -7
  115. package/dist/domain/nativeApiBoundary.js +15 -7
  116. package/dist/domain/nodeRuntimeCharacterization.js +14 -13
  117. package/dist/domain/processCaptureReactiveValidation.js +8 -3
  118. package/dist/domain/processCaptureValidation.js +7 -3
  119. package/dist/domain/processComparison.js +12 -7
  120. package/dist/domain/processObservation.js +5 -6
  121. package/dist/domain/processReactiveMatching.js +14 -7
  122. package/dist/domain/processReactiveTransition.js +9 -3
  123. package/dist/domain/processScenario.js +6 -3
  124. package/dist/domain/processTraceComparison.js +3 -2
  125. package/dist/domain/protocolCapture.js +8 -6
  126. package/dist/domain/reconstructionReadinessSchemas.js +30 -2
  127. package/dist/domain/referenceSourceImportParsing.js +2 -2
  128. package/dist/domain/replayMachineRuntime.js +5 -3
  129. package/dist/domain/sourceToBundleComparison.js +2 -2
  130. package/dist/domain/sourceToBundleComparisonSchemas.js +8 -8
  131. package/dist/domain/sourceToBundleSignals.js +7 -1
  132. package/dist/domain/staticRuntimeCorrelation.js +7 -5
  133. package/dist/domain/webBundleAnalysis.js +14 -14
  134. package/dist/domain/webCaptureDiff.js +15 -12
  135. package/dist/dotnet/ManagedNativeBoundaryInspector.js +2 -3
  136. package/dist/generatedMcpToolCatalog.js +1 -1
  137. package/dist/generatedPackageMetadata.js +1 -1
  138. package/dist/ghidra/GhidraClient.js +1 -1
  139. package/dist/ghidra/GhidraClientStartup.js +1 -1
  140. package/dist/ghidra/GhidraFunctionValues.js +17 -15
  141. package/dist/ghidra/GhidraInventoryValues.js +57 -47
  142. package/dist/ghidra/GhidraProvider.js +18 -10
  143. package/dist/ghidra/GhidraProviderCapabilities.js +5 -1
  144. package/dist/ghidra/GhidraSocketConnection.js +1 -1
  145. package/dist/ghidra/protocol.js +19 -42
  146. package/dist/hopper/BridgeLauncher.js +3 -1
  147. package/dist/hopper/HopperAnalysisProfile.js +2 -4
  148. package/dist/hopper/HopperCleanup.js +5 -6
  149. package/dist/hopper/HopperClient.js +1 -1
  150. package/dist/hopper/HopperProvider.js +0 -14
  151. package/dist/hopper/LinuxPrivateDisplayProbe.js +16 -10
  152. package/dist/main/reload.js +1 -6
  153. package/dist/main/state.js +0 -14
  154. package/dist/main/transport.js +30 -37
  155. package/dist/main.js +17 -15
  156. package/dist/mcpDoctor.js +2 -24
  157. package/dist/process/PrivateRuntimeRoot.js +17 -0
  158. package/dist/process/ProcessOwnership.js +64 -128
  159. package/dist/process/ProcessOwnershipIdentity.js +13 -0
  160. package/dist/process/ProcessOwnershipObservation.js +88 -0
  161. package/dist/process/ProcessOwnershipProcessTree.js +22 -0
  162. package/dist/process/ProviderDeadline.js +8 -12
  163. package/dist/process/WindowsAuthority.js +32 -0
  164. package/dist/reference/ReferenceSourceReader.js +3 -3
  165. package/dist/reference/ReferenceSourceReaderEntries.js +2 -2
  166. package/dist/replay/JavaScriptReplayWorker.js +95 -11
  167. package/dist/replay/JavaScriptReplayWorkerTypes.js +1 -0
  168. package/dist/replay/LinuxJavaScriptReplayRunner.js +10 -15
  169. package/dist/replay/ReplayOutcome.js +26 -15
  170. package/dist/replay/ReplayWorkerProtocol.js +34 -29
  171. package/dist/server/createServer.js +158 -94
  172. package/dist/server/mcpClientMetadata.js +32 -0
  173. package/dist/server/registerApplicationTools/characterization.js +6 -13
  174. package/dist/server/registerApplicationTools/compareExportShapes.js +2 -7
  175. package/dist/server/registerApplicationTools/compareSourceToBundle.js +2 -7
  176. package/dist/server/registerApplicationTools/compareVersions.js +3 -8
  177. package/dist/server/registerApplicationTools/controlledReplay.js +6 -11
  178. package/dist/server/registerApplicationTools/coverage.js +13 -19
  179. package/dist/server/registerApplicationTools/obligationLedger.js +1 -6
  180. package/dist/server/registerApplicationTools/readiness.js +1 -6
  181. package/dist/server/registerApplicationTools/traceFeature.js +3 -8
  182. package/dist/server/registerApplicationTools/traceSemantics.js +1 -6
  183. package/dist/server/registerArtifactComparisonTool.js +12 -16
  184. package/dist/server/registerBrowserScenarioTool.js +1 -6
  185. package/dist/server/registerBrowserTools.js +37 -79
  186. package/dist/server/registerBundleComparisonTool.js +10 -14
  187. package/dist/server/registerCloseLifecycleTool.js +6 -14
  188. package/dist/server/registerElectronTools.js +30 -47
  189. package/dist/server/registerEnhancedTools.js +114 -122
  190. package/dist/server/registerEvidenceTools.js +5 -9
  191. package/dist/server/registerFunctionComparisonTool.js +11 -15
  192. package/dist/server/registerInvestigationTools.js +41 -57
  193. package/dist/server/registerJavaScriptRuntimeObservationTools.js +13 -27
  194. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +4 -9
  195. package/dist/server/registerManagedWorkflowTools/contract.js +2 -3
  196. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +3 -8
  197. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +3 -8
  198. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +8 -13
  199. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +4 -9
  200. package/dist/server/registerOfficialTools.js +8 -9
  201. package/dist/server/registerProcessComparisonTool.js +19 -24
  202. package/dist/server/registerReplayMachineTool.js +4 -10
  203. package/dist/server/registerSessionRecordTools.js +69 -40
  204. package/dist/server/registerSessionStatusTool.js +13 -28
  205. package/dist/server/registerSessionTools.js +36 -52
  206. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  207. package/dist/server/sessionToolPolicies.js +1 -5
  208. package/dist/server/toolRegistrationOptions.js +4 -192
  209. package/dist/server/toolResult.js +2 -18
  210. package/package.json +22 -14
  211. package/scripts/electron-active-hook-boundaries.cjs +394 -0
  212. package/scripts/electron-active-hook.cjs +501 -0
  213. package/scripts/rea.mjs +1 -11
  214. package/skills/reverse-engineer-anything/SKILL.md +2 -2
  215. package/dist/server/DynamicToolAvailability.js +0 -70
  216. package/dist/server/LazyToolCatalog.js +0 -134
  217. package/dist/server/hydrateServerTools.js +0 -191
  218. package/dist/server/toolInputValidation.js +0 -77
  219. package/scripts/package-runner-bootstrap.mjs +0 -72
@@ -20,14 +20,16 @@ const reconcileEntity = (entity, index, layers) => {
20
20
  const content = index.byCategoryDigest.get(categoryDigestKey(entity.staticKind, entity.sourceSha256)) ?? [];
21
21
  const locatedKeys = new Set(locations.map(({ candidate }) => candidateKey(candidate)));
22
22
  const combined = content.filter((candidate) => locatedKeys.has(candidateKey(candidate)));
23
- if (combined.length === 1)
24
- return matched(entity, combined[0], {
23
+ const combinedCandidate = combined.at(0);
24
+ if (combined.length === 1 && combinedCandidate !== undefined)
25
+ return matched(entity, combinedCandidate, {
25
26
  basis: "content-and-location",
26
27
  confidence: "high",
27
28
  reason: "content-and-location-match",
28
29
  });
29
- if (content.length === 1) {
30
- const candidate = content[0];
30
+ const contentCandidate = content.at(0);
31
+ if (content.length === 1 && contentCandidate !== undefined) {
32
+ const candidate = contentCandidate;
31
33
  const digestBasis = candidate.digests.find(({ sha256 }) => sha256 === entity.sourceSha256)?.basis;
32
34
  return matched(entity, candidate, {
33
35
  basis: digestBasis ?? "content-sha256",
@@ -59,8 +61,9 @@ const reconcileEntity = (entity, index, layers) => {
59
61
  candidates: [],
60
62
  });
61
63
  }
62
- if (locations.length === 1) {
63
- const located = locations[0];
64
+ const location = locations.at(0);
65
+ if (locations.length === 1 && location !== undefined) {
66
+ const located = location;
64
67
  return matched(entity, located.candidate, {
65
68
  basis: located.mapping.basis,
66
69
  confidence: "medium",
@@ -174,50 +177,55 @@ const createItem = (entity, input) => {
174
177
  ...semantic,
175
178
  };
176
179
  };
177
- const reconciliationEvidence = (entity, layer, basis, confidence) => ({
178
- authority: "cross-layer-reconciliation",
179
- state: "inferred",
180
- confidence: confidence === "medium" ? "medium" : "high",
181
- artifact: entity.sourceSha256 === null
182
- ? {
183
- available: false,
184
- reason: "not-observed",
185
- detail: "Runtime source bytes were not captured for this mapping.",
186
- }
187
- : {
188
- available: true,
189
- artifact_id: `art_${entity.sourceSha256}`,
190
- sha256: entity.sourceSha256,
191
- },
192
- location: entity.node.observations[0].evidence.location,
193
- extractor: {
194
- name: "rea-javascript-runtime-reconciliation",
195
- version: "1",
196
- operation: "reconcile_javascript_runtime",
197
- executable_sha256: null,
198
- },
199
- coverage: layer.graph.coverage.status === "complete" &&
200
- entity.capture.scriptsCompleteWithinScope
201
- ? { status: "complete", truncated: false, omitted_count: 0, limits: [] }
202
- : {
203
- status: "partial",
204
- truncated: false,
205
- omitted_count: null,
206
- limits: [],
180
+ const reconciliationEvidence = (entity, layer, basis, confidence) => {
181
+ const observation = entity.node.observations.at(0);
182
+ if (observation === undefined)
183
+ throw new TypeError("Runtime reconciliation entity has no observation");
184
+ return {
185
+ authority: "cross-layer-reconciliation",
186
+ state: "inferred",
187
+ confidence: confidence === "medium" ? "medium" : "high",
188
+ artifact: entity.sourceSha256 === null
189
+ ? {
190
+ available: false,
191
+ reason: "not-observed",
192
+ detail: "Runtime source bytes were not captured for this mapping.",
193
+ }
194
+ : {
195
+ available: true,
196
+ artifact_id: `art_${entity.sourceSha256}`,
197
+ sha256: entity.sourceSha256,
198
+ },
199
+ location: observation.evidence.location,
200
+ extractor: {
201
+ name: "rea-javascript-runtime-reconciliation",
202
+ version: "1",
203
+ operation: "reconcile_javascript_runtime",
204
+ executable_sha256: null,
207
205
  },
208
- limitations: [
209
- basis === "content-sha256" ||
210
- basis === "module-source-sha256" ||
211
- basis === "content-and-location"
212
- ? "Equal content digests establish byte identity, not execution causality."
213
- : "Location correspondence is inferred from an artifact root or caller-declared mapping.",
214
- "Passive script parsing or target presence does not prove feature execution.",
215
- ],
216
- evidence_ids: [
217
- layer.evidence.evidence_id,
218
- entity.capture.evidence.evidence_id,
219
- ].sort(),
220
- });
206
+ coverage: layer.graph.coverage.status === "complete" &&
207
+ entity.capture.scriptsCompleteWithinScope
208
+ ? { status: "complete", truncated: false, omitted_count: 0, limits: [] }
209
+ : {
210
+ status: "partial",
211
+ truncated: false,
212
+ omitted_count: null,
213
+ limits: [],
214
+ },
215
+ limitations: [
216
+ basis === "content-sha256" ||
217
+ basis === "module-source-sha256" ||
218
+ basis === "content-and-location"
219
+ ? "Equal content digests establish byte identity, not execution causality."
220
+ : "Location correspondence is inferred from an artifact root or caller-declared mapping.",
221
+ "Passive script parsing or target presence does not prove feature execution.",
222
+ ],
223
+ evidence_ids: [
224
+ layer.evidence.evidence_id,
225
+ entity.capture.evidence.evidence_id,
226
+ ].sort(),
227
+ };
228
+ };
221
229
  const strongestLocationBasis = (values) => {
222
230
  const bases = new Set(values.map(({ mapping }) => mapping.basis));
223
231
  if (bases.has("artifact-path"))
@@ -9,6 +9,7 @@ import { javascriptRuntimeObservationSchema, javascriptRuntimeKindSchema, } from
9
9
  import { parseEvidence } from "./evidence.js";
10
10
  import { javascriptApplicationAnalysisResultSchema, } from "./javascriptApplicationAnalysis.js";
11
11
  import { compareCodePoints, } from "./javascriptApplicationGraph.js";
12
+ import { parseActiveElectronCapture } from "./javascriptRuntimeReconciliationActive.js";
12
13
  /** Parse and semantically bind every static analysis Evidence layer. */
13
14
  export const parseStaticLayers = (layers) => layers
14
15
  .map((layer) => parseStaticLayer(layer))
@@ -133,7 +134,10 @@ const parseRuntimeCapture = (input) => {
133
134
  scriptsCompleteWithinScope: false,
134
135
  };
135
136
  }
136
- throw new TypeError("Runtime reconciliation requires inspect_web_page, inspect_electron_page, or observe_javascript_runtime Evidence");
137
+ if (evidence.operation === "capture_electron_scenario") {
138
+ return parseActiveElectronCapture(evidence);
139
+ }
140
+ throw new TypeError("Runtime reconciliation requires inspect_web_page, inspect_electron_page, observe_javascript_runtime, or capture_electron_scenario Evidence");
137
141
  };
138
142
  const assertV8RuntimeParameters = (evidence, result) => {
139
143
  const parameters = z
@@ -78,27 +78,34 @@ const sourceMapAuthority = (input) => ({
78
78
  limitation: "Source-map declarations and approved original-source reads retain separate static authority and are not used as primary runtime byte matches.",
79
79
  });
80
80
  const reconciliationCoverage = (input, completion) => ({
81
- status: completion.outputTruncated || completion.inputTruncated
81
+ status: completion.outputTruncated || completion.inputStatus === "truncated"
82
82
  ? "truncated"
83
- : completion.inputPartial
83
+ : completion.inputStatus === "partial"
84
84
  ? "partial"
85
85
  : "complete-within-inputs",
86
- truncated: completion.outputTruncated || completion.inputTruncated,
86
+ truncated: completion.outputTruncated || completion.inputStatus === "truncated",
87
87
  omitted_runtime_entities: input.runtime.omittedEntities,
88
88
  omitted_reconciliation_items: input.matching.omittedItems,
89
89
  omitted_static_load_states: input.loadStates.omittedStates,
90
90
  omitted_graph_items: input.omittedGraphItems,
91
91
  });
92
- const completionFlags = (input) => ({
93
- outputTruncated: input.runtime.omittedEntities > 0 ||
94
- input.matching.omittedItems > 0 ||
95
- input.loadStates.omittedStates > 0 ||
96
- input.omittedGraphItems > 0,
97
- inputTruncated: input.layers.some(({ graph }) => graph.coverage.truncated) ||
98
- input.captures.some(({ inspection }) => inspection.completeness.conditions.includes("truncated")),
99
- inputPartial: input.layers.some(({ graph }) => graph.coverage.status !== "complete") ||
100
- input.captures.some(({ inspection }) => inspection.completeness.status !== "complete_within_window"),
101
- });
92
+ const completionFlags = (input) => {
93
+ const inputTruncated = input.layers.some(({ graph }) => graph.coverage.truncated) ||
94
+ input.captures.some(({ inspection }) => inspection.completeness.conditions.includes("truncated"));
95
+ const inputPartial = input.layers.some(({ graph }) => graph.coverage.status !== "complete") ||
96
+ input.captures.some(({ inspection }) => inspection.completeness.status !== "complete_within_window");
97
+ return {
98
+ outputTruncated: input.runtime.omittedEntities > 0 ||
99
+ input.matching.omittedItems > 0 ||
100
+ input.loadStates.omittedStates > 0 ||
101
+ input.omittedGraphItems > 0,
102
+ inputStatus: inputTruncated
103
+ ? "truncated"
104
+ : inputPartial
105
+ ? "partial"
106
+ : "complete",
107
+ };
108
+ };
102
109
  const reconciliationLimitations = (completion) => uniqueSorted([
103
110
  "Only caller-supplied, semantically verified Evidence records were reconciled.",
104
111
  "Digest equality establishes byte identity but not code reachability, module initialization, or feature execution.",
@@ -106,7 +113,7 @@ const reconciliationLimitations = (completion) => uniqueSorted([
106
113
  "Not-observed means absent from the bounded captures in scope, never globally unloaded.",
107
114
  "Caller-declared file and URL mappings are inference inputs and do not expand CDP origin or filesystem-root authority.",
108
115
  "Source-map authority remains separate from passive runtime authority.",
109
- ...(completion.inputPartial
116
+ ...(completion.inputStatus !== "complete"
110
117
  ? ["One or more static graphs or passive captures were incomplete."]
111
118
  : []),
112
119
  ...(completion.outputTruncated
@@ -133,7 +133,7 @@ const layerSummarySchema = z.strictObject({
133
133
  const captureSummarySchema = z.strictObject({
134
134
  evidence_id: evidenceIdSchema,
135
135
  capture_sha256: digestSchema,
136
- kind: z.enum(["browser", "electron", "v8-inspector"]),
136
+ kind: z.enum(["browser", "electron", "v8-inspector", "electron-active"]),
137
137
  target_node_id: nodeIdSchema,
138
138
  target_key: boundedTextSchema,
139
139
  target_location: boundedTextSchema,
@@ -11,11 +11,19 @@ export const assessKnownPageCoverage = (page, includedCount = page.items.length)
11
11
  page.returned === page.total &&
12
12
  sourceOmittedCount === 0;
13
13
  const omittedCount = Math.max(0, page.total - retainedCount);
14
- return {
15
- complete: sourceComplete && omittedCount === 0,
16
- sourceComplete,
17
- includedCount: retainedCount,
18
- omittedCount,
19
- sourceOmittedCount,
20
- };
14
+ return sourceComplete && omittedCount === 0
15
+ ? {
16
+ complete: true,
17
+ sourceComplete: true,
18
+ includedCount: retainedCount,
19
+ omittedCount: 0,
20
+ sourceOmittedCount: 0,
21
+ }
22
+ : {
23
+ complete: false,
24
+ sourceComplete,
25
+ includedCount: retainedCount,
26
+ omittedCount,
27
+ sourceOmittedCount,
28
+ };
21
29
  };
@@ -13,9 +13,7 @@ const inferenceEvidenceSchema = z
13
13
  detail: z.string().min(1),
14
14
  })
15
15
  .strict();
16
- const inferredBoundaryTypeSchema = z
17
- .object({
18
- role: z.enum(["return", "parameter"]),
16
+ const inferredBoundaryTypeFacts = {
19
17
  ordinal: z.number().int().min(0).nullable(),
20
18
  name: z.string().min(1).nullable(),
21
19
  data_type: z.string().min(1),
@@ -28,8 +26,18 @@ const inferredBoundaryTypeSchema = z
28
26
  "register-or-stack-storage",
29
27
  "compiler-generated-variable",
30
28
  ])),
31
- })
32
- .strict();
29
+ };
30
+ const inferredReturnTypeSchema = z.strictObject({
31
+ ...inferredBoundaryTypeFacts,
32
+ role: z.literal("return"),
33
+ ordinal: z.null(),
34
+ name: z.null(),
35
+ });
36
+ const inferredParameterTypeSchema = z.strictObject({
37
+ ...inferredBoundaryTypeFacts,
38
+ role: z.literal("parameter"),
39
+ ordinal: z.number().int().min(0),
40
+ });
33
41
  const jumpTableDataSourceSchema = z
34
42
  .object({
35
43
  address: z.string().min(1),
@@ -68,8 +76,8 @@ const availableNativeApiBoundarySchema = z
68
76
  provenance: z.string().min(1),
69
77
  signature_source: z.string().min(1),
70
78
  calling_convention: z.string().min(1),
71
- return_type: inferredBoundaryTypeSchema,
72
- parameters: z.array(inferredBoundaryTypeSchema),
79
+ return_type: inferredReturnTypeSchema,
80
+ parameters: z.array(inferredParameterTypeSchema),
73
81
  parameters_truncated: z.boolean(),
74
82
  jump_tables: z.array(inferredJumpTableSchema),
75
83
  jump_tables_truncated: z.boolean(),
@@ -1,24 +1,25 @@
1
1
  import { z } from "zod";
2
- import { controlledReplayInputSchema, controlledReplayOutputSchema, } from "./javascriptReplay.js";
2
+ import { controlledReplayExecutionOutputSchema, controlledReplayPlanInputSchema, controlledReplayPlanOutputSchema, } from "./javascriptReplay.js";
3
3
  import { javascriptExportInstrumentationInputSchema, javascriptExportTransformationManifestSchema, } from "./javascriptExportInstrumentation.js";
4
4
  import { runtimeCharacterizationPlanSchema } from "./runtimeCharacterization.js";
5
5
  import { evidenceRecordSchema } from "./evidence.js";
6
6
  const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
7
+ const nodeCharacterizationExpectedEffectSchema = z.enum([
8
+ "pure",
9
+ "observation-only",
10
+ ]);
11
+ const nodeRuntimeCharacterizationPlanSchema = runtimeCharacterizationPlanSchema.extend({
12
+ expected_effect: nodeCharacterizationExpectedEffectSchema,
13
+ });
7
14
  export const nodeCharacterizationPreparationInputSchema = z
8
15
  .strictObject({
9
16
  preparation_approved: z.literal(true),
10
17
  selected_alias: z.string().min(1).max(200),
11
- expected_effect: z.enum(["pure", "observation-only"]),
18
+ expected_effect: nodeCharacterizationExpectedEffectSchema,
12
19
  instrumentation: javascriptExportInstrumentationInputSchema,
13
- replay: controlledReplayInputSchema,
20
+ replay: controlledReplayPlanInputSchema,
14
21
  })
15
22
  .superRefine((input, context) => {
16
- if (input.replay.mode !== "plan")
17
- context.addIssue({
18
- code: "custom",
19
- path: ["replay", "mode"],
20
- message: "Characterization preparation requires replay plan mode",
21
- });
22
23
  const selected = input.replay.left.modules.filter(({ alias }) => alias === input.selected_alias);
23
24
  if (selected.length !== 1 ||
24
25
  selected[0]?.path !== input.instrumentation.artifact_path ||
@@ -50,17 +51,17 @@ export const nodeCharacterizationExecutionInputSchema = z.strictObject({
50
51
  export const nodeCharacterizationPreparationOutputSchema = z.strictObject({
51
52
  schema_version: z.literal(1),
52
53
  phase: z.literal("preparation"),
53
- plan: runtimeCharacterizationPlanSchema,
54
+ plan: nodeRuntimeCharacterizationPlanSchema,
54
55
  transformation: javascriptExportTransformationManifestSchema,
55
56
  transformation_evidence: evidenceRecordSchema,
56
- replay: controlledReplayOutputSchema,
57
+ replay: controlledReplayPlanOutputSchema,
57
58
  });
58
59
  export const nodeCharacterizationExecutionOutputSchema = z.strictObject({
59
60
  schema_version: z.literal(1),
60
61
  phase: z.literal("execution"),
61
- plan: runtimeCharacterizationPlanSchema,
62
+ plan: nodeRuntimeCharacterizationPlanSchema,
62
63
  transformation: javascriptExportTransformationManifestSchema,
63
64
  transformation_evidence: evidenceRecordSchema,
64
65
  evidence: evidenceRecordSchema,
65
- replay: controlledReplayOutputSchema,
66
+ replay: controlledReplayExecutionOutputSchema,
66
67
  });
@@ -118,6 +118,8 @@ const replayTransitions = (scenario, run, observations, require) => {
118
118
  while ((run.controls[controlIndex]?.after_capture_order ??
119
119
  Number.MAX_SAFE_INTEGER) < observation.capture_order) {
120
120
  const control = run.controls[controlIndex];
121
+ if (control === undefined)
122
+ break;
121
123
  advanceControl(control, controlIndex);
122
124
  controlIndex += 1;
123
125
  }
@@ -125,6 +127,8 @@ const replayTransitions = (scenario, run, observations, require) => {
125
127
  }
126
128
  for (; controlIndex < run.controls.length; controlIndex += 1) {
127
129
  const control = run.controls[controlIndex];
130
+ if (control === undefined)
131
+ break;
128
132
  advanceControl(control, controlIndex);
129
133
  }
130
134
  require(transitionIndex ===
@@ -141,10 +145,11 @@ const validateControls = (run, observations, require) => {
141
145
  require(control.sequence ===
142
146
  index, `reactive_run.controls[${String(index)}].sequence`, "reactive control sequence must be contiguous");
143
147
  require(admittedOrders.has(control.after_capture_order), `reactive_run.controls[${String(index)}].after_capture_order`, "reactive control cutoff must identify the last admitted observation");
148
+ const previous = run.controls.at(index - 1);
144
149
  require(index === 0 ||
145
- control.after_capture_order >=
146
- run.controls[index - 1]
147
- .after_capture_order, `reactive_run.controls[${String(index)}].after_capture_order`, "reactive control cutoffs must be ordered");
150
+ (previous !== undefined &&
151
+ control.after_capture_order >=
152
+ previous.after_capture_order), `reactive_run.controls[${String(index)}].after_capture_order`, "reactive control cutoffs must be ordered");
148
153
  }
149
154
  };
150
155
  /** Validate one capture's reactive journal by replaying the production reducer. */
@@ -1,7 +1,11 @@
1
1
  import { validateProcessCaptureReactiveRun } from "./processCaptureReactiveValidation.js";
2
2
  import { replayMachineSchema } from "./replayMachine.js";
3
3
  import { digestProcessCommitment } from "./processScenario.js";
4
- const orderedTimestamps = (values) => values.every((value, index) => index === 0 || value.at_ms >= values[index - 1].at_ms);
4
+ const orderedTimestamps = (values) => values.every((value, index) => {
5
+ const previous = values[index - 1];
6
+ return previous === undefined || value.at_ms >= previous.at_ms;
7
+ });
8
+ const transitionsAreContiguous = (previous, current) => previous !== undefined && previous.state_after === current.state_before;
5
9
  const validateCommitments = (capture, require) => {
6
10
  const { manifest } = capture;
7
11
  require(manifest.scenario.executable_sha256 ===
@@ -192,9 +196,9 @@ const validateReplayEvents = (capture, require) => {
192
196
  if (index === 0)
193
197
  require(transition.state_before ===
194
198
  machine?.initial_state, "replay_transitions", "first replay transition does not start at the declared initial state");
199
+ const previous = capture.replay_transitions[index - 1];
195
200
  if (index > 0)
196
- require(capture.replay_transitions[index - 1].state_after ===
197
- transition.state_before, "replay_transitions", "replay transition states are not contiguous");
201
+ require(transitionsAreContiguous(previous, transition), "replay_transitions", "replay transition states are not contiguous");
198
202
  }
199
203
  };
200
204
  /** Recompute v4 commitments and cross-field invariants without side effects. */
@@ -1,3 +1,4 @@
1
+ import canonicalize from "canonicalize";
1
2
  import { z } from "zod";
2
3
  import { validateProcessCapture } from "./processCapture.js";
3
4
  import { jsonValueSchema } from "./jsonValue.js";
@@ -97,6 +98,10 @@ const filesystemObservations = (capture) => [
97
98
  effects: capture.filesystem_effects,
98
99
  },
99
100
  ];
101
+ const sameJsonValue = (left, right) => {
102
+ const encodedLeft = canonicalize(left);
103
+ return encodedLeft !== undefined && encodedLeft === canonicalize(right);
104
+ };
100
105
  /** Canonical observations for one process-comparison dimension. */
101
106
  export const processDimensionObservations = (capture, dimension) => {
102
107
  switch (dimension) {
@@ -117,7 +122,7 @@ export const processDimensionObservations = (capture, dimension) => {
117
122
  }
118
123
  };
119
124
  const classifyCollection = (left, right) => {
120
- if (JSON.stringify(left) === JSON.stringify(right))
125
+ if (sameJsonValue(left, right))
121
126
  return "unchanged";
122
127
  if (left.length === 0)
123
128
  return "added";
@@ -141,7 +146,7 @@ const firstCollectionDivergence = (dimension, left, right) => {
141
146
  for (let index = 0; index < length; index += 1) {
142
147
  const leftValue = left[index] ?? null;
143
148
  const rightValue = right[index] ?? null;
144
- if (JSON.stringify(leftValue) === JSON.stringify(rightValue))
149
+ if (sameJsonValue(leftValue, rightValue))
145
150
  continue;
146
151
  return {
147
152
  status: "found",
@@ -168,8 +173,9 @@ const assertComparable = (left, right, options) => {
168
173
  ...validateProcessCapture(left),
169
174
  ...validateProcessCapture(right),
170
175
  ];
171
- if (invalid.length > 0)
172
- throw new TypeError(`Invalid Process Capture v4: ${invalid[0].path}`);
176
+ const firstInvalid = invalid.at(0);
177
+ if (firstInvalid !== undefined)
178
+ throw new TypeError(`Invalid Process Capture v4: ${firstInvalid.path}`);
173
179
  if (left.schema_version !== right.schema_version ||
174
180
  left.manifest.comparison_contract_sha256 !==
175
181
  right.manifest.comparison_contract_sha256)
@@ -239,8 +245,7 @@ const compareDimensions = (left, right, sameNormalization) => {
239
245
  interaction: classify("interaction", left.interaction_events, right.interaction_events),
240
246
  exit: hasUnknown(left, "exit") || hasUnknown(right, "exit")
241
247
  ? "unknown"
242
- : JSON.stringify({ exit: left.exit, settlement: left.settlement }) ===
243
- JSON.stringify({ exit: right.exit, settlement: right.settlement })
248
+ : sameJsonValue({ exit: left.exit, settlement: left.settlement }, { exit: right.exit, settlement: right.settlement })
244
249
  ? "unchanged"
245
250
  : "changed",
246
251
  filesystem: classify("filesystem", filesystemObservations(left), filesystemObservations(right)),
@@ -266,7 +271,7 @@ export const compareProcessCaptures = (left, right, options = {}) => {
266
271
  trace: compareProcessTraces(left, right, options.traceSpecification),
267
272
  };
268
273
  }
269
- const sameNormalization = JSON.stringify(left.normalization) === JSON.stringify(right.normalization);
274
+ const sameNormalization = sameJsonValue(left.normalization, right.normalization);
270
275
  const exactDimensions = compareDimensions(left, right, sameNormalization);
271
276
  const trace = options.traceSpecification === undefined
272
277
  ? undefined
@@ -52,14 +52,13 @@ export const createProcessObservation = (input) => ({
52
52
  const atMilliseconds = (value) => {
53
53
  if (typeof value !== "object" || value === null)
54
54
  return null;
55
- for (const name of ["at_ms", "dispatched_at_ms"])
56
- if (name in value) {
57
- const candidate = value[name];
58
- if (typeof candidate === "number" && Number.isSafeInteger(candidate))
59
- return candidate;
60
- }
55
+ if ("at_ms" in value && isSafeMillisecond(value.at_ms))
56
+ return value.at_ms;
57
+ if ("dispatched_at_ms" in value && isSafeMillisecond(value.dispatched_at_ms))
58
+ return value.dispatched_at_ms;
61
59
  return null;
62
60
  };
61
+ const isSafeMillisecond = (value) => typeof value === "number" && Number.isSafeInteger(value);
63
62
  /** Derive the stable capture-local identity for one normalized observation. */
64
63
  export const processObservationSubject = (source, value) => {
65
64
  if (typeof value !== "object" || value === null || Array.isArray(value))
@@ -125,13 +125,20 @@ const matchTerminalText = (trigger, context) => {
125
125
  overflow: false,
126
126
  };
127
127
  };
128
- const mergeMatches = (matches) => ({
129
- matched: matches.every(({ matched }) => matched),
130
- eventIds: [...new Set(matches.flatMap(({ eventIds }) => eventIds))],
131
- consumeIds: [...new Set(matches.flatMap(({ consumeIds }) => consumeIds))],
132
- lastOrder: Math.max(-1, ...matches.map(({ lastOrder }) => lastOrder)),
133
- overflow: matches.some(({ overflow }) => overflow),
134
- });
128
+ const mergeMatches = (matches) => {
129
+ const base = {
130
+ eventIds: [...new Set(matches.flatMap(({ eventIds }) => eventIds))],
131
+ consumeIds: [...new Set(matches.flatMap(({ consumeIds }) => consumeIds))],
132
+ lastOrder: Math.max(-1, ...matches.map(({ lastOrder }) => lastOrder)),
133
+ };
134
+ return matches.every(({ matched }) => matched)
135
+ ? { ...base, matched: true, overflow: false }
136
+ : {
137
+ ...base,
138
+ matched: false,
139
+ overflow: matches.some(({ overflow }) => overflow),
140
+ };
141
+ };
135
142
  const matchEarliestPrefix = (trigger, context) => {
136
143
  for (const observation of context.snapshot.observations) {
137
144
  if (observation.capture_order <= context.afterOrder)
@@ -11,6 +11,8 @@ const incrementCounter = (counters, id) => {
11
11
  };
12
12
  /** Commit one uniquely selected transition after its declarative effects succeed. */
13
13
  const applyProcessReactiveTransition = (input) => {
14
+ if (input.effectResults.length !== input.transition.actions.length)
15
+ throw new TypeError("Reactive transition effects do not match the admitted actions");
14
16
  const outcome = input.transition.target.kind === "finish"
15
17
  ? input.transition.target.outcome
16
18
  : null;
@@ -31,13 +33,17 @@ const applyProcessReactiveTransition = (input) => {
31
33
  for (const eventId of input.consumeEventIds)
32
34
  consumed.add(eventId);
33
35
  const checkpoints = [...input.snapshot.checkpoints];
34
- for (const [index, action] of input.transition.actions.entries())
36
+ for (const [index, action] of input.transition.actions.entries()) {
37
+ const effect = input.effectResults[index];
38
+ if (effect === undefined)
39
+ throw new TypeError("Reactive transition effect is missing");
35
40
  if (action.type === "checkpoint")
36
41
  checkpoints.push({
37
42
  name: action.name,
38
- event_id: input.effectResults[index].observation.event_id,
39
- capture_order: input.effectResults[index].observation.capture_order,
43
+ event_id: effect.observation.event_id,
44
+ capture_order: effect.observation.capture_order,
40
45
  });
46
+ }
41
47
  const completedCaptureOrder = Math.max(input.observation.capture_order, ...input.effectResults.map(({ observation }) => observation.capture_order));
42
48
  let next = {
43
49
  ...input.snapshot,
@@ -283,7 +283,9 @@ export const processScenarioSchema = z
283
283
  .strict()
284
284
  .superRefine((scenario, context) => {
285
285
  for (let index = 1; index < scenario.events.length; index += 1) {
286
- if (scenario.events[index].at_ms < scenario.events[index - 1].at_ms) {
286
+ const event = scenario.events[index];
287
+ const previous = scenario.events[index - 1];
288
+ if (eventsOutOfOrder(event, previous)) {
287
289
  context.addIssue({
288
290
  code: "custom",
289
291
  message: "events must be ordered by at_ms",
@@ -361,15 +363,16 @@ export const processScenarioSchema = z
361
363
  path: ["replay"],
362
364
  });
363
365
  });
366
+ const eventsOutOfOrder = (event, previous) => event !== undefined && previous !== undefined && event.at_ms < previous.at_ms;
364
367
  /** Parse untrusted process scenario input and apply safe default budgets. */
365
368
  export const parseProcessScenario = (input) => processScenarioSchema.parse(input);
366
369
  const isWithin = (candidate, root) => resolve(candidate) === resolve(root) ||
367
370
  resolve(candidate).startsWith(`${resolve(root)}${resolve(root).endsWith("/") ? "" : "/"}`);
368
371
  /** Evaluate scenario authority before any process or filesystem side effect occurs. */
369
372
  export const authorizeProcessScenario = (scenario, policy) => {
370
- if (!policy.enabled)
373
+ if (policy.status === "disabled")
371
374
  return { allowed: false, reason: "process capture is disabled" };
372
- if (scenario.network_access === "host" && !policy.allowExternalNetwork)
375
+ if (scenario.network_access === "host" && policy.networkAccess === "none")
373
376
  return {
374
377
  allowed: false,
375
378
  reason: "host network access is not approved by operator policy",
@@ -9,8 +9,9 @@ export const compareProcessTraces = (left, right, specificationInput) => {
9
9
  ...validateProcessCapture(left),
10
10
  ...validateProcessCapture(right),
11
11
  ];
12
- if (captureIssues.length > 0)
13
- throw new TypeError(`Invalid Process Capture v4: ${captureIssues[0].path}`);
12
+ const firstCaptureIssue = captureIssues.at(0);
13
+ if (firstCaptureIssue !== undefined)
14
+ throw new TypeError(`Invalid Process Capture v4: ${firstCaptureIssue.path}`);
14
15
  const specification = processTraceSpecificationSchema.parse(specificationInput);
15
16
  const leftEvaluation = evaluateProcessTraceSide(left, specification);
16
17
  const rightEvaluation = evaluateProcessTraceSide(right, specification);
@@ -109,7 +109,7 @@ export function looksLikeCredential(path) {
109
109
  /** Decode a JSON-RPC message from raw payload bytes. */
110
110
  export function decodeJsonRpc(rawPayload) {
111
111
  const text = new TextDecoder().decode(rawPayload);
112
- const parsed = JSON.parse(text);
112
+ const parsed = jsonValueSchema.parse(JSON.parse(text));
113
113
  const fields = [];
114
114
  if (typeof parsed === "object" && parsed !== null) {
115
115
  for (const [key, value] of Object.entries(parsed)) {
@@ -125,7 +125,7 @@ export function decodeJsonRpc(rawPayload) {
125
125
  fields.push({
126
126
  path: key,
127
127
  wire_type: "json",
128
- value: value,
128
+ value,
129
129
  inferred: false,
130
130
  });
131
131
  }
@@ -141,6 +141,8 @@ export function decodeMessagePack(rawPayload) {
141
141
  return { decoded_fields: [], truncated: false };
142
142
  }
143
143
  const tag = rawPayload[0];
144
+ if (tag === undefined)
145
+ return { decoded_fields: [], truncated: false };
144
146
  // Map type: 0x80-0x8f (fixmap), 0xde (map16), 0xdf (map32)
145
147
  if ((tag >= 0x80 && tag <= 0x8f) || tag === 0xde || tag === 0xdf) {
146
148
  fields.push({
@@ -189,10 +191,10 @@ export function decodeGrpcFrame(rawPayload) {
189
191
  };
190
192
  }
191
193
  const compressed = rawPayload[0] !== 0;
192
- const length = (rawPayload[1] << 24) |
193
- (rawPayload[2] << 16) |
194
- (rawPayload[3] << 8) |
195
- rawPayload[4];
194
+ const length = ((rawPayload[1] ?? 0) << 24) |
195
+ ((rawPayload[2] ?? 0) << 16) |
196
+ ((rawPayload[3] ?? 0) << 8) |
197
+ (rawPayload[4] ?? 0);
196
198
  return {
197
199
  decoded_fields: [
198
200
  {