rea-agents 1.6.0 → 2.0.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 +157 -66
- package/bridge/ghidra/ReaGhidraBridge.java +2075 -0
- package/bridge/hopper_bridge.py +13 -3
- package/dist/application/AbortablePromise.js +25 -0
- package/dist/application/AnalysisProvider.js +3 -0
- package/dist/application/AnalysisProviderEvaluation.js +60 -0
- package/dist/application/AnalysisProviderRegistry.js +228 -0
- package/dist/application/AnalysisSnapshotCache.js +41 -21
- package/dist/application/AnalysisSnapshotFiles.js +5 -6
- package/dist/application/BinarySession.js +218 -101
- package/dist/application/CapabilityInventory.js +18 -0
- package/dist/application/CompositeProvider.js +20 -8
- package/dist/application/CrossVersionInvestigation.js +10 -6
- package/dist/application/DirectAnalysis.js +49 -35
- package/dist/application/Doctor.js +110 -8
- package/dist/application/ElectronBoundaryAnalysis.js +96 -0
- package/dist/application/ElectronBoundaryGraph.js +9 -0
- package/dist/application/ElectronBoundaryGraphContext.js +62 -0
- package/dist/application/ElectronBoundaryGraphIpc.js +242 -0
- package/dist/application/ElectronBoundaryGraphNative.js +86 -0
- package/dist/application/ElectronBoundaryGraphWindows.js +199 -0
- package/dist/application/EnhancedTools.js +39 -14
- package/dist/application/InvestigationProviders.js +41 -0
- package/dist/application/JavaScriptApplicationEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +89 -0
- package/dist/application/JavaScriptApplicationService.js +74 -0
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +29 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +90 -0
- package/dist/application/JavaScriptArtifactAnalysis.js +305 -0
- package/dist/application/JavaScriptArtifactAnalysisTypes.js +1 -0
- package/dist/application/JavaScriptArtifactFiles.js +231 -0
- package/dist/application/JavaScriptArtifactGraphAccumulator.js +54 -0
- package/dist/application/JavaScriptArtifactGraphBuilder.js +144 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +205 -0
- package/dist/application/JavaScriptArtifactGraphDocuments.js +108 -0
- package/dist/application/JavaScriptArtifactGraphEvidence.js +99 -0
- package/dist/application/JavaScriptArtifactGraphFindings.js +271 -0
- package/dist/application/JavaScriptArtifactGraphStructure.js +409 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +77 -0
- package/dist/application/JavaScriptArtifactReconstructionInput.js +18 -0
- package/dist/application/JavaScriptReplayPlanning.js +201 -0
- package/dist/application/JavaScriptReplayService.js +266 -0
- package/dist/application/JavaScriptRuntimeReconciliationEvidence.js +25 -0
- package/dist/application/JavaScriptRuntimeReconciliationService.js +27 -0
- package/dist/application/MacHopper.js +20 -4
- package/dist/application/ManagedApplicationGraphService.js +55 -0
- package/dist/application/ManagedMemberComparisonService.js +122 -0
- package/dist/application/ManagedNativeVerificationService.js +39 -0
- package/dist/application/ManagedReconstructionService.js +58 -0
- package/dist/application/ManagedRuntimeCorrelationService.js +103 -0
- package/dist/application/PermissionAuthority.js +1 -1
- package/dist/application/ProcessCaptureLifecycle.js +1 -1
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessSampling.js +16 -12
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/SessionProviderRouter.js +219 -0
- package/dist/application/Setup.js +84 -60
- package/dist/application/SetupClients.js +1 -1
- package/dist/application/SetupPlan.js +9 -2
- package/dist/application/SupportedClients.js +40 -16
- package/dist/application/Upgrade.js +1 -1
- package/dist/application/runtime.js +11 -6
- package/dist/artifacts/AsarArtifactReader.js +23 -3
- package/dist/browser/CdpBrowserProvider.js +4 -2
- package/dist/browser/CdpCaptureEvents.js +49 -0
- package/dist/browser/CdpElectronInspection.js +106 -162
- package/dist/browser/CdpElectronProvider.js +9 -3
- package/dist/browser/CdpElectronScriptEvents.js +69 -0
- package/dist/browser/CdpElectronScripts.js +94 -0
- package/dist/browser/CdpElectronWorkers.js +56 -0
- package/dist/browser/CdpEndpoint.js +14 -1
- package/dist/browser/CdpPageCapture.js +8 -4
- package/dist/catalogIdentity.js +6 -42
- package/dist/cli.js +446 -34
- package/dist/cliApplicationCommands.js +62 -0
- package/dist/cliBrowserAdvancedCommands.js +5 -4
- package/dist/cliBrowserCommands.js +5 -4
- package/dist/cliCommandNames.js +56 -0
- package/dist/cliElectronCommands.js +106 -3
- package/dist/cliEvidenceCommands.js +4 -3
- package/dist/cliInvestigationCommands.js +2 -1
- package/dist/cliJsonInput.js +54 -0
- package/dist/cliPolicyCommands.js +2 -1
- package/dist/cliProcessCommands.js +3 -2
- package/dist/config.js +101 -2
- package/dist/contracts/applicationToolContracts.js +91 -0
- package/dist/contracts/artifactToolContracts.js +5 -9
- package/dist/contracts/browserToolContracts.js +14 -62
- package/dist/contracts/electronToolContracts.js +45 -19
- package/dist/contracts/enhancedInputs.js +2 -2
- package/dist/contracts/errorSchemas.js +1 -0
- package/dist/contracts/investigationExamples.js +0 -6
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +46 -0
- package/dist/contracts/javascriptRuntimeReconciliationExample.js +208 -0
- package/dist/contracts/managedToolContracts.js +153 -0
- package/dist/contracts/managedWorkflowExamples.js +346 -0
- package/dist/contracts/managedWorkflowToolContracts.js +203 -0
- package/dist/contracts/nativeToolContracts.js +2 -6
- package/dist/contracts/promptContracts.js +18 -2
- package/dist/contracts/providerSelection.js +28 -0
- package/dist/contracts/sessionLifecycleInputs.js +2 -0
- package/dist/contracts/toolContractExamples.js +10 -25
- package/dist/contracts/toolContracts.js +69 -65
- package/dist/contracts/toolEffects.js +160 -0
- package/dist/contracts/toolOutputSchemas.js +152 -42
- package/dist/doctorRuntime.js +47 -0
- package/dist/domain/analysisProfile.js +78 -0
- package/dist/domain/analysisSnapshot.js +92 -46
- package/dist/domain/artifactComparison.js +3 -4
- package/dist/domain/artifactGraph.js +1 -0
- package/dist/domain/binaryTarget.js +4 -29
- package/dist/domain/browserObservation.js +3 -0
- package/dist/domain/bundleComparison.js +4 -4
- package/dist/domain/electronObservation.js +13 -0
- package/dist/domain/electronStaticAnalysis.js +9 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +194 -0
- package/dist/domain/electronStaticAnalysisIpc.js +171 -0
- package/dist/domain/electronStaticAnalysisNative.js +164 -0
- package/dist/domain/electronStaticAnalysisTypes.js +1 -0
- package/dist/domain/electronStaticAnalysisValues.js +103 -0
- package/dist/domain/errors.js +148 -7
- package/dist/domain/evidence.js +44 -5
- package/dist/domain/evidenceBundle.js +9 -7
- package/dist/domain/functionComparison.js +9 -3
- package/dist/domain/functionComparisonNormalization.js +37 -0
- package/dist/domain/functionComparisonSchemas.js +3 -8
- package/dist/domain/functionDossierEvidence.js +4 -1
- package/dist/domain/hopperValues.js +66 -9
- package/dist/domain/inputIssueProjection.js +52 -0
- package/dist/domain/investigationWorkspace.js +2 -1
- package/dist/domain/javascriptApplicationAnalysis.js +155 -0
- package/dist/domain/javascriptApplicationChangeGraph.js +231 -0
- package/dist/domain/javascriptApplicationEvidenceSchemas.js +379 -0
- package/dist/domain/javascriptApplicationGraph.js +282 -0
- package/dist/domain/javascriptApplicationGraphSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionComparison.js +116 -0
- package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +139 -0
- package/dist/domain/javascriptApplicationVersionItems.js +221 -0
- package/dist/domain/javascriptApplicationVersionKeys.js +170 -0
- package/dist/domain/javascriptAstFingerprint.js +143 -0
- package/dist/domain/javascriptFeatureSeed.js +140 -0
- package/dist/domain/javascriptFeatureTrace.js +232 -0
- package/dist/domain/javascriptFeatureTraceSchemas.js +136 -0
- package/dist/domain/javascriptFeatureTraversal.js +88 -0
- package/dist/domain/javascriptNativeHandoff.js +85 -0
- package/dist/domain/javascriptReplay.js +361 -0
- package/dist/domain/javascriptRuntimeLoadState.js +106 -0
- package/dist/domain/javascriptRuntimeReconciliation.js +42 -0
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +169 -0
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +247 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +160 -0
- package/dist/domain/javascriptRuntimeReconciliationResult.js +119 -0
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +316 -0
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +245 -0
- package/dist/domain/javascriptRuntimeStaticCandidates.js +191 -0
- package/dist/domain/javascriptStaticAnalysis.js +346 -0
- package/dist/domain/javascriptStaticAnalysisFindings.js +39 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +274 -0
- package/dist/domain/javascriptStaticAnalysisState.js +23 -0
- package/dist/domain/javascriptStaticAnalysisTypes.js +1 -0
- package/dist/domain/managedApplicationGraph.js +556 -0
- package/dist/domain/managedArtifact.js +444 -0
- package/dist/domain/managedMemberComparison.js +565 -0
- package/dist/domain/managedNativeVerification.js +408 -0
- package/dist/domain/managedReconstruction.js +183 -0
- package/dist/domain/managedRuntimeCorrelation.js +175 -0
- package/dist/domain/reconstructionVerificationSchemas.js +1 -3
- package/dist/dotnet/ManagedArtifactInspector.js +318 -0
- package/dist/dotnet/ManagedMemberInspector.js +887 -0
- package/dist/dotnet/ManagedMetadataHeaps.js +131 -0
- package/dist/dotnet/ManagedMetadataInventory.js +368 -0
- package/dist/dotnet/ManagedMetadataLayout.js +322 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +391 -0
- package/dist/dotnet/ManagedPeReader.js +157 -0
- package/dist/dotnet/ManagedReaderFailure.js +10 -0
- package/dist/dotnet/ManagedStaticProvider.js +203 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraAnalysisProfile.js +37 -0
- package/dist/ghidra/GhidraClient.js +400 -0
- package/dist/ghidra/GhidraClientTypes.js +1 -0
- package/dist/ghidra/GhidraDefaults.js +20 -0
- package/dist/ghidra/GhidraDiagnostics.js +43 -0
- package/dist/ghidra/GhidraDoctor.js +59 -0
- package/dist/ghidra/GhidraFunctionValues.js +264 -0
- package/dist/ghidra/GhidraInstallation.js +239 -0
- package/dist/ghidra/GhidraInventoryValues.js +217 -0
- package/dist/ghidra/GhidraLauncher.js +139 -0
- package/dist/ghidra/GhidraProvider.js +366 -0
- package/dist/ghidra/GhidraRequestQueue.js +98 -0
- package/dist/ghidra/GhidraResponseBuffer.js +31 -0
- package/dist/ghidra/GhidraResponseRouter.js +24 -0
- package/dist/ghidra/GhidraSessionError.js +39 -0
- package/dist/ghidra/GhidraSessionValues.js +62 -0
- package/dist/ghidra/GhidraSocketConnection.js +46 -0
- package/dist/ghidra/protocol.js +69 -0
- package/dist/hopper/BridgeLauncher.js +19 -54
- package/dist/hopper/HopperAnalysisProfile.js +106 -0
- package/dist/hopper/HopperClient.js +154 -162
- package/dist/hopper/HopperProvider.js +99 -7
- package/dist/hopper/HopperSessionValues.js +27 -0
- package/dist/hopper/HopperSocketConnection.js +31 -0
- package/dist/main.js +14 -0
- package/dist/native/NativeMacOSProvider.js +3 -1
- package/dist/process/PendingOperations.js +59 -0
- package/dist/process/PrivateRuntimeRoot.js +33 -0
- package/dist/process/ProviderDeadline.js +95 -0
- package/dist/process/ProviderProcess.js +275 -0
- package/dist/replay/JavaScriptReplayWorker.js +286 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +434 -0
- package/dist/replay/LinuxRuntimeClosure.js +46 -0
- package/dist/replay/LinuxSeccompPolicy.js +40 -0
- package/dist/replay/ReplayWorkerProtocol.js +55 -0
- package/dist/replay/SystemJavaScriptReplayHost.js +182 -0
- package/dist/server/createServer.js +53 -0
- package/dist/server/promptCompletion.js +9 -3
- package/dist/server/registerApplicationTools.js +115 -0
- package/dist/server/registerArtifactComparisonTool.js +16 -9
- package/dist/server/registerBrowserTools.js +33 -8
- package/dist/server/registerBundleComparisonTool.js +17 -20
- package/dist/server/registerElectronTools.js +45 -3
- package/dist/server/registerEnhancedTools.js +67 -19
- package/dist/server/registerEvidenceTools.js +5 -1
- package/dist/server/registerFunctionComparisonTool.js +14 -7
- package/dist/server/registerInvestigationTools.js +21 -17
- package/dist/server/registerManagedTools.js +6 -0
- package/dist/server/registerManagedWorkflowTools.js +277 -0
- package/dist/server/registerOfficialTools.js +11 -8
- package/dist/server/registerProcessComparisonTool.js +25 -47
- package/dist/server/registerSessionStatusTool.js +7 -7
- package/dist/server/registerSessionTools.js +57 -87
- package/dist/server/sessionEvidence.js +14 -19
- package/dist/server/toolInputValidation.js +77 -0
- package/dist/server/toolRegistrationOptions.js +79 -2
- package/dist/server/toolResult.js +25 -17
- package/package.json +17 -7
- package/scripts/rea.mjs +36 -2
- package/skills/rea-analysis/SKILL.md +59 -3
- /package/dist/{application → process}/ProcessOwnership.js +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseBinaryTarget } from "../domain/binaryTarget.js";
|
|
2
|
-
import {
|
|
2
|
+
import { analysisProfilesEqual, } from "../domain/analysisProfile.js";
|
|
3
|
+
import { AnalysisCapabilityUnavailableError, EvidenceIntegrityError, EvidenceLimitError, AnalysisCancelledError, NoBinaryOpenError, ProviderAdapterError, } from "../domain/errors.js";
|
|
3
4
|
import { err, ok } from "../domain/result.js";
|
|
4
5
|
import { createEvidence } from "../domain/evidence.js";
|
|
5
6
|
import { evidenceBundleForTarget } from "../domain/evidenceBundle.js";
|
|
@@ -7,27 +8,25 @@ import { EvidenceLedger } from "./EvidenceLedger.js";
|
|
|
7
8
|
import { UnknownRegistryError } from "../domain/errors.js";
|
|
8
9
|
import { enhancedToolNameSchema } from "../contracts/enhancedInputs.js";
|
|
9
10
|
import { OFFICIAL_TOOL_CONTRACTS } from "../contracts/toolContracts.js";
|
|
10
|
-
import { snapshotMatchesTarget, snapshotTarget, } from "../domain/analysisSnapshot.js";
|
|
11
|
+
import { snapshotMatchesBinding, snapshotMatchesTarget, snapshotTarget, } from "../domain/analysisSnapshot.js";
|
|
11
12
|
import { AnalysisSnapshotCache, isSnapshotCacheable, } from "./AnalysisSnapshotCache.js";
|
|
12
13
|
import { UNKNOWN_REGISTRY_PROVIDER, unknownEvidenceLinks, unknownMutationEvidence, } from "./UnknownEvidence.js";
|
|
13
14
|
import { parseInvestigationWorkspace, } from "../domain/investigationWorkspace.js";
|
|
15
|
+
import { SessionProviderRouter, } from "./SessionProviderRouter.js";
|
|
14
16
|
const OFFICIAL_OPERATIONS = new Set(OFFICIAL_TOOL_CONTRACTS.map(({ name }) => name));
|
|
15
17
|
/**
|
|
16
18
|
* Owns the single active target shared by CLI and MCP adapters.
|
|
17
19
|
*
|
|
18
|
-
* Target transitions are serialized because
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* client whose bridge was already shut down.
|
|
20
|
+
* Target transitions are serialized because switching targets tears down the
|
|
21
|
+
* active provider client. A failed switch recreates the previous target instead
|
|
22
|
+
* of retaining a client whose resources were already shut down.
|
|
22
23
|
*/
|
|
23
24
|
export class BinarySession {
|
|
24
25
|
provider;
|
|
25
26
|
#active;
|
|
26
27
|
#transition = Promise.resolve();
|
|
27
28
|
#calls = new Set();
|
|
28
|
-
#
|
|
29
|
-
#providerIdentity;
|
|
30
|
-
#capabilities;
|
|
29
|
+
#providerRouter;
|
|
31
30
|
#evidence = new EvidenceLedger({
|
|
32
31
|
maxRecords: 10_000,
|
|
33
32
|
maxBytes: 64 * 1024 * 1024,
|
|
@@ -36,46 +35,55 @@ export class BinarySession {
|
|
|
36
35
|
#investigationWorkspaces = new Map();
|
|
37
36
|
#runtimeAvailability = new Map();
|
|
38
37
|
#availabilityListeners = new Set();
|
|
39
|
-
#snapshotsEnabled;
|
|
40
38
|
#snapshotInvalidated = false;
|
|
41
39
|
constructor(provider, options = {}) {
|
|
42
40
|
this.provider = provider;
|
|
43
|
-
this.#
|
|
44
|
-
|
|
45
|
-
typeof provider === "function"
|
|
41
|
+
this.#providerRouter =
|
|
42
|
+
provider instanceof SessionProviderRouter
|
|
46
43
|
? provider
|
|
47
|
-
: (
|
|
48
|
-
this.#providerIdentity =
|
|
49
|
-
typeof provider === "function"
|
|
50
|
-
? { id: "unidentified", name: "Unidentified provider", version: null }
|
|
51
|
-
: provider.identity();
|
|
52
|
-
this.#capabilities =
|
|
53
|
-
typeof provider === "function"
|
|
54
|
-
? undefined
|
|
55
|
-
: new Map(provider
|
|
56
|
-
.capabilities()
|
|
57
|
-
.map((descriptor) => [descriptor.operation, descriptor]));
|
|
44
|
+
: SessionProviderRouter.legacy(provider, options);
|
|
58
45
|
}
|
|
59
46
|
/** Identify the provider producing evidence for this session. */
|
|
60
47
|
providerIdentity(operation) {
|
|
48
|
+
const route = this.#currentRoute();
|
|
49
|
+
let selected = route.identity;
|
|
61
50
|
if (operation !== undefined) {
|
|
62
|
-
const exact =
|
|
51
|
+
const exact = route.capabilities?.get(operation)?.provider;
|
|
63
52
|
if (exact !== undefined)
|
|
64
|
-
|
|
65
|
-
if (enhancedToolNameSchema.safeParse(operation).success) {
|
|
53
|
+
selected = exact;
|
|
54
|
+
else if (enhancedToolNameSchema.safeParse(operation).success) {
|
|
66
55
|
const providers = new Map();
|
|
67
|
-
for (const descriptor of
|
|
56
|
+
for (const descriptor of route.capabilities?.values() ?? [])
|
|
68
57
|
if (descriptor.available &&
|
|
69
58
|
OFFICIAL_OPERATIONS.has(descriptor.operation))
|
|
70
59
|
providers.set(descriptor.provider.id, descriptor.provider);
|
|
71
60
|
if (providers.size === 1) {
|
|
72
61
|
const provider = providers.values().next().value;
|
|
73
62
|
if (provider !== undefined)
|
|
74
|
-
|
|
63
|
+
selected = provider;
|
|
75
64
|
}
|
|
76
65
|
}
|
|
77
66
|
}
|
|
78
|
-
|
|
67
|
+
const profile = this.#active?.profile;
|
|
68
|
+
return structuredClone(profile !== null &&
|
|
69
|
+
profile !== undefined &&
|
|
70
|
+
profile.provider.id === selected.id
|
|
71
|
+
? profile.provider
|
|
72
|
+
: selected);
|
|
73
|
+
}
|
|
74
|
+
/** Return the selected immutable profile, optionally scoped to an operation. */
|
|
75
|
+
analysisProfile(operation) {
|
|
76
|
+
const profile = this.#active?.profile;
|
|
77
|
+
if (profile === null || profile === undefined)
|
|
78
|
+
return undefined;
|
|
79
|
+
if (operation !== undefined &&
|
|
80
|
+
this.providerIdentity(operation).id !== profile.provider.id)
|
|
81
|
+
return undefined;
|
|
82
|
+
return structuredClone(profile);
|
|
83
|
+
}
|
|
84
|
+
/** Return opaque adapter metadata retained for legacy open_binary output. */
|
|
85
|
+
openCompatibility() {
|
|
86
|
+
return structuredClone(this.#active?.compatibility ?? {});
|
|
79
87
|
}
|
|
80
88
|
/** Observe runtime provider-health changes that affect discovery metadata. */
|
|
81
89
|
onAvailabilityChanged(listener) {
|
|
@@ -104,20 +112,24 @@ export class BinarySession {
|
|
|
104
112
|
}
|
|
105
113
|
/** Export immutable cached calls and session evidence for the active target. */
|
|
106
114
|
exportAnalysisSnapshot() {
|
|
107
|
-
if (!this.#snapshotsEnabled)
|
|
108
|
-
return err(new EvidenceIntegrityError("Analysis snapshots are unavailable with custom Hopper loader arguments"));
|
|
109
115
|
if (this.#snapshotInvalidated)
|
|
110
116
|
return err(new EvidenceIntegrityError("Analysis snapshots are unavailable after analysis metadata mutations"));
|
|
111
117
|
const target = this.#active?.target;
|
|
112
|
-
|
|
118
|
+
const profile = this.#active?.profile ?? undefined;
|
|
119
|
+
if (target !== undefined && profile === undefined)
|
|
120
|
+
return err(new EvidenceIntegrityError("Analysis snapshots require a concrete provider analysis profile"));
|
|
121
|
+
return this.#snapshot.export(target, profile, target === undefined
|
|
113
122
|
? this.#evidence.export()
|
|
114
123
|
: evidenceBundleForTarget(this.#evidence.export(), target.sha256));
|
|
115
124
|
}
|
|
116
125
|
/** Stage validated cached calls for an identical binary and merge its evidence. */
|
|
117
126
|
importAnalysisSnapshot(snapshot) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
const active = this.#active;
|
|
128
|
+
if (active?.profile === null)
|
|
129
|
+
return err(new EvidenceIntegrityError("Analysis snapshot profile_mismatch: the active target has no concrete analysis profile"));
|
|
130
|
+
return this.#snapshot.import(snapshot, active === undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: { target: active.target, profile: active.profile }, (bundle) => this.#evidence.import(bundle));
|
|
121
133
|
}
|
|
122
134
|
/** Retain one validated immutable workspace revision for session resources. */
|
|
123
135
|
retainInvestigationWorkspace(workspace) {
|
|
@@ -187,15 +199,31 @@ export class BinarySession {
|
|
|
187
199
|
const parsed = await parseBinaryTarget(path, process.cwd(), process.arch, options.targetKind);
|
|
188
200
|
if (!parsed.ok)
|
|
189
201
|
return parsed;
|
|
202
|
+
const sameTarget = this.#active?.target.path === parsed.value.path &&
|
|
203
|
+
snapshotMatchesTarget(snapshotTarget(this.#active.target), parsed.value);
|
|
204
|
+
const resolvedRoute = sameTarget &&
|
|
205
|
+
options.providerId === undefined &&
|
|
206
|
+
this.#active !== undefined
|
|
207
|
+
? ok(this.#active.route)
|
|
208
|
+
: await this.#providerRouter.resolve(parsed.value, options.providerId, options.signal);
|
|
209
|
+
if (!resolvedRoute.ok)
|
|
210
|
+
return resolvedRoute;
|
|
211
|
+
const route = resolvedRoute.value;
|
|
212
|
+
const { profile, compatibility } = route;
|
|
190
213
|
if (options.snapshot !== undefined &&
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
214
|
+
(profile === null ||
|
|
215
|
+
!snapshotMatchesBinding(options.snapshot, parsed.value, profile)))
|
|
216
|
+
return err(new EvidenceIntegrityError("Analysis snapshot profile_mismatch: target, provider, or analysis profile does not match the requested binary"));
|
|
217
|
+
if (this.#active === undefined &&
|
|
218
|
+
!this.#snapshot.matches(parsed.value, profile ?? undefined))
|
|
219
|
+
return err(new EvidenceIntegrityError("Analysis snapshot profile_mismatch: staged target, provider, or analysis profile does not match"));
|
|
195
220
|
if (isAborted(options.signal))
|
|
196
221
|
return err(new AnalysisCancelledError("open_binary"));
|
|
197
|
-
|
|
198
|
-
|
|
222
|
+
const activeProfile = this.#active?.profile;
|
|
223
|
+
const sameProfile = activeProfile === null || activeProfile === undefined
|
|
224
|
+
? profile === null
|
|
225
|
+
: profile !== null && analysisProfilesEqual(activeProfile, profile);
|
|
226
|
+
if (sameTarget && sameProfile) {
|
|
199
227
|
if (options.snapshot !== undefined) {
|
|
200
228
|
const imported = this.importAnalysisSnapshot(options.snapshot);
|
|
201
229
|
if (!imported.ok)
|
|
@@ -207,7 +235,7 @@ export class BinarySession {
|
|
|
207
235
|
const previous = this.#active;
|
|
208
236
|
this.#active = undefined;
|
|
209
237
|
await previous?.client.close();
|
|
210
|
-
const client =
|
|
238
|
+
const client = route.createClient(parsed.value);
|
|
211
239
|
const started = await client.execute("health", {}, options);
|
|
212
240
|
if (!started.ok) {
|
|
213
241
|
await client.close();
|
|
@@ -219,9 +247,18 @@ export class BinarySession {
|
|
|
219
247
|
await this.#restore(previous);
|
|
220
248
|
return err(new AnalysisCancelledError("open_binary"));
|
|
221
249
|
}
|
|
222
|
-
this.#active = {
|
|
250
|
+
this.#active = {
|
|
251
|
+
target: parsed.value,
|
|
252
|
+
client,
|
|
253
|
+
profile,
|
|
254
|
+
compatibility: structuredClone(compatibility),
|
|
255
|
+
route,
|
|
256
|
+
};
|
|
223
257
|
this.#clearRuntimeAvailability();
|
|
224
|
-
|
|
258
|
+
if (profile === null)
|
|
259
|
+
this.#snapshot.clear();
|
|
260
|
+
else
|
|
261
|
+
this.#snapshot.select(parsed.value, profile);
|
|
225
262
|
if (options.snapshot !== undefined) {
|
|
226
263
|
const imported = this.importAnalysisSnapshot(options.snapshot);
|
|
227
264
|
if (!imported.ok) {
|
|
@@ -252,59 +289,74 @@ export class BinarySession {
|
|
|
252
289
|
/** Describe the active binary session. */
|
|
253
290
|
status() {
|
|
254
291
|
const target = this.#active?.target;
|
|
292
|
+
const route = this.#currentRoute();
|
|
293
|
+
const configuredProvider = this.#providerRouter.configuredIdentity();
|
|
255
294
|
const provider = {
|
|
256
|
-
id:
|
|
257
|
-
name:
|
|
258
|
-
version:
|
|
295
|
+
id: configuredProvider.id,
|
|
296
|
+
name: configuredProvider.name,
|
|
297
|
+
version: configuredProvider.version,
|
|
259
298
|
};
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
providers.set(provider.id, provider);
|
|
263
|
-
else
|
|
264
|
-
for (const descriptor of this.#capabilities.values())
|
|
265
|
-
providers.set(descriptor.provider.id, descriptor.provider);
|
|
266
|
-
const providerList = [...providers.values()]
|
|
267
|
-
.sort((left, right) => left.id.localeCompare(right.id))
|
|
299
|
+
const providerList = this.#providerRouter
|
|
300
|
+
.providerIdentities(route)
|
|
268
301
|
.map(({ id, name, version }) => ({ id, name, version }));
|
|
269
|
-
const capabilities =
|
|
302
|
+
const capabilities = route.capabilities === undefined
|
|
270
303
|
? []
|
|
271
|
-
: [...
|
|
304
|
+
: [...route.capabilities.values()]
|
|
272
305
|
.sort((left, right) => left.operation.localeCompare(right.operation))
|
|
273
306
|
.map((descriptor) => ({
|
|
274
307
|
...descriptor,
|
|
275
308
|
...this.#runtimeAvailability.get(descriptor.operation),
|
|
276
309
|
}))
|
|
277
|
-
.map((descriptor) => (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
effects: {
|
|
286
|
-
mutates_artifact: descriptor.effects.mutatesArtifact,
|
|
287
|
-
launches_process: descriptor.effects.launchesProcess,
|
|
288
|
-
may_show_ui: descriptor.effects.mayShowUi,
|
|
289
|
-
may_access_network: descriptor.effects.mayAccessNetwork,
|
|
290
|
-
may_write_filesystem: descriptor.effects.mayWriteFilesystem,
|
|
291
|
-
changes_permissions: descriptor.effects.changesPermissions,
|
|
292
|
-
requires_root: descriptor.effects.requiresRoot,
|
|
293
|
-
},
|
|
294
|
-
limits: {
|
|
295
|
-
max_results: descriptor.limits.maxResults,
|
|
296
|
-
max_payload_bytes: descriptor.limits.maxPayloadBytes,
|
|
297
|
-
timeout_ms: descriptor.limits.timeoutMs,
|
|
310
|
+
.map((descriptor) => capabilityStatus(descriptor));
|
|
311
|
+
const analysisProviderBinding = route.binding === null
|
|
312
|
+
? null
|
|
313
|
+
: {
|
|
314
|
+
provider: {
|
|
315
|
+
id: route.binding.identity.id,
|
|
316
|
+
name: route.binding.identity.name,
|
|
317
|
+
version: route.binding.identity.version,
|
|
298
318
|
},
|
|
299
|
-
|
|
300
|
-
|
|
319
|
+
selection_source: route.binding.selectionSource,
|
|
320
|
+
analysis_profile: structuredClone(route.binding.profile),
|
|
321
|
+
};
|
|
322
|
+
const analysisProviderCandidates = this.#providerRouter
|
|
323
|
+
.candidateStatuses(route)
|
|
324
|
+
.map((candidate) => ({
|
|
325
|
+
provider: {
|
|
326
|
+
id: candidate.provider.id,
|
|
327
|
+
name: candidate.provider.name,
|
|
328
|
+
version: candidate.provider.version,
|
|
329
|
+
},
|
|
330
|
+
availability: {
|
|
331
|
+
status: candidate.availability.status,
|
|
332
|
+
code: candidate.availability.code,
|
|
333
|
+
reason: candidate.availability.reason,
|
|
334
|
+
diagnostics: structuredClone(candidate.availability.diagnostics),
|
|
335
|
+
},
|
|
336
|
+
target_support: {
|
|
337
|
+
status: candidate.targetSupport.status,
|
|
338
|
+
code: candidate.targetSupport.code,
|
|
339
|
+
reason: candidate.targetSupport.reason,
|
|
340
|
+
diagnostics: structuredClone(candidate.targetSupport.diagnostics),
|
|
341
|
+
},
|
|
342
|
+
selected: candidate.selected,
|
|
343
|
+
capabilities: candidate.capabilities
|
|
344
|
+
.slice()
|
|
345
|
+
.sort((left, right) => left.operation.localeCompare(right.operation))
|
|
346
|
+
.map((descriptor) => capabilityStatus(descriptor)),
|
|
347
|
+
}));
|
|
348
|
+
const common = {
|
|
349
|
+
provider,
|
|
350
|
+
providers: providerList,
|
|
351
|
+
capabilities,
|
|
352
|
+
analysis_provider_binding: analysisProviderBinding,
|
|
353
|
+
analysis_provider_candidates: analysisProviderCandidates,
|
|
354
|
+
};
|
|
301
355
|
return target === undefined
|
|
302
|
-
? { open: false,
|
|
356
|
+
? { open: false, ...common }
|
|
303
357
|
: {
|
|
304
358
|
open: true,
|
|
305
|
-
|
|
306
|
-
providers: providerList,
|
|
307
|
-
capabilities,
|
|
359
|
+
...common,
|
|
308
360
|
path: target.path,
|
|
309
361
|
sha256: target.sha256,
|
|
310
362
|
format: target.format,
|
|
@@ -312,14 +364,14 @@ export class BinarySession {
|
|
|
312
364
|
architecture: target.architecture ?? null,
|
|
313
365
|
};
|
|
314
366
|
}
|
|
315
|
-
/** Return the immutable artifact identity captured before
|
|
367
|
+
/** Return the immutable artifact identity captured before its provider started. */
|
|
316
368
|
activeTarget() {
|
|
317
369
|
return this.#active === undefined
|
|
318
370
|
? undefined
|
|
319
371
|
: structuredClone(this.#active.target);
|
|
320
372
|
}
|
|
321
373
|
/**
|
|
322
|
-
* Invoke a
|
|
374
|
+
* Invoke a provider operation against the active target.
|
|
323
375
|
* Calls may overlap, but a pending target transition prevents new calls from
|
|
324
376
|
* entering until the transition has settled.
|
|
325
377
|
*/
|
|
@@ -327,15 +379,26 @@ export class BinarySession {
|
|
|
327
379
|
const transitioned = await this.#waitForTransition(name, options?.signal);
|
|
328
380
|
if (!transitioned.ok)
|
|
329
381
|
return transitioned;
|
|
330
|
-
const capability = this.#capabilities?.get(name);
|
|
331
|
-
if (this.#capabilities !== undefined && capability?.available !== true)
|
|
332
|
-
return err(new AnalysisCapabilityUnavailableError(this.#providerIdentity.id, name, capability?.reason ?? "operation is not declared by this provider"));
|
|
333
382
|
const active = this.#active;
|
|
334
383
|
if (active === undefined)
|
|
335
384
|
return err(new NoBinaryOpenError());
|
|
336
|
-
const
|
|
385
|
+
const capability = active.route.capabilities?.get(name);
|
|
386
|
+
if (active.route.capabilities !== undefined &&
|
|
387
|
+
capability?.available !== true) {
|
|
388
|
+
const selectionError = this.#providerRouter.unboundOperationError(name, active.route);
|
|
389
|
+
if (selectionError !== undefined)
|
|
390
|
+
return err(selectionError);
|
|
391
|
+
return err(new AnalysisCapabilityUnavailableError(active.route.binding?.identity.id ?? active.route.identity.id, name, capability?.reason ?? "operation is not declared by this provider"));
|
|
392
|
+
}
|
|
393
|
+
const profile = active.profile !== null &&
|
|
394
|
+
(capability === undefined ||
|
|
395
|
+
capability.provider.id === active.profile.provider.id)
|
|
396
|
+
? active.profile
|
|
397
|
+
: undefined;
|
|
398
|
+
const cacheable = profile !== undefined &&
|
|
399
|
+
isSnapshotCacheable(name, capability, arguments_);
|
|
337
400
|
const cached = cacheable
|
|
338
|
-
? this.#snapshot.lookup(active.target, name, arguments_
|
|
401
|
+
? this.#snapshot.lookup(active.target, profile, name, arguments_)
|
|
339
402
|
: undefined;
|
|
340
403
|
if (cached !== undefined)
|
|
341
404
|
return ok(cached);
|
|
@@ -343,14 +406,21 @@ export class BinarySession {
|
|
|
343
406
|
this.#calls.add(call);
|
|
344
407
|
try {
|
|
345
408
|
const result = await call;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
409
|
+
const profiled = commitExecutionProfile(name, result, profile);
|
|
410
|
+
this.#observeRuntimeAvailability(name, profiled);
|
|
411
|
+
if (profiled.ok && cacheable)
|
|
412
|
+
this.#snapshot.record({
|
|
413
|
+
target: active.target,
|
|
414
|
+
profile,
|
|
415
|
+
operation: name,
|
|
416
|
+
parameters: arguments_,
|
|
417
|
+
execution: profiled.value,
|
|
418
|
+
});
|
|
419
|
+
else if (profiled.ok && capability?.effects.mutatesArtifact === true) {
|
|
350
420
|
this.#snapshot.clear();
|
|
351
421
|
this.#snapshotInvalidated = true;
|
|
352
422
|
}
|
|
353
|
-
return
|
|
423
|
+
return profiled;
|
|
354
424
|
}
|
|
355
425
|
finally {
|
|
356
426
|
this.#calls.delete(call);
|
|
@@ -373,9 +443,10 @@ export class BinarySession {
|
|
|
373
443
|
"HopperStartError",
|
|
374
444
|
"HopperRemoteError",
|
|
375
445
|
].includes(result.error._tag)) {
|
|
376
|
-
const
|
|
446
|
+
const capabilities = this.#active?.route.capabilities;
|
|
447
|
+
const providerId = capabilities?.get(operation)?.provider.id;
|
|
377
448
|
let changed = false;
|
|
378
|
-
for (const descriptor of
|
|
449
|
+
for (const descriptor of capabilities?.values() ?? [])
|
|
379
450
|
if (providerId !== undefined && descriptor.provider.id === providerId)
|
|
380
451
|
changed =
|
|
381
452
|
this.#setRuntimeAvailability(descriptor.operation, false, "Provider became unavailable during this session.") || changed;
|
|
@@ -414,6 +485,9 @@ export class BinarySession {
|
|
|
414
485
|
}
|
|
415
486
|
}
|
|
416
487
|
}
|
|
488
|
+
#currentRoute() {
|
|
489
|
+
return this.#active?.route ?? this.#providerRouter.initialRoute();
|
|
490
|
+
}
|
|
417
491
|
#serialize(operation) {
|
|
418
492
|
const result = this.#transition.then(operation, operation);
|
|
419
493
|
this.#transition = result.then(() => undefined, () => undefined);
|
|
@@ -425,10 +499,16 @@ export class BinarySession {
|
|
|
425
499
|
async #restore(previous) {
|
|
426
500
|
if (previous === undefined)
|
|
427
501
|
return;
|
|
428
|
-
const client =
|
|
502
|
+
const client = previous.route.createClient(previous.target);
|
|
429
503
|
const started = await client.execute("health", {});
|
|
430
504
|
if (started.ok)
|
|
431
|
-
this.#active = {
|
|
505
|
+
this.#active = {
|
|
506
|
+
target: previous.target,
|
|
507
|
+
client,
|
|
508
|
+
profile: previous.profile,
|
|
509
|
+
compatibility: previous.compatibility,
|
|
510
|
+
route: previous.route,
|
|
511
|
+
};
|
|
432
512
|
else
|
|
433
513
|
await client.close();
|
|
434
514
|
}
|
|
@@ -459,3 +539,40 @@ export class BinarySession {
|
|
|
459
539
|
}
|
|
460
540
|
}
|
|
461
541
|
const isAborted = (signal) => signal?.aborted === true;
|
|
542
|
+
const commitExecutionProfile = (operation, result, profile) => {
|
|
543
|
+
if (!result.ok || profile === undefined)
|
|
544
|
+
return result;
|
|
545
|
+
const provider = result.value.provider;
|
|
546
|
+
if (provider.id !== profile.provider.id ||
|
|
547
|
+
provider.name !== profile.provider.name ||
|
|
548
|
+
provider.version !== profile.provider.version)
|
|
549
|
+
return err(new ProviderAdapterError(profile.provider.id, `${operation}:profile`));
|
|
550
|
+
return ok({
|
|
551
|
+
...result.value,
|
|
552
|
+
analysisProfile: structuredClone(profile),
|
|
553
|
+
});
|
|
554
|
+
};
|
|
555
|
+
const capabilityStatus = (descriptor) => ({
|
|
556
|
+
operation: descriptor.operation,
|
|
557
|
+
available: descriptor.available,
|
|
558
|
+
reason: descriptor.reason,
|
|
559
|
+
input_contract_version: descriptor.inputContractVersion,
|
|
560
|
+
output_contract_version: descriptor.outputContractVersion,
|
|
561
|
+
pagination: descriptor.pagination,
|
|
562
|
+
exhaustive: descriptor.exhaustive,
|
|
563
|
+
effects: {
|
|
564
|
+
mutates_artifact: descriptor.effects.mutatesArtifact,
|
|
565
|
+
launches_process: descriptor.effects.launchesProcess,
|
|
566
|
+
may_show_ui: descriptor.effects.mayShowUi,
|
|
567
|
+
may_access_network: descriptor.effects.mayAccessNetwork,
|
|
568
|
+
may_write_filesystem: descriptor.effects.mayWriteFilesystem,
|
|
569
|
+
changes_permissions: descriptor.effects.changesPermissions,
|
|
570
|
+
requires_root: descriptor.effects.requiresRoot,
|
|
571
|
+
},
|
|
572
|
+
limits: {
|
|
573
|
+
max_results: descriptor.limits.maxResults,
|
|
574
|
+
max_payload_bytes: descriptor.limits.maxPayloadBytes,
|
|
575
|
+
timeout_ms: descriptor.limits.timeoutMs,
|
|
576
|
+
},
|
|
577
|
+
limitations: [...descriptor.limitations],
|
|
578
|
+
});
|
|
@@ -44,6 +44,7 @@ export const buildCapabilityInventory = (sessionStatus, policy) => {
|
|
|
44
44
|
available: availability.reason === "available",
|
|
45
45
|
reason: availability.reason,
|
|
46
46
|
remediation: availability.remediation,
|
|
47
|
+
effects: { ...contract.effects },
|
|
47
48
|
annotations: {
|
|
48
49
|
read_only: contract.annotations.readOnlyHint ?? false,
|
|
49
50
|
destructive: contract.annotations.destructiveHint ?? false,
|
|
@@ -59,6 +60,23 @@ const availabilityFor = (name, kind, targetOpen, targetKind, descriptors, policy
|
|
|
59
60
|
reason: "policy_disabled",
|
|
60
61
|
remediation: "Enable or grant process_capture within the administrator ceiling.",
|
|
61
62
|
};
|
|
63
|
+
if (name === "run_controlled_replay" && !policy.javascriptReplayEnabled)
|
|
64
|
+
return {
|
|
65
|
+
reason: "policy_disabled",
|
|
66
|
+
remediation: "Enable javascript_replay with exact source roots and sandbox executables.",
|
|
67
|
+
};
|
|
68
|
+
if (name === "run_controlled_replay")
|
|
69
|
+
return { reason: "available", remediation: null };
|
|
70
|
+
if (name === "plan_managed_runtime_correlation" &&
|
|
71
|
+
!policy.managedRuntimeEnabled)
|
|
72
|
+
return {
|
|
73
|
+
reason: "policy_disabled",
|
|
74
|
+
remediation: "Enable managed_runtime with exact artifact roots and a runtime executable.",
|
|
75
|
+
};
|
|
76
|
+
if (name === "plan_managed_runtime_correlation")
|
|
77
|
+
return { reason: "available", remediation: null };
|
|
78
|
+
if (kind === "application")
|
|
79
|
+
return { reason: "available", remediation: null };
|
|
62
80
|
if (name === "import_evidence_bundle" && policy.evidenceFileRoots === 0)
|
|
63
81
|
return {
|
|
64
82
|
reason: "policy_disabled",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnalysisCapabilityUnavailableError } from "../domain/errors.js";
|
|
2
2
|
import { err, ok } from "../domain/result.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/** Synthetic compatibility identity for a deterministic provider set. */
|
|
4
|
+
export const compositeProviderIdentity = (identities) => ({
|
|
5
|
+
id: `composite:${identities
|
|
6
|
+
.map(({ id }) => id)
|
|
6
7
|
.sort()
|
|
7
8
|
.join("+")}`,
|
|
8
9
|
name: "REA composite analysis provider",
|
|
@@ -14,11 +15,16 @@ export class CompositeProvider {
|
|
|
14
15
|
#identity;
|
|
15
16
|
#capabilities;
|
|
16
17
|
#providerByOperation;
|
|
18
|
+
#profileProvider;
|
|
17
19
|
constructor(providers) {
|
|
18
20
|
this.providers = providers;
|
|
19
21
|
if (providers.length === 0)
|
|
20
22
|
throw new RangeError("CompositeProvider requires at least one provider");
|
|
21
|
-
|
|
23
|
+
const profileProviders = providers.filter(({ resolveAnalysisProfile }) => resolveAnalysisProfile !== undefined);
|
|
24
|
+
if (profileProviders.length > 1)
|
|
25
|
+
throw new TypeError("CompositeProvider supports at most one target-bound analysis profile");
|
|
26
|
+
this.#profileProvider = profileProviders[0];
|
|
27
|
+
this.#identity = Object.freeze(compositeProviderIdentity(providers.map((provider) => provider.identity())));
|
|
22
28
|
const routes = new Map();
|
|
23
29
|
const capabilities = [];
|
|
24
30
|
for (const provider of providers) {
|
|
@@ -39,13 +45,19 @@ export class CompositeProvider {
|
|
|
39
45
|
capabilities() {
|
|
40
46
|
return this.#capabilities;
|
|
41
47
|
}
|
|
42
|
-
|
|
48
|
+
resolveAnalysisProfile(target, options) {
|
|
49
|
+
const resolve = this.#profileProvider?.resolveAnalysisProfile;
|
|
50
|
+
return resolve === undefined
|
|
51
|
+
? Promise.resolve(ok({ profile: null, compatibility: {} }))
|
|
52
|
+
: resolve.call(this.#profileProvider, target, options);
|
|
53
|
+
}
|
|
54
|
+
createClient(target, profile) {
|
|
43
55
|
const clients = new Map();
|
|
44
56
|
const clientFor = (provider) => {
|
|
45
57
|
const existing = clients.get(provider);
|
|
46
58
|
if (existing !== undefined)
|
|
47
59
|
return existing;
|
|
48
|
-
const created = provider.createClient(target);
|
|
60
|
+
const created = provider.createClient(target, profile?.provider.id === provider.identity().id ? profile : undefined);
|
|
49
61
|
clients.set(provider, created);
|
|
50
62
|
return created;
|
|
51
63
|
};
|
|
@@ -62,7 +74,7 @@ export class CompositeProvider {
|
|
|
62
74
|
}));
|
|
63
75
|
const provider = this.#providerByOperation.get(operation);
|
|
64
76
|
return provider === undefined
|
|
65
|
-
? Promise.resolve(err(new
|
|
77
|
+
? Promise.resolve(err(new AnalysisCapabilityUnavailableError(this.#identity.id, operation, "operation is not declared by this provider set")))
|
|
66
78
|
: clientFor(provider).execute(operation, parameters, options);
|
|
67
79
|
},
|
|
68
80
|
close: async () => {
|
|
@@ -14,16 +14,20 @@ export const runCrossVersionInvestigation = async (input, policy, execution) =>
|
|
|
14
14
|
return err(new AnalysisInputError("find_changed_behavior", {
|
|
15
15
|
cause: parsed.error,
|
|
16
16
|
}));
|
|
17
|
-
|
|
17
|
+
return runCrossVersionInvestigationValidated(parsed.data, policy, execution);
|
|
18
|
+
};
|
|
19
|
+
/** Run input already parsed by a trusted adapter boundary. */
|
|
20
|
+
export const runCrossVersionInvestigationValidated = async (input, policy, execution) => {
|
|
21
|
+
const initial = await readInvestigationWorkspace(input.workspace_path, policy);
|
|
18
22
|
if (!initial.ok)
|
|
19
23
|
return initial;
|
|
20
|
-
const preflight = validateWorkspaceRequest(initial.value,
|
|
24
|
+
const preflight = validateWorkspaceRequest(initial.value, input);
|
|
21
25
|
if (!preflight.ok)
|
|
22
26
|
return preflight;
|
|
23
27
|
if (isCancelled(execution.signal))
|
|
24
28
|
return err(new AnalysisCancelledError("find_changed_behavior"));
|
|
25
|
-
if (
|
|
26
|
-
const replay = selectCompletedInvestigationReplay(initial.value,
|
|
29
|
+
if (input.replay_run_id !== undefined) {
|
|
30
|
+
const replay = selectCompletedInvestigationReplay(initial.value, input);
|
|
27
31
|
if (!replay.ok)
|
|
28
32
|
return replay;
|
|
29
33
|
return completeOutcome(replay.value.workspace, replay.value.run, true, execution.session);
|
|
@@ -39,7 +43,7 @@ export const runCrossVersionInvestigation = async (input, policy, execution) =>
|
|
|
39
43
|
total: 4,
|
|
40
44
|
message: "Scanning both version inputs",
|
|
41
45
|
});
|
|
42
|
-
const snapshots = await scanVersions(
|
|
46
|
+
const snapshots = await scanVersions(input, execution.inputRoots, execution.signal, execution.integrityContinueEnabled);
|
|
43
47
|
if (!snapshots.ok)
|
|
44
48
|
return snapshots;
|
|
45
49
|
await execution.progress?.report({
|
|
@@ -49,7 +53,7 @@ export const runCrossVersionInvestigation = async (input, policy, execution) =>
|
|
|
49
53
|
message: "Version inputs scanned",
|
|
50
54
|
});
|
|
51
55
|
return continueInvestigation({
|
|
52
|
-
input
|
|
56
|
+
input,
|
|
53
57
|
initial: initial.value,
|
|
54
58
|
snapshots: snapshots.value,
|
|
55
59
|
policy,
|