rea-agents 2.0.0 → 2.2.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 +103 -38
- package/bridge/ghidra/ReaGhidraBridge.java +96 -11
- package/dist/application/AndroidApplicationService.js +17 -0
- package/dist/application/AppleApplicationService.js +17 -0
- package/dist/application/ArtifactExtraction.js +1 -1
- package/dist/application/ArtifactInventory/classify.js +42 -0
- package/dist/application/ArtifactInventory/hash.js +43 -0
- package/dist/application/ArtifactInventory/reader.js +41 -0
- package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
- package/dist/application/ArtifactInventory/scanReader.js +124 -0
- package/dist/application/ArtifactInventory/types.js +10 -0
- package/dist/application/ArtifactInventory.js +9 -351
- package/dist/application/AuthorizedArtifactInventory.js +2 -2
- package/dist/application/BinarySession.js +47 -270
- package/dist/application/BinarySessionExecution.js +28 -0
- package/dist/application/BinarySessionOpen.js +39 -0
- package/dist/application/BinarySessionRecords.js +149 -0
- package/dist/application/BinarySessionStatus.js +75 -0
- package/dist/application/CapabilityInventory.js +24 -2
- package/dist/application/ClientRegistrationStatus.js +11 -4
- package/dist/application/CrossVersionInventory.js +8 -2
- package/dist/application/CrossVersionInvestigation.js +1 -23
- package/dist/application/CrossVersionInvestigationCollections.js +23 -0
- package/dist/application/DirectAnalysis.js +47 -44
- package/dist/application/Doctor.js +156 -238
- package/dist/application/DoctorDiagnostics.js +180 -0
- package/dist/application/ElectronBoundaryAnalysis.js +23 -7
- package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
- package/dist/application/EnhancedToolPagination.js +22 -0
- package/dist/application/EnhancedToolTypes.js +4 -0
- package/dist/application/EnhancedTools.js +20 -37
- package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
- package/dist/application/InventoryProjectionEvidence.js +41 -0
- package/dist/application/InvestigationProviders.js +12 -0
- package/dist/application/InvestigationWorkspaceStore.js +24 -10
- package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
- package/dist/application/JavaScriptArtifactFiles.js +6 -2
- package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
- package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
- package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
- package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
- package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
- package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
- package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
- package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
- package/dist/application/JavaScriptJsonModules.js +39 -0
- package/dist/application/JavaScriptModuleRelationships.js +262 -0
- package/dist/application/JavaScriptReplayPermission.js +16 -0
- package/dist/application/JavaScriptReplayService.js +182 -156
- package/dist/application/LinuxHopper.js +10 -0
- package/dist/application/MacHopper.js +22 -11
- package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
- package/dist/application/PermissionAuthority.js +106 -63
- package/dist/application/ProcessCaptureCapability.js +1 -1
- package/dist/application/ProcessCapturePermission.js +13 -0
- package/dist/application/ProcessCli.js +2 -12
- package/dist/application/ProcessHarness.js +1 -1
- package/dist/application/ProcessPairedExperiment.js +72 -0
- package/dist/application/ProjectPermissionStore.js +48 -9
- package/dist/application/RealHopperAssertions.js +76 -0
- package/dist/application/ReconstructionCoverageService.js +69 -0
- package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
- package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
- package/dist/application/Setup.js +148 -260
- package/dist/application/SetupClientConfiguration.js +224 -0
- package/dist/application/SetupClients.js +24 -2
- package/dist/application/SetupPlan.js +124 -12
- package/dist/application/SetupSkill.js +54 -36
- package/dist/application/SupportedClients.js +1 -0
- package/dist/application/Uninstall.js +12 -3
- package/dist/application/Upgrade.js +16 -1
- package/dist/application/WindowsCapabilities.js +86 -0
- package/dist/artifacts/AsarArtifactReader.js +11 -0
- package/dist/browser/BrowserProcessStartup.js +88 -0
- package/dist/browser/CdpCaptureDocuments.js +12 -8
- package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
- package/dist/browser/CdpCaptureEventHandlers.js +336 -0
- package/dist/browser/CdpCaptureEventHelpers.js +71 -0
- package/dist/browser/CdpCaptureEventState.js +1 -0
- package/dist/browser/CdpCaptureEventTypes.js +1 -0
- package/dist/browser/CdpCaptureEvents.js +29 -496
- package/dist/browser/CdpObservationSession.js +51 -8
- package/dist/browser/CdpPageCapture.js +12 -133
- package/dist/browser/CdpPageCaptureScripts.js +97 -0
- package/dist/browser/CdpPageCaptureWorkers.js +46 -0
- package/dist/browser/CdpWebMcpDiscovery.js +38 -14
- package/dist/browser/PngVisualDiff.js +9 -2
- package/dist/catalogIdentity.js +4 -0
- package/dist/cli/artifactCommands.js +82 -0
- package/dist/cli/coreAnalysisCommands.js +232 -0
- package/dist/cli/managedCommands.js +397 -0
- package/dist/cli/managedProjectGraphCommand.js +24 -0
- package/dist/cli/options.js +9 -0
- package/dist/cli/setupCommands.js +113 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/utilityCommands.js +122 -0
- package/dist/cli.js +10 -768
- package/dist/cliApplicationCommands.js +118 -4
- package/dist/cliBrowserAdvancedCommands.js +73 -16
- package/dist/cliBrowserCommands.js +39 -115
- package/dist/cliCommandNames.js +4 -0
- package/dist/cliElectronCommands.js +33 -62
- package/dist/cliEvidenceCommands.js +13 -2
- package/dist/cliInvestigationCommands.js +53 -10
- package/dist/cliObservationOptions.js +137 -0
- package/dist/cliPolicyCommands.js +80 -46
- package/dist/cliSetup.js +306 -0
- package/dist/config/environment.js +107 -0
- package/dist/config/parseConfig.js +228 -0
- package/dist/config/parsers.js +68 -0
- package/dist/config/permissions.js +30 -0
- package/dist/config/types.js +1 -0
- package/dist/config.js +1 -375
- package/dist/contracts/applicationToolContracts.js +134 -0
- package/dist/contracts/electronToolContracts.js +51 -3
- package/dist/contracts/managedToolContracts.js +8 -3
- package/dist/contracts/managedWorkflowToolContracts.js +3 -3
- package/dist/contracts/providerSelection.js +2 -0
- package/dist/contracts/sessionStatusContract.js +26 -0
- package/dist/contracts/toolContractTypes.js +12 -1
- package/dist/contracts/toolContracts.js +5 -12
- package/dist/contracts/toolEffects.js +14 -0
- package/dist/contracts/toolOutputSchemaGroups.js +274 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +328 -0
- package/dist/contracts/toolOutputSchemas.js +2 -550
- package/dist/domain/analysisErrorPresentation.js +234 -0
- package/dist/domain/analysisErrorProjection.js +284 -0
- package/dist/domain/androidApplication.js +206 -0
- package/dist/domain/appleApplication.js +221 -0
- package/dist/domain/artifactComparison.js +34 -15
- package/dist/domain/binaryTarget.js +87 -37
- package/dist/domain/boundedCartesianProjection.js +15 -0
- package/dist/domain/browserObservation.js +1 -285
- package/dist/domain/browserObservationSchemas.js +286 -0
- package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
- package/dist/domain/errors.js +4 -486
- package/dist/domain/functionComparison.js +4 -165
- package/dist/domain/functionComparisonDimensions.js +164 -0
- package/dist/domain/javascriptApplicationVersionItems.js +18 -6
- package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
- package/dist/domain/javascriptExportInstrumentation.js +97 -0
- package/dist/domain/javascriptFeatureTraversal.js +19 -7
- package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
- package/dist/domain/javascriptSemanticAnalysis.js +386 -0
- package/dist/domain/javascriptSemanticIr.js +30 -0
- package/dist/domain/javascriptSemanticProjection.js +366 -0
- package/dist/domain/javascriptSemanticState.js +40 -0
- package/dist/domain/javascriptSemanticTraversal.js +22 -0
- package/dist/domain/javascriptSemanticValues.js +352 -0
- package/dist/domain/javascriptStaticAnalysis.js +5 -221
- package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
- package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
- package/dist/domain/knownPageCoverage.js +21 -0
- package/dist/domain/managedApplicationGraph.js +14 -364
- package/dist/domain/managedApplicationGraphCoverage.js +170 -0
- package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
- package/dist/domain/managedApplicationGraphNodes.js +336 -0
- package/dist/domain/managedArtifact.js +16 -8
- package/dist/domain/managedMemberComparison.js +37 -391
- package/dist/domain/managedMemberComparisonCoverage.js +72 -0
- package/dist/domain/managedMemberComparisonMatch.js +356 -0
- package/dist/domain/managedNativeVerification.js +27 -389
- package/dist/domain/managedNativeVerificationMatch.js +272 -0
- package/dist/domain/managedNativeVerificationSchemas.js +141 -0
- package/dist/domain/managedReconstruction.js +82 -69
- package/dist/domain/managedRuntimeCorrelation.js +12 -3
- package/dist/domain/nodeRuntimeCharacterization.js +66 -0
- package/dist/domain/permissionPolicy.js +2 -0
- package/dist/domain/processComparison.js +19 -0
- package/dist/domain/processPairedExperiment.js +128 -0
- package/dist/domain/reconstructionCoverage.js +387 -0
- package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
- package/dist/domain/reconstructionVerificationSchemas.js +8 -0
- package/dist/domain/replayMachine.js +316 -0
- package/dist/domain/replayMachineRuntime.js +215 -0
- package/dist/domain/runtimeCharacterization.js +85 -0
- package/dist/domain/runtimeIdentification.js +210 -0
- package/dist/domain/webBundleAnalyzer.js +3 -409
- package/dist/domain/webBundleAnalyzerAst.js +77 -0
- package/dist/domain/webBundleAnalyzerInspection.js +307 -0
- package/dist/domain/webBundleAnalyzerResult.js +120 -0
- package/dist/dotnet/ManagedArtifactInspector.js +50 -29
- package/dist/dotnet/ManagedMemberInspector.js +51 -785
- package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
- package/dist/dotnet/ManagedMemberRows.js +120 -0
- package/dist/dotnet/ManagedMetadataInventory.js +42 -306
- package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
- package/dist/dotnet/ManagedMetadataLayout.js +48 -111
- package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
- package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
- package/dist/dotnet/ManagedPeReader.js +59 -28
- package/dist/evaluation/CodexAgentEval.js +132 -0
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraClient.js +76 -91
- package/dist/ghidra/GhidraClientStartup.js +29 -0
- package/dist/ghidra/GhidraClientWire.js +34 -0
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraDiagnostics.js +6 -1
- package/dist/ghidra/GhidraInstallation.js +20 -15
- package/dist/ghidra/GhidraLauncher.js +93 -29
- package/dist/ghidra/GhidraProvider.js +61 -161
- package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
- package/dist/ghidra/GhidraSessionValues.js +2 -0
- package/dist/ghidra/GhidraSocketConnection.js +44 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
- package/dist/ghidra/GhidraTransport.js +71 -0
- package/dist/identity.js +4 -2
- package/dist/main/messages.js +5 -0
- package/dist/main/reload.js +49 -0
- package/dist/main/shutdown.js +25 -0
- package/dist/main/startup.js +14 -0
- package/dist/main/state.js +26 -0
- package/dist/main/transport.js +61 -0
- package/dist/main/types.js +1 -0
- package/dist/main.js +35 -146
- package/dist/process/PrivateRuntimeRoot.js +3 -2
- package/dist/process/ProcessOwnership.js +35 -0
- package/dist/process/ProviderProcess.js +2 -0
- package/dist/replay/JavaScriptReplayWorker.js +64 -50
- package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
- package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
- package/dist/replay/ReplayOutcome.js +95 -0
- package/dist/replay/ReplayProcessLifecycle.js +71 -0
- package/dist/server/ProcessCaptureElicitation.js +157 -0
- package/dist/server/createServer.js +124 -56
- package/dist/server/javascriptApplicationResult.js +85 -0
- package/dist/server/registerApplicationTools/characterization.js +57 -0
- package/dist/server/registerApplicationTools/compareVersions.js +34 -0
- package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
- package/dist/server/registerApplicationTools/coverage.js +84 -0
- package/dist/server/registerApplicationTools/helpers.js +42 -0
- package/dist/server/registerApplicationTools/traceFeature.js +26 -0
- package/dist/server/registerApplicationTools/types.js +1 -0
- package/dist/server/registerApplicationTools.js +10 -112
- package/dist/server/registerBrowserTools.js +59 -83
- package/dist/server/registerElectronTools.js +48 -49
- package/dist/server/registerEnhancedTools.js +34 -42
- package/dist/server/registerEvidenceResources.js +56 -0
- package/dist/server/registerInvestigationTools/helpers.js +74 -0
- package/dist/server/registerInvestigationTools/types.js +1 -0
- package/dist/server/registerInvestigationTools.js +83 -116
- package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
- package/dist/server/registerManagedTools.js +18 -1
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
- package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
- package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
- package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
- package/dist/server/registerManagedWorkflowTools/types.js +1 -0
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
- package/dist/server/registerManagedWorkflowTools.js +10 -274
- package/dist/server/registerOfficialTools.js +73 -59
- package/dist/server/registerSessionRecordTools.js +137 -0
- package/dist/server/registerSessionStatusTool.js +97 -26
- package/dist/server/registerSessionTools.js +60 -164
- package/dist/server/sessionAvailabilityPolicy.js +10 -0
- package/dist/server/toolRegistrationOptions.js +101 -3
- package/dist/server/toolResult.js +15 -4
- package/package.json +11 -11
- package/skills/reverse-engineer-anything/SKILL.md +76 -0
- package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
- package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +25 -0
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
- package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
- package/scripts/prepare-node-pty.mjs +0 -35
- package/scripts/rebuild-native.mjs +0 -22
- package/skills/rea-analysis/SKILL.md +0 -222
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
export const recommendedReconstructionProbes = (reasons) => reasons.slice(0, 100).map((reason) => ({
|
|
2
|
+
operation: probeOperation(reason.code),
|
|
3
|
+
subject_id: reason.subject_id,
|
|
4
|
+
rationale: reason.detail,
|
|
5
|
+
}));
|
|
6
|
+
const probeOperation = (code) => {
|
|
7
|
+
if (code.startsWith("owner-") || code.startsWith("authority-routing"))
|
|
8
|
+
return "reconcile_reconstruction_owner";
|
|
9
|
+
if (code.startsWith("verifier-"))
|
|
10
|
+
return "run_reconstruction_verifier";
|
|
11
|
+
if (code.startsWith("package-proof"))
|
|
12
|
+
return "verify_reconstruction_package";
|
|
13
|
+
if (code === "active-contradiction")
|
|
14
|
+
return "resolve_contradiction";
|
|
15
|
+
if (code === "active-unknown")
|
|
16
|
+
return "probe_residual_unknown";
|
|
17
|
+
return "update_authoritative_inventory";
|
|
18
|
+
};
|
|
19
|
+
export const evaluateReconstructionSurfaces = (context) => {
|
|
20
|
+
const surfaces = indexUnique(context.workspace.surfaces, "surface_id");
|
|
21
|
+
for (const surfaceId of context.boundary.required_surface_ids) {
|
|
22
|
+
const surface = surfaces.get(surfaceId);
|
|
23
|
+
if (surface === undefined) {
|
|
24
|
+
addReason(context, "surface-missing", surfaceId, "Required surface is absent from the authoritative inventory.");
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
addEvidence(context, surface.evidence_ids);
|
|
28
|
+
for (const dependencyId of surface.dependency_surface_ids)
|
|
29
|
+
if (!surfaces.has(dependencyId))
|
|
30
|
+
addReason(context, "dependency-surface-missing", surfaceId, `Declared dependency surface is absent: ${dependencyId}`);
|
|
31
|
+
evaluateOwner(context, surfaceId);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const evaluateOwner = (context, surfaceId) => {
|
|
35
|
+
const owners = context.workspace.owners.filter(({ surface_id: id }) => id === surfaceId);
|
|
36
|
+
if (owners.length === 0) {
|
|
37
|
+
addReason(context, "owner-missing", surfaceId, "Required surface has no reconstructed owner or explicit disposition.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (owners.length > 1) {
|
|
41
|
+
addReason(context, "owner-duplicate", surfaceId, "Required surface has conflicting ownership declarations.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const owner = owners[0]?.ownership;
|
|
45
|
+
if (owner === undefined)
|
|
46
|
+
return;
|
|
47
|
+
if (owner.disposition !== "implemented") {
|
|
48
|
+
if (!context.boundary.allowed_dispositions.includes(owner.disposition))
|
|
49
|
+
addReason(context, "disposition-not-allowed", surfaceId, `${owner.disposition} is not allowed by this completion boundary.`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const ownerChecks = [
|
|
53
|
+
[
|
|
54
|
+
owner.path_state === "missing",
|
|
55
|
+
"owner-path-missing",
|
|
56
|
+
"Declared owner path is missing.",
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
owner.path_state === "unknown",
|
|
60
|
+
"owner-path-unknown",
|
|
61
|
+
"Declared owner path has not been checked.",
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
owner.package_state === "missing",
|
|
65
|
+
"owner-undistributed",
|
|
66
|
+
"Declared owner is absent from the distributable.",
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
owner.package_state === "unknown",
|
|
70
|
+
"owner-distribution-unknown",
|
|
71
|
+
"Owner distribution state is unknown.",
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
owner.authority_route === "detected",
|
|
75
|
+
"authority-routing-detected",
|
|
76
|
+
"Reconstructed owner routes into the authority.",
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
owner.authority_route === "unknown",
|
|
80
|
+
"authority-routing-unknown",
|
|
81
|
+
"Authority independence has not been established.",
|
|
82
|
+
],
|
|
83
|
+
];
|
|
84
|
+
for (const [matches, code, detail] of ownerChecks)
|
|
85
|
+
if (matches)
|
|
86
|
+
addReason(context, code, surfaceId, detail);
|
|
87
|
+
};
|
|
88
|
+
export const evaluateReconstructionClaims = (context) => {
|
|
89
|
+
const claims = indexUnique(context.workspace.claims, "claim_id");
|
|
90
|
+
for (const claimId of context.boundary.required_claim_ids) {
|
|
91
|
+
const claim = claims.get(claimId);
|
|
92
|
+
if (claim === undefined) {
|
|
93
|
+
addReason(context, "claim-missing", claimId, "Required finite claim is absent.");
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const contracts = context.workspace.verifier_contracts.filter(({ claim_ids: ids }) => ids.includes(claimId));
|
|
97
|
+
if (contracts.length === 0) {
|
|
98
|
+
addReason(context, "verifier-missing", claimId, "No verifier contract covers this claim.");
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (contracts.length > 1) {
|
|
102
|
+
addReason(context, "verifier-duplicate", claimId, "Multiple verifier contracts claim primary coverage.");
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const contract = contracts[0];
|
|
106
|
+
if (contract !== undefined)
|
|
107
|
+
evaluateVerifierResult(context, claim, contract);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const evaluateVerifierResult = (context, claim, contract) => {
|
|
111
|
+
const results = context.workspace.verifier_results
|
|
112
|
+
.filter(({ verifier_id: id }) => id === contract.verifier_id)
|
|
113
|
+
.sort((left, right) => Date.parse(right.observed_at) - Date.parse(left.observed_at));
|
|
114
|
+
const result = results[0];
|
|
115
|
+
if (result === undefined) {
|
|
116
|
+
addReason(context, "verifier-result-missing", claim.claim_id, "Verifier has no recorded result.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
addEvidence(context, result.evidence_ids);
|
|
120
|
+
if (!verifierResultIsCompatible(context, claim, contract, result)) {
|
|
121
|
+
addReason(context, "verifier-result-incompatible", claim.claim_id, "Latest verifier result does not match current artifacts, owners, dimensions, authority, normalization, repeats, or contract.");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const observedAt = Date.parse(result.observed_at);
|
|
125
|
+
if (observedAt > context.nowEpochMs ||
|
|
126
|
+
context.nowEpochMs - observedAt > contract.max_age_ms) {
|
|
127
|
+
addReason(context, "verifier-result-stale", claim.claim_id, "Latest compatible verifier result is stale.");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (result.status === "fail")
|
|
131
|
+
addReason(context, "verifier-failed", claim.claim_id, "Latest compatible verifier result failed.");
|
|
132
|
+
else if (result.status !== "pass")
|
|
133
|
+
addReason(context, "verifier-unknown", claim.claim_id, `Latest compatible verifier result is ${result.status}.`);
|
|
134
|
+
};
|
|
135
|
+
const verifierResultIsCompatible = (context, claim, contract, result) => {
|
|
136
|
+
const artifactDigests = new Set(context.workspace.artifacts.map(({ artifact_sha256: value }) => value));
|
|
137
|
+
const ownerDigests = new Set(context.workspace.owners.flatMap(({ ownership }) => ownership.disposition === "implemented" ? [ownership.owner_sha256] : []));
|
|
138
|
+
const surfaces = new Map(context.workspace.surfaces.map((surface) => [surface.surface_id, surface]));
|
|
139
|
+
const artifacts = new Map(context.workspace.artifacts.map((artifact) => [
|
|
140
|
+
artifact.artifact_id,
|
|
141
|
+
artifact,
|
|
142
|
+
]));
|
|
143
|
+
const expectedArtifactDigests = claim.surface_ids.flatMap((surfaceId) => {
|
|
144
|
+
const surface = surfaces.get(surfaceId);
|
|
145
|
+
const artifact = surface === undefined ? undefined : artifacts.get(surface.artifact_id);
|
|
146
|
+
return artifact === undefined ? [] : [artifact.artifact_sha256];
|
|
147
|
+
});
|
|
148
|
+
const expectedOwnerDigests = context.workspace.owners.flatMap(({ surface_id: surfaceId, ownership }) => claim.surface_ids.includes(surfaceId) &&
|
|
149
|
+
ownership.disposition === "implemented"
|
|
150
|
+
? [ownership.owner_sha256]
|
|
151
|
+
: []);
|
|
152
|
+
return (result.contract_sha256 === contract.contract_sha256 &&
|
|
153
|
+
result.normalization_sha256 === contract.normalization_sha256 &&
|
|
154
|
+
result.covered_claim_ids.includes(claim.claim_id) &&
|
|
155
|
+
result.covered_claim_ids.every((item) => contract.claim_ids.includes(item)) &&
|
|
156
|
+
claim.required_dimensions.every((item) => contract.dimensions.includes(item)) &&
|
|
157
|
+
claim.required_dimensions.every((item) => result.covered_dimensions.includes(item)) &&
|
|
158
|
+
result.covered_dimensions.every((item) => contract.dimensions.includes(item)) &&
|
|
159
|
+
result.artifact_sha256s.every((item) => artifactDigests.has(item)) &&
|
|
160
|
+
expectedArtifactDigests.every((item) => result.artifact_sha256s.includes(item)) &&
|
|
161
|
+
result.owner_sha256s.every((item) => ownerDigests.has(item)) &&
|
|
162
|
+
expectedOwnerDigests.every((item) => result.owner_sha256s.includes(item)) &&
|
|
163
|
+
result.repeats >= contract.minimum_repeats &&
|
|
164
|
+
claim.required_authority === contract.authority);
|
|
165
|
+
};
|
|
166
|
+
export const evaluateReconstructionWorkspaceRisks = (context) => {
|
|
167
|
+
for (const unknownId of context.workspace.residual_unknown_ids)
|
|
168
|
+
if (!context.boundary.allowed_unknown_ids.includes(unknownId))
|
|
169
|
+
addReason(context, "active-unknown", unknownId, "Active residual unknown is not permitted by the completion boundary.");
|
|
170
|
+
for (const contradiction of context.workspace.contradictions)
|
|
171
|
+
if (contradiction.status === "active" &&
|
|
172
|
+
(contradiction.surface_ids.some((id) => context.boundary.required_surface_ids.includes(id)) ||
|
|
173
|
+
contradiction.claim_ids.some((id) => context.boundary.required_claim_ids.includes(id)))) {
|
|
174
|
+
addEvidence(context, contradiction.evidence_ids);
|
|
175
|
+
addReason(context, "active-contradiction", contradiction.contradiction_id, "Active contradiction affects the completion boundary.");
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
export const evaluateReconstructionPackageProofs = (context) => {
|
|
179
|
+
const surfaces = new Map(context.workspace.surfaces.map((surface) => [surface.surface_id, surface]));
|
|
180
|
+
const artifacts = new Map(context.workspace.artifacts.map((artifact) => [
|
|
181
|
+
artifact.artifact_id,
|
|
182
|
+
artifact,
|
|
183
|
+
]));
|
|
184
|
+
const requiredArtifactDigests = context.boundary.required_surface_ids.flatMap((surfaceId) => {
|
|
185
|
+
const surface = surfaces.get(surfaceId);
|
|
186
|
+
const artifact = surface === undefined ? undefined : artifacts.get(surface.artifact_id);
|
|
187
|
+
return artifact === undefined ? [] : [artifact.artifact_sha256];
|
|
188
|
+
});
|
|
189
|
+
for (const kind of context.boundary.required_package_proof_kinds) {
|
|
190
|
+
const proofs = context.workspace.package_proofs.filter((proof) => proof.kind === kind);
|
|
191
|
+
const passing = proofs.find(({ artifact_sha256s: digests, status }) => status === "pass" &&
|
|
192
|
+
requiredArtifactDigests.every((digest) => digests.includes(digest)));
|
|
193
|
+
if (passing !== undefined) {
|
|
194
|
+
addEvidence(context, passing.evidence_ids);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (proofs.some(({ status }) => status === "fail"))
|
|
198
|
+
addReason(context, "package-proof-failed", kind, "Required package or integration proof failed.");
|
|
199
|
+
else if (proofs.length > 0)
|
|
200
|
+
addReason(context, "package-proof-unknown", kind, "Required package or integration proof is not passing.");
|
|
201
|
+
else
|
|
202
|
+
addReason(context, "package-proof-missing", kind, "Required package or integration proof is missing.");
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const indexUnique = (items, key) => new Map(items.map((item) => [item[key], item]));
|
|
206
|
+
const addReason = (context, code, subjectId, detail) => {
|
|
207
|
+
context.reasons.push({ code, subject_id: subjectId, detail });
|
|
208
|
+
};
|
|
209
|
+
const addEvidence = (context, evidenceIds) => {
|
|
210
|
+
for (const evidenceId of evidenceIds)
|
|
211
|
+
context.evidenceIds.add(evidenceId);
|
|
212
|
+
};
|
|
@@ -70,6 +70,14 @@ export const reconstructionSpecificationSchema = z
|
|
|
70
70
|
/** Bounded reconstruction-verification input. */
|
|
71
71
|
export const reconstructionVerificationInputSchema = z.strictObject({
|
|
72
72
|
specification: reconstructionSpecificationSchema,
|
|
73
|
+
coverage: z
|
|
74
|
+
.strictObject({
|
|
75
|
+
workspace_id: z.string().regex(/^rcw_[a-f0-9]{64}$/u),
|
|
76
|
+
revision: z.number().int().min(1),
|
|
77
|
+
revision_sha256: digestSchema,
|
|
78
|
+
boundary_id: z.string().min(1).max(200),
|
|
79
|
+
})
|
|
80
|
+
.optional(),
|
|
73
81
|
offset: z.number().int().min(0).default(0),
|
|
74
82
|
limit: z.number().int().min(1).max(100).default(100),
|
|
75
83
|
unknown_registry_approved: z.literal(true).optional(),
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { MultiDirectedGraph } from "graphology";
|
|
2
|
+
import { singleSource } from "graphology-shortest-path";
|
|
3
|
+
import canonicalize from "canonicalize";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
const replayIdentifierSchema = z
|
|
6
|
+
.string()
|
|
7
|
+
.regex(/^[A-Za-z][A-Za-z0-9_-]{0,63}$/u);
|
|
8
|
+
const replayPathSchema = z
|
|
9
|
+
.array(z.union([z.string().max(256), z.number().int().nonnegative()]))
|
|
10
|
+
.max(32);
|
|
11
|
+
const sensitiveHeaderNames = new Set([
|
|
12
|
+
"authorization",
|
|
13
|
+
"cookie",
|
|
14
|
+
"proxy-authorization",
|
|
15
|
+
"set-cookie",
|
|
16
|
+
]);
|
|
17
|
+
const replayHeaderMatchersSchema = z
|
|
18
|
+
.record(z.string().min(1).max(256), z.string().max(8_192))
|
|
19
|
+
.superRefine((headers, context) => {
|
|
20
|
+
const names = Object.keys(headers).map((name) => name.toLowerCase());
|
|
21
|
+
if (new Set(names).size !== names.length)
|
|
22
|
+
context.addIssue({
|
|
23
|
+
code: "custom",
|
|
24
|
+
message: "replay header matchers must be unique ignoring case",
|
|
25
|
+
input: headers,
|
|
26
|
+
});
|
|
27
|
+
if (names.some((name) => sensitiveHeaderNames.has(name)))
|
|
28
|
+
context.addIssue({
|
|
29
|
+
code: "custom",
|
|
30
|
+
message: "replay header matchers cannot persist authorization or cookie values; capture and guard through a secret alias instead",
|
|
31
|
+
input: headers,
|
|
32
|
+
});
|
|
33
|
+
})
|
|
34
|
+
.transform((headers) => Object.fromEntries(Object.entries(headers).map(([name, value]) => [
|
|
35
|
+
name.toLowerCase(),
|
|
36
|
+
value,
|
|
37
|
+
])));
|
|
38
|
+
const replayValueSourceSchema = z.discriminatedUnion("source", [
|
|
39
|
+
z.strictObject({
|
|
40
|
+
source: z.literal("request_header"),
|
|
41
|
+
name: z
|
|
42
|
+
.string()
|
|
43
|
+
.min(1)
|
|
44
|
+
.transform((name) => name.toLowerCase()),
|
|
45
|
+
}),
|
|
46
|
+
z.strictObject({ source: z.literal("request_json"), path: replayPathSchema }),
|
|
47
|
+
z.strictObject({
|
|
48
|
+
source: z.literal("websocket_json"),
|
|
49
|
+
path: replayPathSchema,
|
|
50
|
+
}),
|
|
51
|
+
z.strictObject({ source: z.literal("action_json"), path: replayPathSchema }),
|
|
52
|
+
]);
|
|
53
|
+
const replayTriggerSchema = z.discriminatedUnion("protocol", [
|
|
54
|
+
z.strictObject({
|
|
55
|
+
protocol: z.literal("http"),
|
|
56
|
+
method: z
|
|
57
|
+
.string()
|
|
58
|
+
.min(1)
|
|
59
|
+
.max(16)
|
|
60
|
+
.transform((method) => method.toUpperCase()),
|
|
61
|
+
path: z.string().startsWith("/"),
|
|
62
|
+
headers: replayHeaderMatchersSchema.default({}),
|
|
63
|
+
body: z.string().max(1_000_000).nullable().default(null),
|
|
64
|
+
}),
|
|
65
|
+
z.strictObject({
|
|
66
|
+
protocol: z.literal("websocket_connect"),
|
|
67
|
+
path: z.string().startsWith("/"),
|
|
68
|
+
headers: replayHeaderMatchersSchema.default({}),
|
|
69
|
+
}),
|
|
70
|
+
z.strictObject({
|
|
71
|
+
protocol: z.literal("websocket_message"),
|
|
72
|
+
path: z.string().startsWith("/"),
|
|
73
|
+
body: z.string().max(1_000_000).nullable().default(null),
|
|
74
|
+
}),
|
|
75
|
+
]);
|
|
76
|
+
const replayActionSchema = z.discriminatedUnion("type", [
|
|
77
|
+
z.strictObject({
|
|
78
|
+
type: z.literal("http_response"),
|
|
79
|
+
status: z.number().int().min(100).max(599),
|
|
80
|
+
headers: z.record(z.string(), z.string()).default({}),
|
|
81
|
+
body: z.string().max(1_000_000),
|
|
82
|
+
}),
|
|
83
|
+
z.strictObject({
|
|
84
|
+
type: z.literal("websocket_send"),
|
|
85
|
+
data: z.string().max(1_000_000),
|
|
86
|
+
}),
|
|
87
|
+
z.strictObject({ type: z.literal("disconnect") }),
|
|
88
|
+
z.strictObject({
|
|
89
|
+
type: z.literal("delay"),
|
|
90
|
+
duration_ms: z.number().int().nonnegative().max(30_000),
|
|
91
|
+
}),
|
|
92
|
+
]);
|
|
93
|
+
const replayTransitionSchema = z.strictObject({
|
|
94
|
+
id: replayIdentifierSchema,
|
|
95
|
+
from: replayIdentifierSchema,
|
|
96
|
+
to: replayIdentifierSchema,
|
|
97
|
+
priority: z.number().int().min(0).max(1_000).default(100),
|
|
98
|
+
trigger: replayTriggerSchema,
|
|
99
|
+
guards: z
|
|
100
|
+
.array(z.strictObject({
|
|
101
|
+
variable: replayIdentifierSchema,
|
|
102
|
+
value: replayValueSourceSchema,
|
|
103
|
+
}))
|
|
104
|
+
.max(32)
|
|
105
|
+
.default([]),
|
|
106
|
+
captures: z
|
|
107
|
+
.array(z.strictObject({
|
|
108
|
+
variable: replayIdentifierSchema,
|
|
109
|
+
value: replayValueSourceSchema,
|
|
110
|
+
sensitive: z.boolean().default(false),
|
|
111
|
+
}))
|
|
112
|
+
.max(32)
|
|
113
|
+
.default([]),
|
|
114
|
+
actions: z.array(replayActionSchema).min(1).max(32),
|
|
115
|
+
max_uses: z.number().int().min(1).max(10_000),
|
|
116
|
+
});
|
|
117
|
+
const transitionSignature = (transition) => {
|
|
118
|
+
const signature = canonicalize({
|
|
119
|
+
from: transition.from,
|
|
120
|
+
priority: transition.priority,
|
|
121
|
+
trigger: transition.trigger,
|
|
122
|
+
});
|
|
123
|
+
if (signature === undefined)
|
|
124
|
+
throw new TypeError("Replay transition signature is not canonical JSON");
|
|
125
|
+
return signature;
|
|
126
|
+
};
|
|
127
|
+
const replayMachineBaseSchema = z.strictObject({
|
|
128
|
+
initial_state: replayIdentifierSchema,
|
|
129
|
+
states: z
|
|
130
|
+
.array(z.strictObject({
|
|
131
|
+
name: replayIdentifierSchema,
|
|
132
|
+
terminal: z.boolean().default(false),
|
|
133
|
+
max_visits: z.number().int().min(1).max(100_000).default(100_000),
|
|
134
|
+
}))
|
|
135
|
+
.min(1)
|
|
136
|
+
.max(256),
|
|
137
|
+
transitions: z.array(replayTransitionSchema).min(1).max(2_000),
|
|
138
|
+
max_transitions: z.number().int().min(1).max(100_000),
|
|
139
|
+
limits: z
|
|
140
|
+
.strictObject({
|
|
141
|
+
connections: z.number().int().min(1).max(1_000).default(100),
|
|
142
|
+
messages: z.number().int().min(1).max(100_000).default(10_000),
|
|
143
|
+
bytes: z.number().int().min(1).max(10_000_000).default(1_000_000),
|
|
144
|
+
duration_ms: z.number().int().min(1).max(300_000).default(30_000),
|
|
145
|
+
})
|
|
146
|
+
.default({
|
|
147
|
+
connections: 100,
|
|
148
|
+
messages: 10_000,
|
|
149
|
+
bytes: 1_000_000,
|
|
150
|
+
duration_ms: 30_000,
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
const validateReplayIdentities = (machine, context) => {
|
|
154
|
+
const names = machine.states.map(({ name }) => name);
|
|
155
|
+
const nameSet = new Set(names);
|
|
156
|
+
if (nameSet.size !== names.length)
|
|
157
|
+
context.addIssue({
|
|
158
|
+
code: "custom",
|
|
159
|
+
message: "replay machine state names must be unique",
|
|
160
|
+
path: ["states"],
|
|
161
|
+
input: machine,
|
|
162
|
+
});
|
|
163
|
+
if (!nameSet.has(machine.initial_state))
|
|
164
|
+
context.addIssue({
|
|
165
|
+
code: "custom",
|
|
166
|
+
message: "replay machine initial state is not declared",
|
|
167
|
+
path: ["initial_state"],
|
|
168
|
+
input: machine,
|
|
169
|
+
});
|
|
170
|
+
const transitionIds = machine.transitions.map(({ id }) => id);
|
|
171
|
+
if (new Set(transitionIds).size !== transitionIds.length)
|
|
172
|
+
context.addIssue({
|
|
173
|
+
code: "custom",
|
|
174
|
+
message: "replay machine transition IDs must be unique",
|
|
175
|
+
path: ["transitions"],
|
|
176
|
+
input: machine,
|
|
177
|
+
});
|
|
178
|
+
const signatures = machine.transitions.map(transitionSignature);
|
|
179
|
+
if (new Set(signatures).size !== signatures.length)
|
|
180
|
+
context.addIssue({
|
|
181
|
+
code: "custom",
|
|
182
|
+
message: "replay machine transitions with the same state, trigger, and priority are ambiguous",
|
|
183
|
+
path: ["transitions"],
|
|
184
|
+
input: machine,
|
|
185
|
+
});
|
|
186
|
+
return nameSet;
|
|
187
|
+
};
|
|
188
|
+
const validateReplayVariables = (machine, context) => {
|
|
189
|
+
const capturedVariables = new Map();
|
|
190
|
+
for (const [transitionIndex, transition] of machine.transitions.entries()) {
|
|
191
|
+
for (const capture of transition.captures) {
|
|
192
|
+
const sensitivity = capturedVariables.get(capture.variable);
|
|
193
|
+
if (sensitivity !== undefined && sensitivity !== capture.sensitive)
|
|
194
|
+
context.addIssue({
|
|
195
|
+
code: "custom",
|
|
196
|
+
message: "a replay variable cannot change secret classification between captures",
|
|
197
|
+
path: ["transitions", transitionIndex, "captures"],
|
|
198
|
+
input: machine,
|
|
199
|
+
});
|
|
200
|
+
capturedVariables.set(capture.variable, capture.sensitive);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const availableByState = new Map([
|
|
204
|
+
[machine.initial_state, new Set()],
|
|
205
|
+
]);
|
|
206
|
+
let changed = true;
|
|
207
|
+
while (changed) {
|
|
208
|
+
changed = false;
|
|
209
|
+
for (const transition of machine.transitions) {
|
|
210
|
+
const available = availableByState.get(transition.from);
|
|
211
|
+
if (available === undefined ||
|
|
212
|
+
transition.guards.some(({ variable }) => !available.has(variable)))
|
|
213
|
+
continue;
|
|
214
|
+
const nextAvailable = new Set(available);
|
|
215
|
+
for (const { variable } of transition.captures)
|
|
216
|
+
nextAvailable.add(variable);
|
|
217
|
+
const existing = availableByState.get(transition.to);
|
|
218
|
+
if (existing === undefined) {
|
|
219
|
+
availableByState.set(transition.to, nextAvailable);
|
|
220
|
+
changed = true;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
for (const variable of nextAvailable)
|
|
224
|
+
if (!existing.has(variable)) {
|
|
225
|
+
existing.add(variable);
|
|
226
|
+
changed = true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
for (const [transitionIndex, transition] of machine.transitions.entries())
|
|
231
|
+
for (const [guardIndex, guard] of transition.guards.entries())
|
|
232
|
+
if (!availableByState.get(transition.from)?.has(guard.variable))
|
|
233
|
+
context.addIssue({
|
|
234
|
+
code: "custom",
|
|
235
|
+
message: "replay guard variable is not captured on an executable path before use",
|
|
236
|
+
path: ["transitions", transitionIndex, "guards", guardIndex],
|
|
237
|
+
input: machine,
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
const validateReplayActions = (machine, context) => {
|
|
241
|
+
for (const [index, transition] of machine.transitions.entries()) {
|
|
242
|
+
const httpResponses = transition.actions.filter(({ type }) => type === "http_response").length;
|
|
243
|
+
const websocketSends = transition.actions.some(({ type }) => type === "websocket_send");
|
|
244
|
+
const invalid = transition.trigger.protocol === "http"
|
|
245
|
+
? httpResponses !== 1 || websocketSends
|
|
246
|
+
: httpResponses !== 0;
|
|
247
|
+
if (invalid)
|
|
248
|
+
context.addIssue({
|
|
249
|
+
code: "custom",
|
|
250
|
+
message: "replay actions must match the transition protocol and HTTP transitions require exactly one response",
|
|
251
|
+
path: ["transitions", index, "actions"],
|
|
252
|
+
input: machine,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const validateReplayGraph = (machine, names, context) => {
|
|
257
|
+
const graph = new MultiDirectedGraph();
|
|
258
|
+
for (const name of names)
|
|
259
|
+
graph.addNode(name);
|
|
260
|
+
for (const [index, transition] of machine.transitions.entries()) {
|
|
261
|
+
if (!names.has(transition.from) || !names.has(transition.to)) {
|
|
262
|
+
context.addIssue({
|
|
263
|
+
code: "custom",
|
|
264
|
+
message: "replay machine transition references an unknown state",
|
|
265
|
+
path: ["transitions", index],
|
|
266
|
+
input: machine,
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
graph.addDirectedEdgeWithKey(`${transition.id}:${String(index)}`, transition.from, transition.to);
|
|
271
|
+
}
|
|
272
|
+
if (!graph.hasNode(machine.initial_state))
|
|
273
|
+
return;
|
|
274
|
+
if (!machine.states.some(({ terminal }) => terminal))
|
|
275
|
+
context.addIssue({
|
|
276
|
+
code: "custom",
|
|
277
|
+
message: "replay machine must declare at least one terminal state",
|
|
278
|
+
path: ["states"],
|
|
279
|
+
input: machine,
|
|
280
|
+
});
|
|
281
|
+
const reachable = new Set(Object.keys(singleSource(graph, machine.initial_state)));
|
|
282
|
+
reachable.add(machine.initial_state);
|
|
283
|
+
for (const [index, state] of machine.states.entries()) {
|
|
284
|
+
if (!reachable.has(state.name))
|
|
285
|
+
context.addIssue({
|
|
286
|
+
code: "custom",
|
|
287
|
+
message: "replay machine state is unreachable from the initial state",
|
|
288
|
+
path: ["states", index],
|
|
289
|
+
input: machine,
|
|
290
|
+
});
|
|
291
|
+
if (state.terminal && graph.outDegree(state.name) > 0)
|
|
292
|
+
context.addIssue({
|
|
293
|
+
code: "custom",
|
|
294
|
+
message: "terminal replay machine state cannot have outgoing transitions",
|
|
295
|
+
path: ["states", index],
|
|
296
|
+
input: machine,
|
|
297
|
+
});
|
|
298
|
+
if (!state.terminal && graph.outDegree(state.name) === 0)
|
|
299
|
+
context.addIssue({
|
|
300
|
+
code: "custom",
|
|
301
|
+
message: "non-terminal replay machine state has no outgoing transition",
|
|
302
|
+
path: ["states", index],
|
|
303
|
+
input: machine,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
/** Portable finite replay machine with no executable callbacks. */
|
|
308
|
+
export const replayMachineSchema = z
|
|
309
|
+
.object(replayMachineBaseSchema.shape)
|
|
310
|
+
.strict()
|
|
311
|
+
.superRefine((machine, context) => {
|
|
312
|
+
const names = validateReplayIdentities(machine, context);
|
|
313
|
+
validateReplayVariables(machine, context);
|
|
314
|
+
validateReplayActions(machine, context);
|
|
315
|
+
validateReplayGraph(machine, names, context);
|
|
316
|
+
});
|