rea-agents 2.3.0 → 2.5.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.
- package/README.md +80 -18
- package/bridge/ghidra/ReaGhidraBridge.java +48 -1
- package/bridge/hopper_bridge.py +259 -24
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/ArtifactExtraction.js +5 -1
- package/dist/application/ArtifactGraphConstruction.js +3 -15
- package/dist/application/ArtifactInventory/classify.js +5 -1
- package/dist/application/ArtifactInventory/reader.js +2 -0
- package/dist/application/BinarySession.js +15 -4
- package/dist/application/BinarySessionStatus.js +32 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +79 -15
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +7 -2
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +35 -121
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/ProcessCaptureEnvironment.js +26 -0
- package/dist/application/ProcessCaptureLifecycle.js +1 -0
- package/dist/application/ProcessCheckpoints.js +5 -0
- package/dist/application/ProcessHarness.js +45 -34
- package/dist/application/ProcessNormalization.js +6 -0
- package/dist/application/ProcessReactiveCoordinator.js +10 -3
- package/dist/application/ProcessReactiveEffects.js +15 -1
- package/dist/application/ProcessReactiveHarness.js +100 -0
- package/dist/application/ProcessReactiveObservations.js +101 -16
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +8 -0
- package/dist/cli/artifactCommands.js +39 -0
- package/dist/cli/coreAnalysisCommands.js +37 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +8 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +22 -0
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +25 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +5 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +13 -21
- package/dist/contracts/toolEffects.js +21 -0
- package/dist/contracts/toolOutputSchemaGroups.js +70 -20
- package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
- package/dist/domain/analysisErrorProjection.js +8 -0
- package/dist/domain/analysisSnapshot.js +2 -0
- package/dist/domain/artifactGraph.js +2 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/binaryTarget.js +3 -6
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -0
- package/dist/domain/evidence.js +2 -0
- package/dist/domain/hopperValues.js +10 -0
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +5 -4
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptStaticAnalysis.js +4 -3
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- package/dist/domain/processCapture.js +71 -1
- package/dist/domain/processCaptureReactiveValidation.js +175 -0
- package/dist/domain/processCaptureValidation.js +2 -0
- package/dist/domain/processObservation.js +15 -6
- package/dist/domain/processReactiveRuntime.js +21 -10
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +17 -10
- package/dist/domain/processScenario.js +21 -0
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- package/dist/domain/zipPackageFormat.js +20 -0
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +13 -1
- package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +69 -99
- package/dist/hopper/HopperProvider.js +20 -1
- package/dist/hopper/HopperRequestQueue.js +187 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +1 -0
- package/dist/main/transport.js +24 -24
- package/dist/main.js +6 -0
- package/dist/mcpDoctor.js +25 -2
- package/dist/native/NativeMacOSProvider.js +1 -0
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/createServer.js +46 -1
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +16 -5
- package/dist/server/registerEvidenceResources.js +4 -0
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
- package/dist/server/registerReconstructionReadinessResource.js +39 -0
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +11 -57
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/package.json +12 -6
- package/skills/reverse-engineer-anything/SKILL.md +4 -3
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +2 -2
|
@@ -3,7 +3,8 @@ import canonicalize from "canonicalize";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { webPageInspectionSchema, } from "./browserObservation.js";
|
|
5
5
|
import { webMcpDiscoverySchema, } from "./webMcpDiscovery.js";
|
|
6
|
-
|
|
6
|
+
/** One normalized passive page snapshot accepted by capture comparison. */
|
|
7
|
+
export const captureSnapshotSchema = z.object({
|
|
7
8
|
inspection: webPageInspectionSchema,
|
|
8
9
|
webmcp: webMcpDiscoverySchema.nullable().default(null),
|
|
9
10
|
});
|
|
@@ -24,6 +25,13 @@ const dimensionSchema = z.object({
|
|
|
24
25
|
omitted_changes: z.number().int().min(0),
|
|
25
26
|
reason: z.string().nullable(),
|
|
26
27
|
});
|
|
28
|
+
const legacyUnknownDimension = {
|
|
29
|
+
status: "unknown",
|
|
30
|
+
total_changes: 0,
|
|
31
|
+
changes: [],
|
|
32
|
+
omitted_changes: 0,
|
|
33
|
+
reason: "Dimension was not recorded by this version 1 capture diff.",
|
|
34
|
+
};
|
|
27
35
|
/** Completeness-aware changes across stable browser evidence dimensions. */
|
|
28
36
|
export const webCaptureDiffSchema = z.object({
|
|
29
37
|
schema_version: z.literal(1),
|
|
@@ -37,39 +45,17 @@ export const webCaptureDiffSchema = z.object({
|
|
|
37
45
|
network: dimensionSchema,
|
|
38
46
|
metadata: dimensionSchema,
|
|
39
47
|
webmcp: dimensionSchema,
|
|
48
|
+
accessibility: dimensionSchema.default(legacyUnknownDimension),
|
|
49
|
+
storage: dimensionSchema.default(legacyUnknownDimension),
|
|
40
50
|
}),
|
|
41
51
|
limitations: z.array(z.string()),
|
|
42
52
|
});
|
|
43
53
|
/** Compare normalized observations without treating incomplete absence as proof. */
|
|
44
54
|
export const compareWebCaptures = (input) => {
|
|
45
|
-
|
|
46
|
-
const dimension = (before, after, complete, reason) => {
|
|
47
|
-
const all = compareIdentities(before, after);
|
|
48
|
-
const retained = all.slice(0, remaining);
|
|
49
|
-
remaining -= retained.length;
|
|
50
|
-
return {
|
|
51
|
-
status: all.length > 0 ? "changed" : complete ? "unchanged" : "unknown",
|
|
52
|
-
total_changes: all.length,
|
|
53
|
-
changes: retained,
|
|
54
|
-
omitted_changes: all.length - retained.length,
|
|
55
|
-
reason: all.length === 0 && !complete ? reason : null,
|
|
56
|
-
};
|
|
57
|
-
};
|
|
55
|
+
const remaining = { value: input.max_changes };
|
|
58
56
|
const before = input.before.inspection;
|
|
59
57
|
const after = input.after.inspection;
|
|
60
|
-
const dimensions =
|
|
61
|
-
dom_structure: dimension(singleton("document", digest(domProjection(before))), singleton("document", digest(domProjection(after))), sectionsComplete(before, ["frames", "dom"]) &&
|
|
62
|
-
sectionsComplete(after, ["frames", "dom"]), "DOM or frame capture was incomplete in at least one observation."),
|
|
63
|
-
scripts: dimension(keyed(before.scripts.items.map(scriptProjection), (item) => item.script_key), keyed(after.scripts.items.map(scriptProjection), (item) => item.script_key), sectionsComplete(before, ["scripts"]) &&
|
|
64
|
-
sectionsComplete(after, ["scripts"]), "Script inventory was incomplete in at least one observation."),
|
|
65
|
-
resources: dimension(keyed(before.resources, (item) => item.resource_key), keyed(after.resources, (item) => item.resource_key), sectionsComplete(before, ["resources"]) &&
|
|
66
|
-
sectionsComplete(after, ["resources"]), "Resource inventory was incomplete in at least one observation."),
|
|
67
|
-
network: dimension(networkMap(before), networkMap(after), sectionsComplete(before, ["network_requests"]) &&
|
|
68
|
-
sectionsComplete(after, ["network_requests"]), "Network capture is attach-window limited or incomplete."),
|
|
69
|
-
metadata: dimension(singleton("metadata", digest(metadataProjection(before))), singleton("metadata", digest(metadataProjection(after))), sectionsComplete(before, ["metadata"]) &&
|
|
70
|
-
sectionsComplete(after, ["metadata"]), "Safe metadata capture was incomplete in at least one observation."),
|
|
71
|
-
webmcp: dimension(webMcpMap(input.before.webmcp), webMcpMap(input.after.webmcp), webMcpComplete(input.before.webmcp) && webMcpComplete(input.after.webmcp), "WebMCP discovery was unavailable or incomplete in at least one capture."),
|
|
72
|
-
};
|
|
58
|
+
const dimensions = compareWebCaptureDimensions(input, remaining);
|
|
73
59
|
const statuses = Object.values(dimensions).map(({ status }) => status);
|
|
74
60
|
return webCaptureDiffSchema.parse({
|
|
75
61
|
schema_version: 1,
|
|
@@ -90,6 +76,8 @@ export const compareWebCaptures = (input) => {
|
|
|
90
76
|
limitations: [
|
|
91
77
|
"A changed status proves an observed difference; an unknown status means absence could not be established from capture completeness.",
|
|
92
78
|
"Network comparison covers only activity observed after each CDP attachment.",
|
|
79
|
+
"Accessibility roles, ignored state, text, and hierarchy are compared only when the accessibility tree was fully captured and text capture was approved and not truncated.",
|
|
80
|
+
"Storage key inventories are compared only when approved and complete; usage and quota are compared only when reported. Redacted content is compared through complete SHA-256 fingerprints.",
|
|
93
81
|
],
|
|
94
82
|
});
|
|
95
83
|
};
|
|
@@ -108,6 +96,54 @@ const compareIdentities = (before, after) => {
|
|
|
108
96
|
return changes.sort((left, right) => left.identity.localeCompare(right.identity) ||
|
|
109
97
|
left.change.localeCompare(right.change));
|
|
110
98
|
};
|
|
99
|
+
const compareDimension = (remaining) => (before, after, complete, reason) => {
|
|
100
|
+
const all = compareIdentities(before, after);
|
|
101
|
+
const retained = all.slice(0, remaining.value);
|
|
102
|
+
remaining.value -= retained.length;
|
|
103
|
+
return {
|
|
104
|
+
status: all.length > 0 ? "changed" : complete ? "unchanged" : "unknown",
|
|
105
|
+
total_changes: all.length,
|
|
106
|
+
changes: retained,
|
|
107
|
+
omitted_changes: all.length - retained.length,
|
|
108
|
+
reason: all.length === 0 && !complete ? reason : null,
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const accessibilityDimension = (before, after, remaining) => {
|
|
112
|
+
const beforeAccess = accessibilityComparable(before);
|
|
113
|
+
const afterAccess = accessibilityComparable(after);
|
|
114
|
+
const textComparable = beforeAccess.text && afterAccess.text;
|
|
115
|
+
const nodesComparable = beforeAccess.nodes && afterAccess.nodes;
|
|
116
|
+
return compareDimension(remaining)(singleton("accessibility_tree", digest(accessibilityProjection(before.accessibility, textComparable, nodesComparable))), singleton("accessibility_tree", digest(accessibilityProjection(after.accessibility, textComparable, nodesComparable))), beforeAccess.complete && afterAccess.complete, "Accessibility tree or text capture was incomplete in at least one observation.");
|
|
117
|
+
};
|
|
118
|
+
const storageDimension = (before, after, remaining) => {
|
|
119
|
+
const keysComplete = storageKeysComparable(before) && storageKeysComparable(after);
|
|
120
|
+
const usageComplete = before.storage.usage_bytes !== null &&
|
|
121
|
+
after.storage.usage_bytes !== null &&
|
|
122
|
+
before.storage.quota_bytes !== null &&
|
|
123
|
+
after.storage.quota_bytes !== null;
|
|
124
|
+
const comparableFingerprints = storageFingerprintIdentities(before.storage, after.storage);
|
|
125
|
+
return compareDimension(remaining)(storageMap(before.storage, keysComplete, usageComplete, comparableFingerprints), storageMap(after.storage, keysComplete, usageComplete, comparableFingerprints), storageComparable(before, after, keysComplete, usageComplete), "Storage fingerprints, inventories, usage, or quota were unavailable or truncated in at least one observation.");
|
|
126
|
+
};
|
|
127
|
+
const compareWebCaptureDimensions = (input, remaining) => {
|
|
128
|
+
const before = input.before.inspection;
|
|
129
|
+
const after = input.after.inspection;
|
|
130
|
+
const dimension = compareDimension(remaining);
|
|
131
|
+
return {
|
|
132
|
+
dom_structure: dimension(singleton("document", digest(domProjection(before))), singleton("document", digest(domProjection(after))), sectionsComplete(before, ["frames", "dom"]) &&
|
|
133
|
+
sectionsComplete(after, ["frames", "dom"]), "DOM or frame capture was incomplete in at least one observation."),
|
|
134
|
+
scripts: dimension(keyed(before.scripts.items.map(scriptProjection), (item) => item.script_key), keyed(after.scripts.items.map(scriptProjection), (item) => item.script_key), sectionsComplete(before, ["scripts"]) &&
|
|
135
|
+
sectionsComplete(after, ["scripts"]), "Script inventory was incomplete in at least one observation."),
|
|
136
|
+
resources: dimension(keyed(before.resources, (item) => item.resource_key), keyed(after.resources, (item) => item.resource_key), sectionsComplete(before, ["resources"]) &&
|
|
137
|
+
sectionsComplete(after, ["resources"]), "Resource inventory was incomplete in at least one observation."),
|
|
138
|
+
network: dimension(networkMap(before), networkMap(after), sectionsComplete(before, ["network_requests"]) &&
|
|
139
|
+
sectionsComplete(after, ["network_requests"]), "Network capture is attach-window limited or incomplete."),
|
|
140
|
+
metadata: dimension(singleton("metadata", digest(metadataProjection(before))), singleton("metadata", digest(metadataProjection(after))), sectionsComplete(before, ["metadata"]) &&
|
|
141
|
+
sectionsComplete(after, ["metadata"]), "Safe metadata capture was incomplete in at least one observation."),
|
|
142
|
+
webmcp: dimension(webMcpMap(input.before.webmcp), webMcpMap(input.after.webmcp), webMcpComplete(input.before.webmcp) && webMcpComplete(input.after.webmcp), "WebMCP discovery was unavailable or incomplete in at least one capture."),
|
|
143
|
+
accessibility: accessibilityDimension(before, after, remaining),
|
|
144
|
+
storage: storageDimension(before, after, remaining),
|
|
145
|
+
};
|
|
146
|
+
};
|
|
111
147
|
const keyed = (values, identity) => new Map(values.map((value) => [identity(value), digest(value)]));
|
|
112
148
|
const networkMap = (inspection) => {
|
|
113
149
|
const grouped = new Map();
|
|
@@ -182,3 +218,81 @@ const digest = (value) => {
|
|
|
182
218
|
throw new TypeError("Expected canonical JSON");
|
|
183
219
|
return createHash("sha256").update(encoded).digest("hex");
|
|
184
220
|
};
|
|
221
|
+
const accessibilityComparable = (inspection) => {
|
|
222
|
+
const text = inspection.accessibility.text_capture.status === "included" &&
|
|
223
|
+
inspection.accessibility.text_capture.excluded_fields === 0 &&
|
|
224
|
+
inspection.accessibility.text_capture.truncated_fields === 0;
|
|
225
|
+
const nodes = inspection.accessibility.total_nodes ===
|
|
226
|
+
inspection.accessibility.nodes.length;
|
|
227
|
+
const complete = text && nodes && sectionsComplete(inspection, ["accessibility"]);
|
|
228
|
+
return { text, nodes, complete };
|
|
229
|
+
};
|
|
230
|
+
const accessibilityProjection = (accessibility, includeText, includeNodes) => {
|
|
231
|
+
const result = {
|
|
232
|
+
total_nodes: accessibility.total_nodes,
|
|
233
|
+
};
|
|
234
|
+
if (!includeNodes)
|
|
235
|
+
return result;
|
|
236
|
+
const nodeIndexes = new Map(accessibility.nodes.map((node, index) => [node.node_id, index]));
|
|
237
|
+
result.nodes = accessibility.nodes.map((node) => ({
|
|
238
|
+
parent_index: node.parent_id === null ? null : (nodeIndexes.get(node.parent_id) ?? -1),
|
|
239
|
+
role: node.role,
|
|
240
|
+
ignored: node.ignored,
|
|
241
|
+
states: node.states,
|
|
242
|
+
...(includeText ? { name: node.name, description: node.description } : {}),
|
|
243
|
+
}));
|
|
244
|
+
return result;
|
|
245
|
+
};
|
|
246
|
+
const storageKeysComparable = (inspection) => sectionsComplete(inspection, ["storage_keys"]);
|
|
247
|
+
const storageComparable = (before, after, keysComplete, usageComplete) => keysComplete &&
|
|
248
|
+
usageComplete &&
|
|
249
|
+
before.storage.fingerprints_complete &&
|
|
250
|
+
after.storage.fingerprints_complete;
|
|
251
|
+
const storageMap = (storage, includeKeys, includeUsage, fingerprintIdentities) => {
|
|
252
|
+
const map = new Map([
|
|
253
|
+
[
|
|
254
|
+
"storage:summary",
|
|
255
|
+
digest({
|
|
256
|
+
origin: storage.origin,
|
|
257
|
+
values_redacted: storage.values_redacted,
|
|
258
|
+
...(includeUsage
|
|
259
|
+
? {
|
|
260
|
+
usage_bytes: storage.usage_bytes,
|
|
261
|
+
quota_bytes: storage.quota_bytes,
|
|
262
|
+
}
|
|
263
|
+
: {}),
|
|
264
|
+
}),
|
|
265
|
+
],
|
|
266
|
+
]);
|
|
267
|
+
if (!includeKeys)
|
|
268
|
+
return map;
|
|
269
|
+
const add = (kind, keys) => {
|
|
270
|
+
for (const key of keys) {
|
|
271
|
+
map.set(`storage:${kind}:${key}`, digest(key));
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
add("local_storage", storage.local_storage_keys);
|
|
275
|
+
add("session_storage", storage.session_storage_keys);
|
|
276
|
+
add("indexed_db", storage.indexed_db_names);
|
|
277
|
+
add("cache", storage.cache_names);
|
|
278
|
+
for (const fingerprint of storage.content_fingerprints) {
|
|
279
|
+
const identity = `${fingerprint.scope}:${fingerprint.identity_sha256}`;
|
|
280
|
+
if (!fingerprintIdentities.has(identity))
|
|
281
|
+
continue;
|
|
282
|
+
map.set(`storage:content:${identity}`, digest(fingerprint.value_sha256));
|
|
283
|
+
}
|
|
284
|
+
return map;
|
|
285
|
+
};
|
|
286
|
+
const storageFingerprintIdentities = (before, after) => {
|
|
287
|
+
const beforeComplete = completeStorageFingerprints(before);
|
|
288
|
+
const afterComplete = completeStorageFingerprints(after);
|
|
289
|
+
if (before.fingerprints_complete && after.fingerprints_complete)
|
|
290
|
+
return new Set([...beforeComplete.keys(), ...afterComplete.keys()]);
|
|
291
|
+
return new Set([...beforeComplete.keys()].filter((identity) => afterComplete.has(identity)));
|
|
292
|
+
};
|
|
293
|
+
const completeStorageFingerprints = (storage) => new Map(storage.content_fingerprints
|
|
294
|
+
.filter(({ complete }) => complete)
|
|
295
|
+
.map(({ scope, identity_sha256, value_sha256 }) => [
|
|
296
|
+
`${scope}:${identity_sha256}`,
|
|
297
|
+
value_sha256,
|
|
298
|
+
]));
|
|
@@ -4,7 +4,7 @@ import { browserCompletenessSchema } from "./browserCompleteness.js";
|
|
|
4
4
|
import { browserAllowedOriginsSchema, browserEndpointSchema, } from "./browserObservation.js";
|
|
5
5
|
const MAX_SCREENSHOT_BYTES = 8 * 1_024 * 1_024;
|
|
6
6
|
/** Self-verifying inline PNG artifact for CLI/MCP parity. */
|
|
7
|
-
const webScreenshotArtifactSchema = z
|
|
7
|
+
export const webScreenshotArtifactSchema = z
|
|
8
8
|
.object({
|
|
9
9
|
uri: z.string().regex(/^rea:\/\/web-screenshot\/sha256\/[a-f0-9]{64}$/u),
|
|
10
10
|
sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classify a ZIP-backed package by its complete lower-cased path suffix.
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately extension-only. Callers must separately verify ZIP
|
|
5
|
+
* magic before trusting the classification for a root input.
|
|
6
|
+
*/
|
|
7
|
+
export const zipPackageFormatForPath = (path) => {
|
|
8
|
+
const lower = path.toLowerCase();
|
|
9
|
+
if (lower.endsWith(".zip"))
|
|
10
|
+
return "zip";
|
|
11
|
+
if (lower.endsWith(".ipa"))
|
|
12
|
+
return "ipa";
|
|
13
|
+
if (lower.endsWith(".apk") || lower.endsWith(".aab"))
|
|
14
|
+
return "apk";
|
|
15
|
+
if (lower.endsWith(".msix") || lower.endsWith(".msixbundle"))
|
|
16
|
+
return "msix";
|
|
17
|
+
if (lower.endsWith(".appx") || lower.endsWith(".appxbundle"))
|
|
18
|
+
return "appx";
|
|
19
|
+
return undefined;
|
|
20
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Generated from package.json and package-lock.json; do not edit. */
|
|
2
2
|
export const PACKAGE_METADATA = {
|
|
3
3
|
name: "rea-agents",
|
|
4
|
-
version: "2.
|
|
4
|
+
version: "2.5.0", // x-release-please-version
|
|
5
5
|
serverSdkVersion: "2.0.0-beta.4",
|
|
6
6
|
clientSdkVersion: "2.0.0-beta.4",
|
|
7
7
|
coreSdkVersion: "2.0.0-beta.4",
|
|
@@ -17,4 +17,4 @@ export const GHIDRA_MAX_QUEUED_REQUESTS = 32;
|
|
|
17
17
|
/** Maximum encoded response line accepted from the Java bridge. */
|
|
18
18
|
export const GHIDRA_MAX_LINE_BYTES = 1024 * 1024;
|
|
19
19
|
/** Version of REA's Java bridge handshake and request protocol. */
|
|
20
|
-
export const GHIDRA_BRIDGE_VERSION =
|
|
20
|
+
export const GHIDRA_BRIDGE_VERSION = 5;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AnalysisInputError, AnalysisOutputError } from "../domain/errors.js";
|
|
3
|
-
import { functionDossierSchema, } from "../domain/hopperValues.js";
|
|
3
|
+
import { functionInstructionWindowSchema, functionDossierSchema, } from "../domain/hopperValues.js";
|
|
4
4
|
import { jsonObjectSchema, jsonValueSchema, } from "../domain/jsonValue.js";
|
|
5
5
|
import { err, ok } from "../domain/result.js";
|
|
6
6
|
import { ghidraBoundedIdentifierSchema, ghidraCanonicalAddressSchema, } from "./GhidraInventoryValues.js";
|
|
@@ -12,6 +12,7 @@ export const GHIDRA_FUNCTION_OPERATIONS = [
|
|
|
12
12
|
"procedure_callers",
|
|
13
13
|
"procedure_info",
|
|
14
14
|
"procedure_pseudo_code",
|
|
15
|
+
"read_function_instructions",
|
|
15
16
|
"procedure_references",
|
|
16
17
|
"xrefs",
|
|
17
18
|
];
|
|
@@ -66,6 +67,13 @@ const inputSchemas = {
|
|
|
66
67
|
procedure_callers: z.object(directProcedure).strict(),
|
|
67
68
|
procedure_info: z.object(directProcedure).strict(),
|
|
68
69
|
procedure_pseudo_code: z.object(directProcedure).strict(),
|
|
70
|
+
read_function_instructions: z
|
|
71
|
+
.object({
|
|
72
|
+
...directProcedure,
|
|
73
|
+
offset: z.number().int().min(0).max(100_000).default(0),
|
|
74
|
+
limit: z.number().int().min(1).max(500).default(64),
|
|
75
|
+
})
|
|
76
|
+
.strict(),
|
|
69
77
|
procedure_references: z
|
|
70
78
|
.object({
|
|
71
79
|
...directProcedure,
|
|
@@ -235,6 +243,9 @@ const ghidraFunctionDossier = functionDossierSchema.superRefine((value, context)
|
|
|
235
243
|
message: "Ghidra dossier omitted required uncertainty limitations",
|
|
236
244
|
});
|
|
237
245
|
});
|
|
246
|
+
const ghidraFunctionInstructionWindow = functionInstructionWindowSchema
|
|
247
|
+
.extend({ procedure: procedureIdentity })
|
|
248
|
+
.strict();
|
|
238
249
|
const resultSchemas = {
|
|
239
250
|
analyze_function: ghidraFunctionDossier,
|
|
240
251
|
procedure_assembly: z.string(),
|
|
@@ -242,6 +253,7 @@ const resultSchemas = {
|
|
|
242
253
|
procedure_callers: z.array(ghidraCanonicalAddressSchema),
|
|
243
254
|
procedure_info: procedureInfo,
|
|
244
255
|
procedure_pseudo_code: z.string().nullable(),
|
|
256
|
+
read_function_instructions: ghidraFunctionInstructionWindow,
|
|
245
257
|
procedure_references: procedureReferences,
|
|
246
258
|
xrefs: z.array(ghidraCanonicalAddressSchema),
|
|
247
259
|
};
|
|
@@ -26,6 +26,7 @@ const PAGINATED_OPERATIONS = new Set([
|
|
|
26
26
|
"search_procedures",
|
|
27
27
|
"search_strings",
|
|
28
28
|
"procedure_references",
|
|
29
|
+
"read_function_instructions",
|
|
29
30
|
"analyze_function",
|
|
30
31
|
]);
|
|
31
32
|
const SEARCH_OPERATIONS = new Set([
|
|
@@ -93,6 +94,12 @@ export const limitationsFor = (operation) => {
|
|
|
93
94
|
"Pseudocode is Ghidra decompiler output, not original source and not text-equivalent to Hopper output; each decompile has a 30-second native deadline.",
|
|
94
95
|
"External functions and functions without an analyzable body return null; other decompiler failures remain explicit.",
|
|
95
96
|
];
|
|
97
|
+
case "read_function_instructions":
|
|
98
|
+
return [
|
|
99
|
+
...common,
|
|
100
|
+
"This bounded fast path reads only the requested function instruction window and does not invoke the decompiler or whole-program string/name inventories.",
|
|
101
|
+
"Instruction text is Ghidra-specific and does not claim textual equivalence with Hopper output.",
|
|
102
|
+
];
|
|
96
103
|
case "procedure_assembly":
|
|
97
104
|
return [
|
|
98
105
|
...common,
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { HopperProcessError } from "../domain/errors.js";
|
|
2
|
+
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
|
|
3
|
+
import { err, ok } from "../domain/result.js";
|
|
4
|
+
import { createOwnedHopperShutdownDiagnostic, providerCleanupFailure, } from "./HopperDiagnostics.js";
|
|
5
|
+
import { isHopperCleanupRequired, isHopperShutdownAcknowledgement, } from "./HopperSessionValues.js";
|
|
6
|
+
/** Close Hopper phases and retain every resource whose cleanup is unverified. */
|
|
7
|
+
export const cleanupHopperSession = async (input) => {
|
|
8
|
+
const state = {
|
|
9
|
+
issues: [],
|
|
10
|
+
resources: new Set(),
|
|
11
|
+
cleanupResult: undefined,
|
|
12
|
+
shutdownConfirmed: false,
|
|
13
|
+
ownedProcessStopped: false,
|
|
14
|
+
};
|
|
15
|
+
await report(input.progress, 0, "requesting Hopper document shutdown");
|
|
16
|
+
await requestShutdown(input, state);
|
|
17
|
+
await report(input.progress, 0.35, "releasing Hopper bridge transport");
|
|
18
|
+
input.releaseTransport(input.socket);
|
|
19
|
+
await stopProcess(input, state);
|
|
20
|
+
recordUnconfirmedDocument(input, state);
|
|
21
|
+
await report(input.progress, 0.75, "removing Hopper private runtime files");
|
|
22
|
+
await closeRuntimeRoot(input.runtimeRoot, state);
|
|
23
|
+
return cleanupOutcome(input, state);
|
|
24
|
+
};
|
|
25
|
+
const requestShutdown = async (input, state) => {
|
|
26
|
+
if (input.socket === undefined ||
|
|
27
|
+
input.socket.destroyed ||
|
|
28
|
+
input.activeRequest !== null) {
|
|
29
|
+
if (input.activeRequest !== null)
|
|
30
|
+
input.logger.warn({
|
|
31
|
+
method: input.activeRequest.operation,
|
|
32
|
+
callerState: input.activeRequest.callerState,
|
|
33
|
+
}, "Hopper shutdown skipped because a bridge operation remained active");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const shutdown = await input
|
|
37
|
+
.request("shutdown")
|
|
38
|
+
.catch(() => err(new HopperProcessError(null)));
|
|
39
|
+
if (shutdown.ok &&
|
|
40
|
+
isHopperCleanupRequired(shutdown.value) &&
|
|
41
|
+
input.launch?.shutdownByCleanup === true &&
|
|
42
|
+
input.launch.cleanup !== undefined) {
|
|
43
|
+
state.cleanupResult = await input.launch
|
|
44
|
+
.cleanup()
|
|
45
|
+
.catch(providerCleanupFailure);
|
|
46
|
+
if (!state.cleanupResult.cleaned) {
|
|
47
|
+
state.shutdownConfirmed = await fallbackDocumentShutdown(input);
|
|
48
|
+
state.cleanupResult = await input.launch
|
|
49
|
+
.cleanup()
|
|
50
|
+
.catch(providerCleanupFailure);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (shutdown.ok) {
|
|
54
|
+
state.shutdownConfirmed = isHopperShutdownAcknowledgement(shutdown.value);
|
|
55
|
+
}
|
|
56
|
+
if (!shutdown.ok ||
|
|
57
|
+
(!state.shutdownConfirmed && state.cleanupResult === undefined))
|
|
58
|
+
input.logger.warn({ status: shutdown.ok ? "invalid-acknowledgement" : "failed" }, "Hopper document shutdown was not confirmed");
|
|
59
|
+
};
|
|
60
|
+
const fallbackDocumentShutdown = async (input) => {
|
|
61
|
+
const fallback = await input
|
|
62
|
+
.request("shutdown_document")
|
|
63
|
+
.catch(() => err(new HopperProcessError(null)));
|
|
64
|
+
const confirmed = fallback.ok && isHopperShutdownAcknowledgement(fallback.value);
|
|
65
|
+
if (!confirmed)
|
|
66
|
+
input.logger.warn({ status: fallback.ok ? "invalid-acknowledgement" : "failed" }, "Hopper document shutdown fallback was not confirmed");
|
|
67
|
+
return confirmed;
|
|
68
|
+
};
|
|
69
|
+
const stopProcess = async (input, state) => {
|
|
70
|
+
const supervisor = input.processSupervisor;
|
|
71
|
+
if (supervisor === undefined)
|
|
72
|
+
return;
|
|
73
|
+
const stopped = await supervisor.stop(state.cleanupResult === undefined
|
|
74
|
+
? {}
|
|
75
|
+
: { cleanupResult: state.cleanupResult });
|
|
76
|
+
const diagnostic = createOwnedHopperShutdownDiagnostic(supervisor.launch, stopped, state.cleanupResult);
|
|
77
|
+
try {
|
|
78
|
+
input.onDiagnostic?.(diagnostic);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Diagnostic consumers cannot change the already-observed cleanup result.
|
|
82
|
+
}
|
|
83
|
+
input.logger.info(diagnostic, "Owned Hopper launcher shutdown completed");
|
|
84
|
+
if (stopped.status !== "incomplete") {
|
|
85
|
+
state.ownedProcessStopped = stopped.status !== "not-owned";
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const processGroupId = supervisor.launch.ownership?.processGroupId;
|
|
89
|
+
const resource = processGroupId === undefined
|
|
90
|
+
? "hopper-process"
|
|
91
|
+
: `process-group:${String(processGroupId)}`;
|
|
92
|
+
state.resources.add(resource);
|
|
93
|
+
state.issues.push({ resource, reason: stopped.reason });
|
|
94
|
+
input.logger.warn({ reason: stopped.reason }, "Owned launcher cleanup failed closed");
|
|
95
|
+
};
|
|
96
|
+
const recordUnconfirmedDocument = (input, state) => {
|
|
97
|
+
if ((input.socket === undefined && input.launch === undefined) ||
|
|
98
|
+
state.shutdownConfirmed ||
|
|
99
|
+
state.ownedProcessStopped)
|
|
100
|
+
return;
|
|
101
|
+
state.resources.add("hopper-document");
|
|
102
|
+
state.issues.push({
|
|
103
|
+
resource: "hopper-document",
|
|
104
|
+
reason: input.activeRequest === null
|
|
105
|
+
? "authenticated shutdown acknowledgement was not observed"
|
|
106
|
+
: `bridge operation ${input.activeRequest.operation} remained active after caller ${input.activeRequest.callerState}`,
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
const closeRuntimeRoot = async (runtimeRoot, state) => {
|
|
110
|
+
try {
|
|
111
|
+
await runtimeRoot?.close();
|
|
112
|
+
}
|
|
113
|
+
catch (cause) {
|
|
114
|
+
const resource = runtimeRoot?.path ?? "hopper-runtime-root";
|
|
115
|
+
state.resources.add(resource);
|
|
116
|
+
state.issues.push({
|
|
117
|
+
resource,
|
|
118
|
+
reason: cause instanceof Error
|
|
119
|
+
? cause.message
|
|
120
|
+
: "private runtime root cleanup failed",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const cleanupOutcome = (input, state) => {
|
|
125
|
+
if (state.issues.length === 0)
|
|
126
|
+
return ok(null);
|
|
127
|
+
return err(new ProviderCleanupError("hopper", [...state.resources], {
|
|
128
|
+
issues: cleanupIssueDetails(state.issues),
|
|
129
|
+
...(input.runtimeRoot === undefined
|
|
130
|
+
? {}
|
|
131
|
+
: { runtime_root: input.runtimeRoot.path }),
|
|
132
|
+
...(input.activeRequest === null
|
|
133
|
+
? {}
|
|
134
|
+
: { active_request: activityDetails(input.activeRequest) }),
|
|
135
|
+
}));
|
|
136
|
+
};
|
|
137
|
+
const cleanupIssueDetails = (issues) => issues.map((issue) => ({
|
|
138
|
+
resource: issue.resource,
|
|
139
|
+
reason: issue.reason,
|
|
140
|
+
}));
|
|
141
|
+
const activityDetails = (activity) => ({
|
|
142
|
+
request_id: activity.requestId,
|
|
143
|
+
operation: activity.operation,
|
|
144
|
+
elapsed_ms: activity.elapsedMs,
|
|
145
|
+
timeout_ms: activity.timeoutMs,
|
|
146
|
+
caller_state: activity.callerState,
|
|
147
|
+
queued_requests: activity.queuedRequests,
|
|
148
|
+
});
|
|
149
|
+
const report = async (progress, completed, message) => {
|
|
150
|
+
await progress
|
|
151
|
+
?.report({
|
|
152
|
+
phase: "hopper_cleanup",
|
|
153
|
+
completed,
|
|
154
|
+
total: 1,
|
|
155
|
+
message,
|
|
156
|
+
})
|
|
157
|
+
.catch(() => undefined);
|
|
158
|
+
};
|