rea-agents 2.4.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/BinarySession.js +15 -4
- package/dist/application/BinarySessionStatus.js +31 -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 +68 -12
- 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/ProcessReactiveEffects.js +6 -0
- package/dist/application/ProcessReactiveHarness.js +16 -1
- 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 +37 -0
- package/dist/domain/analysisErrorProjection.js +8 -0
- package/dist/domain/artifactInspection.js +347 -0
- 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/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 +8 -2
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +9 -4
- 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/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/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 +2 -2
|
@@ -2,9 +2,10 @@ import { z } from "zod";
|
|
|
2
2
|
import { processCaptureComparisonSchema, processCaptureSchema, } from "../domain/processCapture.js";
|
|
3
3
|
import { evidenceBundleSchema } from "../domain/evidenceBundle.js";
|
|
4
4
|
import { residualUnknownSchema } from "../domain/residualUnknown.js";
|
|
5
|
-
import { referenceKindSchema } from "../domain/hopperValues.js";
|
|
5
|
+
import { functionInstructionWindowSchema, referenceKindSchema, } from "../domain/hopperValues.js";
|
|
6
6
|
import { demangleSwiftSchema, inspectMachoSchema, inspectPlistSchema, inspectSignatureSchema, listArchitecturesSchema, } from "../domain/nativeInspection.js";
|
|
7
7
|
import { artifactExtractionResultSchema, artifactInventoryResultSchema, } from "../domain/artifactGraph.js";
|
|
8
|
+
import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
|
|
8
9
|
import { managedArtifactInspectionSchema, managedMemberInspectionSchema, managedNativeBoundaryInspectionSchema, } from "../domain/managedArtifact.js";
|
|
9
10
|
import { managedMemberComparisonResultSchema } from "../domain/managedMemberComparison.js";
|
|
10
11
|
import { managedNativeVerificationResultSchema } from "../domain/managedNativeVerification.js";
|
|
@@ -20,7 +21,7 @@ import { staticRuntimeCorrelationResultSchema } from "../domain/staticRuntimeCor
|
|
|
20
21
|
import { reconstructionVerificationResultSchema } from "../domain/reconstructionVerification.js";
|
|
21
22
|
import { replayMachineRunOutputSchema } from "../domain/replayMachineRun.js";
|
|
22
23
|
import { analysisErrorProjectionSchema } from "./errorSchemas.js";
|
|
23
|
-
import { addressList, addressedEntry, bounded, containingProcedureResolution, functionDossierOutput, graphNode, lifecycleResultOf, nullableText, pageOutput, procedureIdentity, procedureInfoOutput, evidenceResultOf as resultOf, searchPageOutput, segmentOutput, sessionProvider, providerIdentity, toolAvailability, symbolDiscoveryOutput, targetFormatSchema, targetKindSchema, } from "./toolOutputSchemaPrimitives.js";
|
|
24
|
+
import { addressList, analysisActivity, addressedEntry, bounded, containingProcedureResolution, clientFeatureAvailabilitySchema, functionDossierOutput, graphNode, lifecycleResultOf, nullableText, pageOutput, procedureIdentity, procedureInfoOutput, evidenceResultOf as resultOf, searchPageOutput, segmentOutput, sessionProvider, providerIdentity, toolAvailability, symbolDiscoveryOutput, targetFormatSchema, targetKindSchema, } from "./toolOutputSchemaPrimitives.js";
|
|
24
25
|
/** Exact structured-content schemas shared by direct analysis providers. */
|
|
25
26
|
export const officialOutputSchemas = {
|
|
26
27
|
address_name: resultOf(nullableText),
|
|
@@ -43,6 +44,18 @@ export const officialOutputSchemas = {
|
|
|
43
44
|
procedure_callees: resultOf(addressList),
|
|
44
45
|
procedure_callers: resultOf(addressList),
|
|
45
46
|
procedure_info: procedureInfoOutput,
|
|
47
|
+
read_function_instructions: resultOf(functionInstructionWindowSchema),
|
|
48
|
+
read_bytes: resultOf(z.object({
|
|
49
|
+
address: z.string(),
|
|
50
|
+
requested_bytes: z.number().int().min(1).max(4_096),
|
|
51
|
+
returned_bytes: z.number().int().min(0).max(4_096),
|
|
52
|
+
bytes_hex: z.string().regex(/^(?:[a-f0-9]{2})*$/u),
|
|
53
|
+
complete: z.boolean(),
|
|
54
|
+
})),
|
|
55
|
+
address_to_file_offset: resultOf(z.object({
|
|
56
|
+
address: z.string(),
|
|
57
|
+
file_offset: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
|
|
58
|
+
})),
|
|
46
59
|
procedure_references: resultOf(z.object({
|
|
47
60
|
procedure: procedureIdentity,
|
|
48
61
|
direction: z.enum(["incoming", "outgoing"]),
|
|
@@ -69,6 +82,55 @@ export const officialOutputSchemas = {
|
|
|
69
82
|
unset_bookmark: resultOf(z.boolean()),
|
|
70
83
|
xrefs: resultOf(addressList),
|
|
71
84
|
};
|
|
85
|
+
const literalTraceOutput = resultOf(z.object({
|
|
86
|
+
query: z.string(),
|
|
87
|
+
search_mode: z.literal("literal"),
|
|
88
|
+
operations_used: z.number().int().min(0),
|
|
89
|
+
operation_budget: z.number().int().min(1),
|
|
90
|
+
matches: z.array(z.object({
|
|
91
|
+
type: z.enum(["string", "procedure"]),
|
|
92
|
+
address: z.string(),
|
|
93
|
+
value: z.string(),
|
|
94
|
+
})),
|
|
95
|
+
references: z.array(z.object({
|
|
96
|
+
target_address: z.string(),
|
|
97
|
+
source_address: z.string(),
|
|
98
|
+
containing_procedure: containingProcedureResolution,
|
|
99
|
+
})),
|
|
100
|
+
truncated: z.boolean(),
|
|
101
|
+
residual_unknowns: z.array(z.string()),
|
|
102
|
+
}));
|
|
103
|
+
const callPathTraceOutput = resultOf(z.object({
|
|
104
|
+
start: z.string(),
|
|
105
|
+
goal: z.string().nullable(),
|
|
106
|
+
direction: z.enum(["forward", "backward"]),
|
|
107
|
+
goal_status: z.enum(["not_requested", "reached", "not_reached"]),
|
|
108
|
+
nodes: z.array(z.object({
|
|
109
|
+
address: z.string(),
|
|
110
|
+
depth: z.number().int().min(0),
|
|
111
|
+
})),
|
|
112
|
+
edges: z.array(z.object({
|
|
113
|
+
source_address: z.string(),
|
|
114
|
+
target_address: z.string(),
|
|
115
|
+
discovery_depth: z.number().int().min(1),
|
|
116
|
+
})),
|
|
117
|
+
traversal_path: z.array(z.string()),
|
|
118
|
+
failures: z.array(z.object({
|
|
119
|
+
address: z.string(),
|
|
120
|
+
error: analysisErrorProjectionSchema,
|
|
121
|
+
})),
|
|
122
|
+
frontier: z.array(z.string()),
|
|
123
|
+
limits: z.object({
|
|
124
|
+
max_depth: z.number().int().min(1),
|
|
125
|
+
max_nodes: z.number().int().min(1),
|
|
126
|
+
max_operations: z.number().int().min(1),
|
|
127
|
+
nodes_visited: z.number().int().min(1),
|
|
128
|
+
operations_used: z.number().int().min(0),
|
|
129
|
+
}),
|
|
130
|
+
truncated: z.boolean(),
|
|
131
|
+
residual_unknowns: z.array(z.string()),
|
|
132
|
+
limitations: z.array(z.string()),
|
|
133
|
+
}));
|
|
72
134
|
/** Exact structured-content schemas for composed analysis workflows. */
|
|
73
135
|
export const enhancedOutputSchemas = {
|
|
74
136
|
swift_classes: symbolDiscoveryOutput("classes"),
|
|
@@ -126,24 +188,9 @@ export const enhancedOutputSchemas = {
|
|
|
126
188
|
string_count: z.number().int().min(0),
|
|
127
189
|
})),
|
|
128
190
|
analyze_function: functionDossierOutput,
|
|
129
|
-
trace_feature:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
operations_used: z.number().int().min(0),
|
|
133
|
-
operation_budget: z.number().int().min(1),
|
|
134
|
-
matches: z.array(z.object({
|
|
135
|
-
type: z.enum(["string", "procedure"]),
|
|
136
|
-
address: z.string(),
|
|
137
|
-
value: z.string(),
|
|
138
|
-
})),
|
|
139
|
-
references: z.array(z.object({
|
|
140
|
-
target_address: z.string(),
|
|
141
|
-
source_address: z.string(),
|
|
142
|
-
containing_procedure: containingProcedureResolution,
|
|
143
|
-
})),
|
|
144
|
-
truncated: z.boolean(),
|
|
145
|
-
residual_unknowns: z.array(z.string()),
|
|
146
|
-
})),
|
|
191
|
+
trace_feature: literalTraceOutput,
|
|
192
|
+
find_code_for_string: literalTraceOutput,
|
|
193
|
+
trace_call_path: callPathTraceOutput,
|
|
147
194
|
};
|
|
148
195
|
/** Exact Evidence v2 schemas for provider-neutral native inspection. */
|
|
149
196
|
export const nativeOutputSchemas = {
|
|
@@ -156,6 +203,7 @@ export const nativeOutputSchemas = {
|
|
|
156
203
|
/** Exact Evidence v2 schemas for provider-neutral artifact graph operations. */
|
|
157
204
|
export const artifactOutputSchemas = {
|
|
158
205
|
inventory_artifact: resultOf(artifactInventoryResultSchema),
|
|
206
|
+
inspect_artifact: resultOf(artifactInspectionResultSchema),
|
|
159
207
|
extract_artifact: resultOf(artifactExtractionResultSchema),
|
|
160
208
|
};
|
|
161
209
|
/** Exact Evidence v2 schema for execution-free managed static analysis. */
|
|
@@ -210,6 +258,7 @@ export const sessionOutputSchemas = {
|
|
|
210
258
|
reasons: z.array(z.string()),
|
|
211
259
|
remediation: z.string().nullable(),
|
|
212
260
|
}),
|
|
261
|
+
analysis_activity: analysisActivity,
|
|
213
262
|
recommended_actions: z.array(z.string()),
|
|
214
263
|
}),
|
|
215
264
|
z.object({
|
|
@@ -226,6 +275,7 @@ export const sessionOutputSchemas = {
|
|
|
226
275
|
next_cursor: z.number().int().min(0).nullable(),
|
|
227
276
|
has_more: z.boolean(),
|
|
228
277
|
}),
|
|
278
|
+
client_features: clientFeatureAvailabilitySchema,
|
|
229
279
|
}),
|
|
230
280
|
z.union([
|
|
231
281
|
sessionProvider.extend({
|
|
@@ -111,6 +111,22 @@ const analysisRun = z
|
|
|
111
111
|
]),
|
|
112
112
|
})
|
|
113
113
|
.nullable();
|
|
114
|
+
export const analysisActivity = z.object({
|
|
115
|
+
status: z.enum(["not_observed", "idle", "busy", "timed_out_busy"]),
|
|
116
|
+
providers: z.array(z.object({
|
|
117
|
+
provider: providerIdentity,
|
|
118
|
+
active: z
|
|
119
|
+
.object({
|
|
120
|
+
request_id: z.number().int().min(1),
|
|
121
|
+
operation: z.string().min(1),
|
|
122
|
+
elapsed_ms: z.number().int().min(0),
|
|
123
|
+
timeout_ms: z.number().int().min(0),
|
|
124
|
+
caller_state: z.enum(["waiting", "timed_out", "cancelled"]),
|
|
125
|
+
})
|
|
126
|
+
.nullable(),
|
|
127
|
+
queued_requests: z.number().int().min(0),
|
|
128
|
+
})),
|
|
129
|
+
});
|
|
114
130
|
const providerRejectionCode = z.enum(PROVIDER_REJECTION_CODES);
|
|
115
131
|
const providerDiagnostics = z.record(z.string(), z.json());
|
|
116
132
|
const providerAvailability = z.discriminatedUnion("status", [
|
|
@@ -153,12 +169,26 @@ const providerTargetSupport = z.discriminatedUnion("status", [
|
|
|
153
169
|
diagnostics: providerDiagnostics,
|
|
154
170
|
}),
|
|
155
171
|
]);
|
|
172
|
+
const clientFeatureNameSchema = z.enum([
|
|
173
|
+
"elicitation_form",
|
|
174
|
+
"elicitation_url",
|
|
175
|
+
"roots",
|
|
176
|
+
"sampling",
|
|
177
|
+
]);
|
|
178
|
+
/** Negotiated MCP client features that affect REA workflow availability. */
|
|
179
|
+
export const clientFeatureAvailabilitySchema = z.object({
|
|
180
|
+
elicitation_form: z.boolean(),
|
|
181
|
+
elicitation_url: z.boolean(),
|
|
182
|
+
roots: z.boolean(),
|
|
183
|
+
sampling: z.boolean(),
|
|
184
|
+
});
|
|
156
185
|
export const toolAvailability = z.object({
|
|
157
186
|
name: z.string(),
|
|
158
187
|
surface: z.string(),
|
|
159
188
|
available: z.boolean(),
|
|
160
189
|
reason: z.enum([
|
|
161
190
|
"available",
|
|
191
|
+
"client_capability_missing",
|
|
162
192
|
"target_required",
|
|
163
193
|
"provider_missing",
|
|
164
194
|
"provider_unavailable",
|
|
@@ -167,6 +197,12 @@ export const toolAvailability = z.object({
|
|
|
167
197
|
"policy_disabled",
|
|
168
198
|
]),
|
|
169
199
|
remediation: z.string().nullable(),
|
|
200
|
+
client_requirements: z.object({
|
|
201
|
+
required: z.array(clientFeatureNameSchema),
|
|
202
|
+
optional: z.array(clientFeatureNameSchema),
|
|
203
|
+
missing_required: z.array(clientFeatureNameSchema),
|
|
204
|
+
missing_optional: z.array(clientFeatureNameSchema),
|
|
205
|
+
}),
|
|
170
206
|
effects: z.strictObject({
|
|
171
207
|
mutatesTarget: z.boolean(),
|
|
172
208
|
mutatesSession: z.boolean(),
|
|
@@ -189,6 +225,7 @@ export const sessionProvider = z.object({
|
|
|
189
225
|
providers: z.array(providerIdentity),
|
|
190
226
|
capabilities: z.array(providerCapability),
|
|
191
227
|
analysis_run: analysisRun,
|
|
228
|
+
analysis_activity: analysisActivity,
|
|
192
229
|
analysis_provider_binding: z
|
|
193
230
|
.object({
|
|
194
231
|
provider: providerIdentity,
|
|
@@ -21,6 +21,8 @@ export const projectAnalysisError = (error) => {
|
|
|
21
21
|
};
|
|
22
22
|
};
|
|
23
23
|
const errorCode = (error) => {
|
|
24
|
+
if (error.cleanupIncomplete)
|
|
25
|
+
return "cleanup_incomplete";
|
|
24
26
|
if (error instanceof ReplayPlanStaleError)
|
|
25
27
|
return "plan_stale";
|
|
26
28
|
if (error instanceof PermissionRequiredError)
|
|
@@ -221,6 +223,12 @@ const providerErrorDetails = (error) => {
|
|
|
221
223
|
return {
|
|
222
224
|
provider_id: error.providerId,
|
|
223
225
|
operation: error.operation,
|
|
226
|
+
...(error.cleanupIncomplete
|
|
227
|
+
? {
|
|
228
|
+
cleanup: "incomplete",
|
|
229
|
+
resources: [...error.cleanupResources],
|
|
230
|
+
}
|
|
231
|
+
: {}),
|
|
224
232
|
...(error.diagnostics === undefined
|
|
225
233
|
? {}
|
|
226
234
|
: { diagnostics: error.diagnostics }),
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import canonicalize from "canonicalize";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { artifactInventoryResultSchema, } from "./artifactGraph.js";
|
|
5
|
+
import { evidenceSchema, parseEvidence } from "./evidence.js";
|
|
6
|
+
import { jsonValueSchema } from "./jsonValue.js";
|
|
7
|
+
const boundedTextSchema = z.string().min(1).max(4_096);
|
|
8
|
+
const evidenceIdSchema = z.string().regex(/^ev_[a-f0-9]{64}$/u);
|
|
9
|
+
const artifactIdSchema = z.string().regex(/^art_[a-f0-9]{64}$/u);
|
|
10
|
+
const inspectionLimitsSchema = z.strictObject({
|
|
11
|
+
max_observations: z.number().int().min(1).max(500).default(100),
|
|
12
|
+
max_relationships: z.number().int().min(1).max(500).default(100),
|
|
13
|
+
max_hypotheses: z.number().int().min(1).max(100).default(20),
|
|
14
|
+
max_unexplored_branches: z.number().int().min(1).max(100).default(20),
|
|
15
|
+
max_next_probes: z.number().int().min(1).max(50).default(20),
|
|
16
|
+
max_substeps: z.literal(1).default(1),
|
|
17
|
+
});
|
|
18
|
+
export const artifactInspectionLimitsSchema = inspectionLimitsSchema;
|
|
19
|
+
const inspectionObservationSchema = z.strictObject({
|
|
20
|
+
observation_id: z.string().regex(/^aio_[a-f0-9]{64}$/u),
|
|
21
|
+
kind: z.enum(["root-manifest", "artifact", "occurrence", "integrity"]),
|
|
22
|
+
subject: boundedTextSchema,
|
|
23
|
+
value: jsonValueSchema,
|
|
24
|
+
evidence_id: evidenceIdSchema,
|
|
25
|
+
});
|
|
26
|
+
const inspectionRelationshipSchema = z.strictObject({
|
|
27
|
+
relationship_id: z.string().regex(/^air_[a-f0-9]{64}$/u),
|
|
28
|
+
relation: z.enum([
|
|
29
|
+
"contains",
|
|
30
|
+
"extracts",
|
|
31
|
+
"slice-of",
|
|
32
|
+
"embeds",
|
|
33
|
+
"loads",
|
|
34
|
+
"maps-source",
|
|
35
|
+
"derived-from",
|
|
36
|
+
]),
|
|
37
|
+
source_artifact_id: artifactIdSchema,
|
|
38
|
+
target_artifact_id: artifactIdSchema,
|
|
39
|
+
occurrence_id: z.string().regex(/^occ_[a-f0-9]{64}$/u),
|
|
40
|
+
logical_path: boundedTextSchema.nullable(),
|
|
41
|
+
evidence_id: evidenceIdSchema,
|
|
42
|
+
});
|
|
43
|
+
const inspectionHypothesisSchema = z.strictObject({
|
|
44
|
+
hypothesis_id: z.string().regex(/^aih_[a-f0-9]{64}$/u),
|
|
45
|
+
statement: boundedTextSchema,
|
|
46
|
+
confidence: z.enum(["medium", "low"]),
|
|
47
|
+
basis_evidence_ids: z.array(evidenceIdSchema).min(1).max(8),
|
|
48
|
+
limitation: boundedTextSchema,
|
|
49
|
+
});
|
|
50
|
+
const inspectionContradictionSchema = z.strictObject({
|
|
51
|
+
contradiction_id: z.string().regex(/^aic_[a-f0-9]{64}$/u),
|
|
52
|
+
statement: boundedTextSchema,
|
|
53
|
+
occurrence_id: z.string().regex(/^occ_[a-f0-9]{64}$/u),
|
|
54
|
+
declared_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
55
|
+
observed_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
56
|
+
evidence_id: evidenceIdSchema,
|
|
57
|
+
});
|
|
58
|
+
const nextProbeSchema = z.strictObject({
|
|
59
|
+
operation: z.string().min(1).max(128),
|
|
60
|
+
rationale: boundedTextSchema,
|
|
61
|
+
authority: z.enum([
|
|
62
|
+
"artifact-bytes",
|
|
63
|
+
"managed-static-analysis",
|
|
64
|
+
"native-analysis-provider",
|
|
65
|
+
"ast-static-analysis",
|
|
66
|
+
]),
|
|
67
|
+
});
|
|
68
|
+
const unexploredBranchSchema = z.strictObject({
|
|
69
|
+
branch_id: z.string().regex(/^aib_[a-f0-9]{64}$/u),
|
|
70
|
+
reason: z.enum([
|
|
71
|
+
"page-limit",
|
|
72
|
+
"format-specific-analysis-required",
|
|
73
|
+
"unknown-format",
|
|
74
|
+
]),
|
|
75
|
+
detail: boundedTextSchema,
|
|
76
|
+
next_probe: nextProbeSchema.nullable(),
|
|
77
|
+
evidence_id: evidenceIdSchema,
|
|
78
|
+
});
|
|
79
|
+
/** Provider-neutral inspection result with its atomic source Evidence. */
|
|
80
|
+
export const artifactInspectionResultSchema = z.strictObject({
|
|
81
|
+
schema_version: z.literal(1),
|
|
82
|
+
inspection_id: z.string().regex(/^ai_[a-f0-9]{64}$/u),
|
|
83
|
+
subject: z.strictObject({
|
|
84
|
+
manifest_id: z.string().regex(/^agm_[a-f0-9]{64}$/u),
|
|
85
|
+
root_artifact_id: artifactIdSchema,
|
|
86
|
+
root_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
87
|
+
root_format: z.string().min(1).max(128),
|
|
88
|
+
}),
|
|
89
|
+
substeps: z
|
|
90
|
+
.array(z.strictObject({
|
|
91
|
+
substep_id: z.string().regex(/^ais_[a-f0-9]{64}$/u),
|
|
92
|
+
operation: z.literal("inventory_artifact"),
|
|
93
|
+
status: z.literal("completed"),
|
|
94
|
+
evidence_id: evidenceIdSchema,
|
|
95
|
+
evidence: evidenceSchema,
|
|
96
|
+
}))
|
|
97
|
+
.min(1)
|
|
98
|
+
.max(1),
|
|
99
|
+
observations: z.array(inspectionObservationSchema).max(500),
|
|
100
|
+
derived_relationships: z.array(inspectionRelationshipSchema).max(500),
|
|
101
|
+
hypotheses: z.array(inspectionHypothesisSchema).max(100),
|
|
102
|
+
contradictions: z.array(inspectionContradictionSchema).max(100),
|
|
103
|
+
unexplored_branches: z.array(unexploredBranchSchema).max(100),
|
|
104
|
+
next_probes: z.array(nextProbeSchema).max(50),
|
|
105
|
+
coverage: z.strictObject({
|
|
106
|
+
status: z.enum(["complete-within-substeps", "partial", "truncated"]),
|
|
107
|
+
substeps_completed: z.literal(1),
|
|
108
|
+
observations_retained: z.number().int().min(0),
|
|
109
|
+
observations_omitted: z.number().int().min(0),
|
|
110
|
+
relationships_retained: z.number().int().min(0),
|
|
111
|
+
relationships_omitted: z.number().int().min(0),
|
|
112
|
+
hypotheses_retained: z.number().int().min(0),
|
|
113
|
+
hypotheses_omitted: z.number().int().min(0),
|
|
114
|
+
unexplored_branches_retained: z.number().int().min(0),
|
|
115
|
+
unexplored_branches_omitted: z.number().int().min(0),
|
|
116
|
+
next_probes_retained: z.number().int().min(0),
|
|
117
|
+
next_probes_omitted: z.number().int().min(0),
|
|
118
|
+
}),
|
|
119
|
+
evidence_links: z.array(evidenceIdSchema).min(1).max(1),
|
|
120
|
+
limitations: z.array(boundedTextSchema).max(1_000),
|
|
121
|
+
});
|
|
122
|
+
/** Project one successful inventory Evidence record into an inspection report. */
|
|
123
|
+
export const createArtifactInspection = (inventoryInput, limits) => {
|
|
124
|
+
const inventoryEvidence = parseInventoryEvidence(inventoryInput);
|
|
125
|
+
const inventory = artifactInventoryResultSchema.parse(inventoryEvidence.normalized_result);
|
|
126
|
+
const observations = allObservations(inventory, inventoryEvidence.evidence_id);
|
|
127
|
+
const relationships = allRelationships(inventory, inventoryEvidence.evidence_id);
|
|
128
|
+
const hypotheses = allHypotheses(inventory, inventoryEvidence.evidence_id);
|
|
129
|
+
const nextProbes = probesFor(inventory);
|
|
130
|
+
const branches = allUnexploredBranches(inventory, inventoryEvidence.evidence_id, nextProbes);
|
|
131
|
+
const retained = {
|
|
132
|
+
observations: observations.slice(0, limits.max_observations),
|
|
133
|
+
relationships: relationships.slice(0, limits.max_relationships),
|
|
134
|
+
hypotheses: hypotheses.slice(0, limits.max_hypotheses),
|
|
135
|
+
branches: branches.slice(0, limits.max_unexplored_branches),
|
|
136
|
+
probes: nextProbes.slice(0, limits.max_next_probes),
|
|
137
|
+
};
|
|
138
|
+
const omissions = {
|
|
139
|
+
observations: observations.length - retained.observations.length,
|
|
140
|
+
relationships: relationships.length - retained.relationships.length,
|
|
141
|
+
hypotheses: hypotheses.length - retained.hypotheses.length,
|
|
142
|
+
branches: branches.length - retained.branches.length,
|
|
143
|
+
probes: nextProbes.length - retained.probes.length,
|
|
144
|
+
};
|
|
145
|
+
const semantic = {
|
|
146
|
+
schema_version: 1,
|
|
147
|
+
subject: {
|
|
148
|
+
manifest_id: inventory.manifest.manifest_id,
|
|
149
|
+
root_artifact_id: inventory.manifest.root_artifact_id,
|
|
150
|
+
root_sha256: inventory.manifest.root_sha256,
|
|
151
|
+
root_format: inventory.manifest.root_format,
|
|
152
|
+
},
|
|
153
|
+
substeps: [
|
|
154
|
+
{
|
|
155
|
+
substep_id: `ais_${digestCanonical({
|
|
156
|
+
operation: "inventory_artifact",
|
|
157
|
+
evidence_id: inventoryEvidence.evidence_id,
|
|
158
|
+
})}`,
|
|
159
|
+
operation: "inventory_artifact",
|
|
160
|
+
status: "completed",
|
|
161
|
+
evidence_id: inventoryEvidence.evidence_id,
|
|
162
|
+
evidence: inventoryEvidence,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
observations: retained.observations,
|
|
166
|
+
derived_relationships: retained.relationships,
|
|
167
|
+
hypotheses: retained.hypotheses,
|
|
168
|
+
contradictions: contradictions(inventory, inventoryEvidence.evidence_id),
|
|
169
|
+
unexplored_branches: retained.branches,
|
|
170
|
+
next_probes: retained.probes,
|
|
171
|
+
coverage: {
|
|
172
|
+
status: inspectionCoverage(inventory, omissions),
|
|
173
|
+
substeps_completed: 1,
|
|
174
|
+
observations_retained: retained.observations.length,
|
|
175
|
+
observations_omitted: omissions.observations,
|
|
176
|
+
relationships_retained: retained.relationships.length,
|
|
177
|
+
relationships_omitted: omissions.relationships,
|
|
178
|
+
hypotheses_retained: retained.hypotheses.length,
|
|
179
|
+
hypotheses_omitted: omissions.hypotheses,
|
|
180
|
+
unexplored_branches_retained: retained.branches.length,
|
|
181
|
+
unexplored_branches_omitted: omissions.branches,
|
|
182
|
+
next_probes_retained: retained.probes.length,
|
|
183
|
+
next_probes_omitted: omissions.probes,
|
|
184
|
+
},
|
|
185
|
+
evidence_links: [inventoryEvidence.evidence_id],
|
|
186
|
+
limitations: uniqueSorted([
|
|
187
|
+
...inventory.limitations,
|
|
188
|
+
"Inspection derives bounded static hypotheses and next probes; it does not execute the artifact or claim format-specific semantics.",
|
|
189
|
+
]),
|
|
190
|
+
};
|
|
191
|
+
return artifactInspectionResultSchema.parse({
|
|
192
|
+
...semantic,
|
|
193
|
+
inspection_id: `ai_${digestCanonical(semantic)}`,
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
const parseInventoryEvidence = (input) => {
|
|
197
|
+
const evidence = parseEvidence(input);
|
|
198
|
+
if (evidence.operation !== "inventory_artifact")
|
|
199
|
+
throw new TypeError("Artifact inspection substep requires inventory_artifact Evidence");
|
|
200
|
+
return evidence;
|
|
201
|
+
};
|
|
202
|
+
const allObservations = (inventory, evidenceId) => [
|
|
203
|
+
observation("root-manifest", inventory.manifest.root_artifact_id, inventory.manifest, evidenceId),
|
|
204
|
+
...inventory.nodes.items.map((node) => observation("artifact", node.artifact_id, node, evidenceId)),
|
|
205
|
+
...inventory.occurrences.items.map((occurrence) => observation("occurrence", occurrence.occurrence_id, occurrence, evidenceId)),
|
|
206
|
+
...inventory.integrity_contradictions.map((value) => observation("integrity", value.contradiction_id, value, evidenceId)),
|
|
207
|
+
];
|
|
208
|
+
const observation = (kind, subject, value, evidenceId) => {
|
|
209
|
+
const semantic = {
|
|
210
|
+
kind,
|
|
211
|
+
subject,
|
|
212
|
+
value: jsonValueSchema.parse(value),
|
|
213
|
+
evidence_id: evidenceId,
|
|
214
|
+
};
|
|
215
|
+
return { observation_id: `aio_${digestCanonical(semantic)}`, ...semantic };
|
|
216
|
+
};
|
|
217
|
+
const allRelationships = (inventory, evidenceId) => inventory.edges.items.map((edge) => {
|
|
218
|
+
const semantic = {
|
|
219
|
+
relation: edge.relation,
|
|
220
|
+
source_artifact_id: edge.parent_artifact_id,
|
|
221
|
+
target_artifact_id: edge.child_artifact_id,
|
|
222
|
+
occurrence_id: edge.occurrence_id,
|
|
223
|
+
logical_path: edge.logical_path,
|
|
224
|
+
evidence_id: evidenceId,
|
|
225
|
+
};
|
|
226
|
+
return {
|
|
227
|
+
relationship_id: `air_${digestCanonical(semantic)}`,
|
|
228
|
+
...semantic,
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
const allHypotheses = (inventory, evidenceId) => {
|
|
232
|
+
const statements = inventory.manifest.root_format === "pe"
|
|
233
|
+
? [
|
|
234
|
+
"The PE artifact may contain managed CLI metadata or native code; inventory bytes alone do not distinguish them.",
|
|
235
|
+
]
|
|
236
|
+
: ["asar", "javascript-bundle"].includes(inventory.manifest.root_format)
|
|
237
|
+
? [
|
|
238
|
+
"The artifact may contain a recoverable JavaScript application graph.",
|
|
239
|
+
]
|
|
240
|
+
: [];
|
|
241
|
+
return statements.map((statement) => {
|
|
242
|
+
const semantic = {
|
|
243
|
+
statement,
|
|
244
|
+
confidence: "low",
|
|
245
|
+
basis_evidence_ids: [evidenceId],
|
|
246
|
+
limitation: "Format classification proposes a bounded follow-up; it is not semantic proof.",
|
|
247
|
+
};
|
|
248
|
+
return { hypothesis_id: `aih_${digestCanonical(semantic)}`, ...semantic };
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
const contradictions = (inventory, evidenceId) => inventory.integrity_contradictions.map((value) => {
|
|
252
|
+
const semantic = {
|
|
253
|
+
statement: `Declared integrity for ${value.logical_path} contradicts observed bytes.`,
|
|
254
|
+
occurrence_id: value.occurrence_id,
|
|
255
|
+
declared_sha256: value.declared_sha256,
|
|
256
|
+
observed_sha256: value.observed_sha256,
|
|
257
|
+
evidence_id: evidenceId,
|
|
258
|
+
};
|
|
259
|
+
return {
|
|
260
|
+
contradiction_id: `aic_${digestCanonical(semantic)}`,
|
|
261
|
+
...semantic,
|
|
262
|
+
};
|
|
263
|
+
});
|
|
264
|
+
const probesFor = (inventory) => {
|
|
265
|
+
const format = inventory.manifest.root_format;
|
|
266
|
+
if (["asar", "javascript-bundle", "directory"].includes(format))
|
|
267
|
+
return [
|
|
268
|
+
{
|
|
269
|
+
operation: "analyze_javascript_application",
|
|
270
|
+
rationale: "Recover bounded JavaScript modules, source maps, Electron boundaries, and application relationships.",
|
|
271
|
+
authority: "ast-static-analysis",
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
if (format === "pe")
|
|
275
|
+
return [
|
|
276
|
+
{
|
|
277
|
+
operation: "inspect_managed_artifact",
|
|
278
|
+
rationale: "Check execution-free CLI metadata before choosing a deep native provider.",
|
|
279
|
+
authority: "managed-static-analysis",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
operation: "open_binary",
|
|
283
|
+
rationale: "Open as native code only when managed inspection reports a native or mixed boundary.",
|
|
284
|
+
authority: "native-analysis-provider",
|
|
285
|
+
},
|
|
286
|
+
];
|
|
287
|
+
if (["mach-o", "mach-o-universal", "elf"].includes(format))
|
|
288
|
+
return [
|
|
289
|
+
{
|
|
290
|
+
operation: "open_binary",
|
|
291
|
+
rationale: "Bind one deep native provider before function-level analysis.",
|
|
292
|
+
authority: "native-analysis-provider",
|
|
293
|
+
},
|
|
294
|
+
];
|
|
295
|
+
if (format === "plist")
|
|
296
|
+
return [
|
|
297
|
+
{
|
|
298
|
+
operation: "inspect_plist",
|
|
299
|
+
rationale: "Parse property-list values without executing the artifact.",
|
|
300
|
+
authority: "artifact-bytes",
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
return [];
|
|
304
|
+
};
|
|
305
|
+
const allUnexploredBranches = (inventory, evidenceId, nextProbes) => {
|
|
306
|
+
const pages = [
|
|
307
|
+
["nodes", inventory.nodes.next_offset],
|
|
308
|
+
["occurrences", inventory.occurrences.next_offset],
|
|
309
|
+
["relationships", inventory.edges.next_offset],
|
|
310
|
+
];
|
|
311
|
+
const branches = pages.flatMap(([collection, nextOffset]) => nextOffset === null
|
|
312
|
+
? []
|
|
313
|
+
: [
|
|
314
|
+
branch("page-limit", `${collection} continue at offset ${String(nextOffset)}.`, null, evidenceId),
|
|
315
|
+
]);
|
|
316
|
+
branches.push(...nextProbes.map((probe) => branch("format-specific-analysis-required", `${probe.operation} was not run by provider-neutral artifact inspection.`, probe, evidenceId)));
|
|
317
|
+
if (inventory.manifest.root_format === "unknown")
|
|
318
|
+
branches.push(branch("unknown-format", "Artifact bytes did not match an admitted format classifier.", null, evidenceId));
|
|
319
|
+
return branches;
|
|
320
|
+
};
|
|
321
|
+
const branch = (reason, detail, nextProbe, evidenceId) => {
|
|
322
|
+
const semantic = {
|
|
323
|
+
reason,
|
|
324
|
+
detail,
|
|
325
|
+
next_probe: nextProbe,
|
|
326
|
+
evidence_id: evidenceId,
|
|
327
|
+
};
|
|
328
|
+
return { branch_id: `aib_${digestCanonical(semantic)}`, ...semantic };
|
|
329
|
+
};
|
|
330
|
+
const inspectionCoverage = (inventory, omissions) => {
|
|
331
|
+
if (Object.values(omissions).some((count) => count > 0))
|
|
332
|
+
return "truncated";
|
|
333
|
+
return [
|
|
334
|
+
inventory.nodes.next_offset,
|
|
335
|
+
inventory.occurrences.next_offset,
|
|
336
|
+
inventory.edges.next_offset,
|
|
337
|
+
].some((offset) => offset !== null)
|
|
338
|
+
? "partial"
|
|
339
|
+
: "complete-within-substeps";
|
|
340
|
+
};
|
|
341
|
+
const digestCanonical = (value) => {
|
|
342
|
+
const encoded = canonicalize(value);
|
|
343
|
+
if (encoded === undefined)
|
|
344
|
+
throw new TypeError("Artifact inspection could not canonicalize");
|
|
345
|
+
return createHash("sha256").update(encoded).digest("hex");
|
|
346
|
+
};
|
|
347
|
+
const uniqueSorted = (values) => [...new Set(values)].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { browserScenarioDiffSchema, compareBrowserScenarios, compareBrowserScenariosInputSchema, } from "./browserScenarioDiff.js";
|
|
3
|
+
import { compareWebCaptures, captureSnapshotSchema, webCaptureDiffSchema, } from "./webCaptureDiff.js";
|
|
4
|
+
/** Top-level MCP shape for legacy page or step-indexed scenario comparison. */
|
|
5
|
+
export const browserCaptureComparisonWireSchema = z
|
|
6
|
+
.strictObject({
|
|
7
|
+
before: captureSnapshotSchema
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("Earlier passive web-page capture; pair with after."),
|
|
10
|
+
after: captureSnapshotSchema
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Later passive web-page capture; pair with before."),
|
|
13
|
+
before_scenario: compareBrowserScenariosInputSchema.shape.before_scenario
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Earlier browser scenario capture; pair with after_scenario."),
|
|
16
|
+
after_scenario: compareBrowserScenariosInputSchema.shape.after_scenario
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Later browser scenario capture; pair with before_scenario."),
|
|
19
|
+
normalization: compareBrowserScenariosInputSchema.shape.normalization.describe("Scenario-only exact-literal normalization policy."),
|
|
20
|
+
max_changes: z
|
|
21
|
+
.number()
|
|
22
|
+
.int()
|
|
23
|
+
.min(1)
|
|
24
|
+
.max(20_000)
|
|
25
|
+
.default(2_000)
|
|
26
|
+
.describe("Maximum normalized change records to retain."),
|
|
27
|
+
})
|
|
28
|
+
.superRefine((input, context) => {
|
|
29
|
+
const hasPassive = input.before !== undefined || input.after !== undefined;
|
|
30
|
+
const hasScenario = input.before_scenario !== undefined || input.after_scenario !== undefined;
|
|
31
|
+
if (hasPassive === hasScenario)
|
|
32
|
+
context.addIssue({
|
|
33
|
+
code: "custom",
|
|
34
|
+
message: "Provide exactly one complete pair: before/after or before_scenario/after_scenario",
|
|
35
|
+
});
|
|
36
|
+
if (hasPassive && (input.before === undefined || input.after === undefined))
|
|
37
|
+
context.addIssue({
|
|
38
|
+
code: "custom",
|
|
39
|
+
message: "Passive comparison requires both before and after",
|
|
40
|
+
});
|
|
41
|
+
if (hasScenario &&
|
|
42
|
+
(input.before_scenario === undefined ||
|
|
43
|
+
input.after_scenario === undefined))
|
|
44
|
+
context.addIssue({
|
|
45
|
+
code: "custom",
|
|
46
|
+
message: "Browser scenario comparison requires both before_scenario and after_scenario",
|
|
47
|
+
});
|
|
48
|
+
if (hasPassive && input.normalization.rules.length > 0)
|
|
49
|
+
context.addIssue({
|
|
50
|
+
code: "custom",
|
|
51
|
+
path: ["normalization"],
|
|
52
|
+
message: "Literal normalization rules apply only to browser scenario captures",
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
/** Parsed legacy page snapshots or step-indexed browser scenarios. */
|
|
56
|
+
export const browserCaptureComparisonInputSchema = browserCaptureComparisonWireSchema.transform((input) => {
|
|
57
|
+
if (input.before_scenario !== undefined &&
|
|
58
|
+
input.after_scenario !== undefined)
|
|
59
|
+
return {
|
|
60
|
+
before_scenario: input.before_scenario,
|
|
61
|
+
after_scenario: input.after_scenario,
|
|
62
|
+
normalization: input.normalization,
|
|
63
|
+
max_changes: input.max_changes,
|
|
64
|
+
};
|
|
65
|
+
if (input.before !== undefined && input.after !== undefined)
|
|
66
|
+
return {
|
|
67
|
+
before: input.before,
|
|
68
|
+
after: input.after,
|
|
69
|
+
max_changes: input.max_changes,
|
|
70
|
+
};
|
|
71
|
+
throw new TypeError("Capture comparison pair refinement failed");
|
|
72
|
+
});
|
|
73
|
+
/** Result from passive page or browser scenario comparison. */
|
|
74
|
+
export const browserCaptureComparisonSchema = z.union([
|
|
75
|
+
browserScenarioDiffSchema,
|
|
76
|
+
webCaptureDiffSchema,
|
|
77
|
+
]);
|
|
78
|
+
/** Dispatch a parsed capture comparison to its pure domain comparator. */
|
|
79
|
+
export const compareBrowserCaptures = (input) => isScenarioComparison(input)
|
|
80
|
+
? compareBrowserScenarios(input)
|
|
81
|
+
: compareWebCaptures(input);
|
|
82
|
+
const isScenarioComparison = (input) => "before_scenario" in input;
|