@holoscript/core 8.0.13 → 8.0.15
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 +20 -13
- package/bin/holo-decision.mjs +133 -0
- package/bin/holoscript.cjs +0 -0
- package/dist/{GLTFPipeline-L76YTXKA.js → GLTFPipeline-FRBZIATS.js} +7 -7
- package/dist/{GLTFPipeline-ZWJN7GI6.cjs → GLTFPipeline-MTPP444P.cjs} +11 -11
- package/dist/{HoloCompositionParser-P4Y3CXKQ.js → HoloCompositionParser-2GVQAZ2X.js} +7 -7
- package/dist/HoloCompositionParser-3NDF6BMX.cjs +45 -0
- package/dist/HoloScriptPlusParser-URSIBEAW.cjs +30 -0
- package/dist/HoloScriptPlusParser-YCV6L7GI.js +13 -0
- package/dist/{HoloScriptRuntime-YBYXJHJN.cjs → HoloScriptRuntime-CRWI6C3P.cjs} +5 -5
- package/dist/{HoloScriptRuntime-UTRLJI5L.js → HoloScriptRuntime-IAZGHPFP.js} +4 -4
- package/dist/{SceneIRCompiler-63KA3M2F.js → SceneIRCompiler-BJQMKLWL.js} +7 -7
- package/dist/{SceneIRCompiler-KWUFHZ4O.cjs → SceneIRCompiler-RTPPDQAV.cjs} +12 -12
- package/dist/{chunk-FWCHPCD4.js → chunk-22AULOAL.js} +5 -5
- package/dist/{chunk-G35CAQ7I.cjs → chunk-2DJIS7TH.cjs} +5 -5
- package/dist/{chunk-LHUDAJQT.cjs → chunk-2PVEXEOL.cjs} +5 -5
- package/dist/{chunk-WWNCM473.js → chunk-2UP2J3OK.js} +4 -4
- package/dist/{chunk-HVX7QQYK.js → chunk-3AUYBQ5I.js} +3 -3
- package/dist/{chunk-TSTFYGGX.js → chunk-3MQAOMKD.js} +3 -3
- package/dist/{chunk-FWKEY4LD.js → chunk-4AGNARX6.js} +3 -3
- package/dist/{chunk-WNYXT4TE.cjs → chunk-4CQ7ULXZ.cjs} +3 -3
- package/dist/{chunk-U3D4SYQ5.js → chunk-5LCJDX5A.js} +3 -3
- package/dist/{chunk-IK6Z2HNC.js → chunk-6OQHLNTT.js} +2 -2
- package/dist/{chunk-EI3J6LTW.cjs → chunk-6RDHWSZM.cjs} +217 -217
- package/dist/{chunk-77QI552M.js → chunk-7AY66PZP.js} +3 -3
- package/dist/{chunk-7IDJDMPU.cjs → chunk-7FYSSI3C.cjs} +27 -2
- package/dist/{chunk-BP4KTMLY.js → chunk-7HDRN5BW.js} +4 -4
- package/dist/{chunk-SQAAWO4V.cjs → chunk-7YKNV2BE.cjs} +5 -5
- package/dist/{chunk-WTH23ISI.js → chunk-AAUQTR7I.js} +4 -4
- package/dist/{chunk-OC6GV5QH.cjs → chunk-AN3ZH7M7.cjs} +38 -38
- package/dist/{chunk-YW3I2FQL.js → chunk-ANMME7XT.js} +4 -2
- package/dist/{chunk-A4OOLBZD.js → chunk-ATRAK4RC.js} +5 -5
- package/dist/{chunk-BQJ3YBF3.js → chunk-AUF7HFOO.js} +724 -647
- package/dist/{chunk-2X3IPCBK.cjs → chunk-AUMTQV64.cjs} +7 -7
- package/dist/chunk-AUU4ACJP.cjs +1605 -0
- package/dist/{chunk-QQFB4RT2.cjs → chunk-BTVHY3UB.cjs} +2 -2
- package/dist/{chunk-ZK7KX5CD.cjs → chunk-CMEINO5I.cjs} +190 -74
- package/dist/{chunk-BCP4E7MD.cjs → chunk-CRUIGXF7.cjs} +26 -10
- package/dist/{chunk-2E6PP5UE.cjs → chunk-E2SNPMBO.cjs} +11 -11
- package/dist/{chunk-2BYAUHBO.js → chunk-E4YS5SIZ.js} +90 -25
- package/dist/{chunk-KFVNCW4Q.js → chunk-EKNWUSGW.js} +3 -3
- package/dist/{chunk-JEJ7TT54.js → chunk-ETIJOUIO.js} +4 -4
- package/dist/{chunk-FRSKLX6I.js → chunk-F33KLYOS.js} +12 -9
- package/dist/{chunk-GGR7QUKK.cjs → chunk-F47FEV4X.cjs} +40 -40
- package/dist/{chunk-J3UHV5T4.cjs → chunk-FCGTI7FO.cjs} +4 -4
- package/dist/{chunk-RRQIAJLQ.cjs → chunk-FGTXAVKG.cjs} +5 -5
- package/dist/{chunk-2KKOYZYI.cjs → chunk-FPGYWNNA.cjs} +5 -5
- package/dist/{chunk-65S62L4D.cjs → chunk-FQ4MYZYU.cjs} +20 -20
- package/dist/{chunk-UGQ3QNV6.js → chunk-FT4FGTR6.js} +24 -8
- package/dist/{chunk-Q4K2GEMW.cjs → chunk-FVNV4WHQ.cjs} +20 -17
- package/dist/{chunk-SJIRCTIS.cjs → chunk-HSGJFCVJ.cjs} +2 -2
- package/dist/{chunk-B3CWTCK2.js → chunk-IA7FCSRY.js} +27 -2
- package/dist/{chunk-OIBRWLPY.cjs → chunk-ILO2KMER.cjs} +55 -4
- package/dist/{chunk-ZRPALBQB.cjs → chunk-IPBSNGAN.cjs} +92 -27
- package/dist/{chunk-FHVPN7BN.js → chunk-IX2U3RQT.js} +3 -3
- package/dist/{chunk-QEDMOGF3.cjs → chunk-IYK6LMQ7.cjs} +7 -7
- package/dist/{chunk-G6EKZHXY.js → chunk-J3VVM6GV.js} +4 -4
- package/dist/{chunk-LMB555QB.cjs → chunk-J74S4ZMF.cjs} +18 -18
- package/dist/{chunk-36NPLOI4.cjs → chunk-JERWCCEJ.cjs} +4 -2
- package/dist/{chunk-RVNXBF2M.js → chunk-JTDDLCSP.js} +4 -4
- package/dist/{chunk-VTEXJOSD.cjs → chunk-K53L47NH.cjs} +30 -30
- package/dist/{chunk-IJU6AS67.cjs → chunk-LJTM5ONX.cjs} +23 -23
- package/dist/{chunk-G6KACNMO.js → chunk-LJVVOVA6.js} +4 -4
- package/dist/{chunk-PDXX5UI4.cjs → chunk-LNJKBQ2B.cjs} +38 -38
- package/dist/{chunk-EM2OGPYE.js → chunk-M2GDE3NV.js} +3 -3
- package/dist/{chunk-CP7V3ATL.js → chunk-MSZNGI5P.js} +52 -5
- package/dist/{chunk-P7JEKJTZ.cjs → chunk-N2BOTBPG.cjs} +27 -16
- package/dist/{chunk-LB3ZZMKT.cjs → chunk-NFALJJCH.cjs} +12 -12
- package/dist/{chunk-XF63CZ7E.cjs → chunk-NRZRBK25.cjs} +6 -6
- package/dist/{chunk-GZPUA2TL.cjs → chunk-NTSLNIY7.cjs} +10 -10
- package/dist/{chunk-Y57AVOM2.cjs → chunk-NZJOAWJM.cjs} +4 -4
- package/dist/{chunk-RXQNRZ5F.js → chunk-OG7RKWWX.js} +3 -3
- package/dist/{chunk-VVAAZITA.cjs → chunk-OQVHCYY4.cjs} +5 -5
- package/dist/{chunk-PJFTTSKE.js → chunk-PFZ5WLIP.js} +4 -4
- package/dist/{chunk-PXVAPGTR.cjs → chunk-PHLNABJD.cjs} +3788 -1138
- package/dist/{chunk-SQXAZHDG.js → chunk-PLK3DLTF.js} +3 -3
- package/dist/{chunk-QBY7LRN2.js → chunk-PMACVMHL.js} +3 -3
- package/dist/{chunk-DJ5BNRIS.js → chunk-PV2OXNP5.js} +3684 -1041
- package/dist/{chunk-F3YT6GUF.cjs → chunk-Q6VW7MGX.cjs} +14 -14
- package/dist/{chunk-DUG3OQAS.js → chunk-QC3CAVFN.js} +187 -70
- package/dist/{chunk-NQN5LFN4.cjs → chunk-QKLEDRHN.cjs} +19 -19
- package/dist/{chunk-2EGGAZT3.js → chunk-QS27OYBU.js} +3 -3
- package/dist/{chunk-4376VHNJ.js → chunk-RUB7WL6C.js} +3 -3
- package/dist/{chunk-5LVFTGKH.js → chunk-TW2AH2ZS.js} +3 -3
- package/dist/{chunk-AATYYLYK.js → chunk-TZM6DKSJ.js} +18 -7
- package/dist/{chunk-ESSEF6U2.js → chunk-U227RWXB.js} +3 -3
- package/dist/{chunk-TC7JEO6R.cjs → chunk-U6CQOM5Q.cjs} +711 -52
- package/dist/{chunk-Z557DVYN.js → chunk-ULT2QAV5.js} +4 -4
- package/dist/{chunk-S4O7CFHQ.cjs → chunk-VSO6HPDZ.cjs} +7 -7
- package/dist/{chunk-ONPVM4IV.js → chunk-WQJMSSJT.js} +698 -45
- package/dist/{chunk-GILYQ5VH.cjs → chunk-WXSDBWGM.cjs} +4 -4
- package/dist/{chunk-UTUD6GJI.js → chunk-X5PMMK7H.js} +3 -3
- package/dist/{chunk-IFW5IW4L.cjs → chunk-X5ZLSPU2.cjs} +742 -657
- package/dist/{chunk-QM7FJB5W.js → chunk-X727CQ7X.js} +5 -5
- package/dist/{chunk-566ZB4LZ.cjs → chunk-XHZYS4QO.cjs} +5 -5
- package/dist/{chunk-RZ2EPXO3.cjs → chunk-XRQCTTDC.cjs} +8 -8
- package/dist/{chunk-5FYMCVBY.js → chunk-YBIHSN55.js} +4 -4
- package/dist/{chunk-FG6PYGIZ.js → chunk-Z574GVI3.js} +4 -4
- package/dist/{chunk-ZVQMKUGJ.js → chunk-Z5V3KA7L.js} +2 -2
- package/dist/chunk-ZGDEOXGU.js +1571 -0
- package/dist/{chunk-IS7NTKY5.cjs → chunk-ZHW6TDZ6.cjs} +14 -14
- package/dist/{chunk-CKV5NYAE.js → chunk-ZPHCTPR6.js} +4 -4
- package/dist/{chunk-3IUHTGDD.cjs → chunk-ZXXC2GXB.cjs} +38 -38
- package/dist/cli/holoscript-runner.cjs +169 -20
- package/dist/cli/holoscript-runner.js +163 -14
- package/dist/compiler/agent-inference.cjs +8 -8
- package/dist/compiler/agent-inference.js +4 -4
- package/dist/compiler/android-xr.cjs +7 -7
- package/dist/compiler/android-xr.js +5 -5
- package/dist/compiler/android.cjs +6 -6
- package/dist/compiler/android.js +4 -4
- package/dist/compiler/context.cjs +6 -6
- package/dist/compiler/context.js +3 -3
- package/dist/compiler/daimon-seed.cjs +9 -9
- package/dist/compiler/daimon-seed.js +4 -4
- package/dist/compiler/domain-block-utils.cjs +165 -165
- package/dist/compiler/domain-block-utils.js +4 -4
- package/dist/compiler/dtdl.cjs +7 -7
- package/dist/compiler/dtdl.js +4 -4
- package/dist/compiler/gltf-pipeline.cjs +8 -8
- package/dist/compiler/gltf-pipeline.js +1 -1
- package/dist/compiler/godot.cjs +6 -6
- package/dist/compiler/godot.js +5 -5
- package/dist/compiler/incremental.cjs +8 -8
- package/dist/compiler/incremental.js +3 -3
- package/dist/compiler/index.cjs +2275 -212
- package/dist/compiler/index.d.ts +29 -1
- package/dist/compiler/index.js +2078 -66
- package/dist/compiler/ios.cjs +6 -6
- package/dist/compiler/ios.js +4 -4
- package/dist/compiler/llm-provider-capabilities.cjs +6 -6
- package/dist/compiler/llm-provider-capabilities.js +3 -3
- package/dist/compiler/openxr.cjs +6 -6
- package/dist/compiler/openxr.js +5 -5
- package/dist/compiler/sdf.cjs +7 -7
- package/dist/compiler/sdf.js +5 -5
- package/dist/compiler/state.cjs +6 -6
- package/dist/compiler/state.js +4 -4
- package/dist/compiler/trait-composition.cjs +7 -7
- package/dist/compiler/trait-composition.js +3 -3
- package/dist/compiler/unity.cjs +6 -6
- package/dist/compiler/unity.js +5 -5
- package/dist/compiler/unreal.cjs +7 -7
- package/dist/compiler/unreal.js +5 -5
- package/dist/compiler/urdf.cjs +14 -14
- package/dist/compiler/urdf.js +5 -5
- package/dist/compiler/usd-physics.cjs +7 -7
- package/dist/compiler/usd-physics.js +4 -4
- package/dist/compiler/visionos.cjs +6 -6
- package/dist/compiler/visionos.js +5 -5
- package/dist/compiler/vrchat.cjs +7 -7
- package/dist/compiler/vrchat.js +5 -5
- package/dist/compiler/wasm.cjs +10 -10
- package/dist/compiler/wasm.js +5 -5
- package/dist/compiler/webgpu.cjs +6 -6
- package/dist/compiler/webgpu.js +5 -5
- package/dist/constants.cjs +99 -99
- package/dist/constants.js +3 -3
- package/dist/debugger.cjs +5 -5
- package/dist/debugger.js +3 -3
- package/dist/{dist-GRMOP2UL.cjs → dist-YGATNURH.cjs} +705 -9
- package/dist/{dist-ZBUKU2JG.js → dist-ZEZXRCBG.js} +701 -10
- package/dist/entries/scripting.cjs +8 -8
- package/dist/entries/scripting.js +4 -4
- package/dist/evolution/index.cjs +20 -20
- package/dist/evolution/index.js +8 -8
- package/dist/index.cjs +1099 -826
- package/dist/index.d.ts +78 -5
- package/dist/index.js +265 -120
- package/dist/parser/HoloCompositionTypes.d.ts +1 -1
- package/dist/parser.cjs +21 -21
- package/dist/parser.js +7 -7
- package/dist/reconstruction/index.cjs +127 -38
- package/dist/reconstruction/index.d.ts +159 -0
- package/dist/reconstruction/index.js +11 -2
- package/dist/runtime.cjs +9 -9
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +3 -3
- package/dist/traitDocs/traitDocs.cjs +8 -8
- package/dist/traitDocs/traitDocs.js +2 -2
- package/dist/traits/botanical-lotus.d.ts +61 -0
- package/dist/traits/index.cjs +761 -673
- package/dist/traits/index.d.ts +65 -0
- package/dist/traits/index.js +102 -27
- package/dist/types/HoloScriptPlus.d.ts +1 -1
- package/package.json +71 -65
- package/dist/HoloCompositionParser-6GW2BZID.cjs +0 -45
- package/dist/HoloScriptPlusParser-HQV6IJAH.js +0 -13
- package/dist/HoloScriptPlusParser-RFNHRMPE.cjs +0 -30
- package/dist/chunk-DVHZD5HV.cjs +0 -639
- package/dist/chunk-G72754QB.js +0 -625
package/dist/compiler/index.js
CHANGED
|
@@ -1,98 +1,173 @@
|
|
|
1
|
-
export { ContextCompileError, ContextCompiler, createContextCompiler } from '../chunk-
|
|
2
|
-
export { LLMCapabilityCompileError, LLMProviderCapabilitiesCompiler, createLLMProviderCapabilitiesCompiler } from '../chunk-
|
|
3
|
-
export { GLTF_PIPELINE_TOOLS, registerGLTFTools } from '../chunk-
|
|
1
|
+
export { ContextCompileError, ContextCompiler, createContextCompiler } from '../chunk-X5PMMK7H.js';
|
|
2
|
+
export { LLMCapabilityCompileError, LLMProviderCapabilitiesCompiler, createLLMProviderCapabilitiesCompiler } from '../chunk-OG7RKWWX.js';
|
|
3
|
+
export { GLTF_PIPELINE_TOOLS, registerGLTFTools } from '../chunk-M2GDE3NV.js';
|
|
4
4
|
export { BUSINESS_QUEST_TOOLS, buildVRRCompositionFromDraft, businessVRRDraftSchema, draftToHoloPreview, handleBusinessQuestToolCall, registerBusinessQuestTools, validateBusinessVRRDraft } from '../chunk-NWMJPTVL.js';
|
|
5
5
|
export { NodeToyMapper, mapNodeToyToShader } from '../chunk-TG7O4WBC.js';
|
|
6
6
|
export { createReproducibilityContext, parseReproducibilityFlags } from '../chunk-5GLCTO44.js';
|
|
7
|
-
export { QuantumCircuitCompiler } from '../chunk-
|
|
8
|
-
import { init_NextJSAPICompiler, init_DialectRegistry, DialectRegistry, init_HoloMCPCompiler, init_OmnigentAgentYamlCompiler, init_NodeServiceCompiler, init_SDKCompiler, init_LlamaServerCompiler, init_holoscript_gbnf, init_registerBuiltinDialects, init_PCGGraphCompiler } from '../chunk-
|
|
9
|
-
export { CodeEditorCompiler, CompileTimeAuthorityEffectError, CompilerBridge, DEFAULT_MATERIAL_KEYWORDS, DEFAULT_OBJECT_KEYWORDS, DEFAULT_PRIMITIVE_SHAPES, DialectRegistry, EdgeCompiler, FMUCompiler, GaussianSplattingCompiler, GaussianTrainCompiler, GaussianTrainConfigError, HOLOSCRIPT_GRAMMAR_PRESETS, HoloMCPCompiler, LlamaServerCompiler, NextJSAPICompiler, NodeServiceCompiler, OmnigentAgentYamlCompiler, PCGGraphCompiler, PCG_GRAPH_SCHEMA, REMOTE_TRAIN_EXECUTOR, REQUIRED_SANDBOX_AUTHORITY_EFFECT, SANDBOX_AUTHORITY_TRAIT, SCMCompiler, SDKCompiler, SHARED_SORT_SHADER_PATH, SOVEREIGN_TRAIN_EXECUTOR, SVGCompiler, SpatialPartitionPass, ThreeDTilesCompiler, USDZPipeline, absorbFMU, assertAuthorityEffects, bestCategoryForTraits, checkAuthorityEffects, collectAuthorityEffectNodes, compileAllToNextJSAPI, compilePCGGraphFromBlocks, compilePipelineSource, compilePipelineSourceToNode, compilePipelineSourceToPython, compilePipelineToNode, compilePipelineToPython, compileTo3DTiles, compileToFMU, compileToNextJSAPI, compileToPCGGraph, createGaussianSplattingCompiler, createGaussianTrainCompiler, createOmnigentAgentYamlCompiler, createThreeDTilesCompiler, detectMultiUserSharedSort, ensureDialectsBooted, generateHoloScriptGbnf, isHoloScriptGrammarPreset, pcgGraphToUnrealXml, quickSafetyCheck, registerBuiltinDialects, runSafetyPass, selectModality, selectModalityForAll, spatialPartition, streamWorldTiles } from '../chunk-
|
|
7
|
+
export { QuantumCircuitCompiler } from '../chunk-E4YS5SIZ.js';
|
|
8
|
+
import { init_NextJSAPICompiler, init_DialectRegistry, DialectRegistry, init_HoloMCPCompiler, init_OmnigentAgentYamlCompiler, init_NodeServiceCompiler, init_SDKCompiler, init_LlamaServerCompiler, init_holoscript_gbnf, init_registerBuiltinDialects, init_PCGGraphCompiler } from '../chunk-PV2OXNP5.js';
|
|
9
|
+
export { CodeEditorCompiler, CompileTimeAuthorityEffectError, CompilerBridge, ComputePhysicsCompiler, CpuPathTracer, DEFAULT_MATERIAL_KEYWORDS, DEFAULT_OBJECT_KEYWORDS, DEFAULT_PRIMITIVE_SHAPES, DesktopGPUCompiler, DialectRegistry, EdgeCompiler, FMUCompiler, GaussianSplattingCompiler, GaussianTrainCompiler, GaussianTrainConfigError, HOLOSCRIPT_GRAMMAR_PRESETS, HoloMCPCompiler, LlamaServerCompiler, MediaPipelineCompiler, NextJSAPICompiler, NodeServiceCompiler, OmnigentAgentYamlCompiler, PCGGraphCompiler, PCG_GRAPH_SCHEMA, PathTracerCompiler, PhysicsColliderCompiler, REMOTE_TRAIN_EXECUTOR, REQUIRED_SANDBOX_AUTHORITY_EFFECT, SANDBOX_AUTHORITY_TRAIT, SCMCompiler, SDKCompiler, SHARED_SORT_SHADER_PATH, SOVEREIGN_TRAIN_EXECUTOR, SVGCompiler, SpatialAudioCompiler, SpatialPartitionPass, ThreeDTilesCompiler, USDZPipeline, absorbFMU, assertAuthorityEffects, bestCategoryForTraits, checkAuthorityEffects, collectAuthorityEffectNodes, compileAllToNextJSAPI, compilePCGGraphFromBlocks, compilePipelineSource, compilePipelineSourceToNode, compilePipelineSourceToPython, compilePipelineToNode, compilePipelineToPython, compileTo3DTiles, compileToFMU, compileToNextJSAPI, compileToPCGGraph, createGaussianSplattingCompiler, createGaussianTrainCompiler, createOmnigentAgentYamlCompiler, createThreeDTilesCompiler, detectMultiUserSharedSort, ensureDialectsBooted, generateHoloScriptGbnf, isHoloScriptGrammarPreset, pcgGraphToUnrealXml, quickSafetyCheck, registerBuiltinDialects, runSafetyPass, selectModality, selectModalityForAll, spatialPartition, streamWorldTiles } from '../chunk-PV2OXNP5.js';
|
|
10
10
|
export { generateHullGeometry, generateMembraneGeometry, generateSplineGeometry } from '../chunk-UDE4OZMR.js';
|
|
11
11
|
import '../chunk-3F7ZZWIN.js';
|
|
12
|
-
export { SceneIRCompiler } from '../chunk-
|
|
12
|
+
export { SceneIRCompiler } from '../chunk-X727CQ7X.js';
|
|
13
13
|
import '../chunk-2MQKNDNH.js';
|
|
14
|
-
import { init_DaimonSeedCompiler } from '../chunk-
|
|
15
|
-
export { DaimonSeedCompiler, computeExportFidelity, createDaimonSeedCompiler, runHysteresisExp2 } from '../chunk-
|
|
14
|
+
import { init_DaimonSeedCompiler } from '../chunk-PMACVMHL.js';
|
|
15
|
+
export { DaimonSeedCompiler, computeExportFidelity, createDaimonSeedCompiler, runHysteresisExp2 } from '../chunk-PMACVMHL.js';
|
|
16
16
|
import { assertHoloMapManifestContract } from '../chunk-IXWGYWH6.js';
|
|
17
17
|
export { COCOExporterImpl as COCOExporter } from '../chunk-Q56TT7IL.js';
|
|
18
18
|
export { RemotionBridgeImpl as RemotionBridge } from '../chunk-YYXH45E2.js';
|
|
19
19
|
export { SemanticSceneGraph } from '../chunk-HHRZ6STP.js';
|
|
20
|
-
import { init_AgentInferenceExportTarget } from '../chunk-
|
|
21
|
-
export { AgentInferenceCompiler, AgentInferenceExportTarget_default as AgentInferenceExportTarget, createAgentInferenceCompiler, createPythonAgentInferenceCompiler } from '../chunk-
|
|
22
|
-
import { vrTraitRegistry } from '../chunk-
|
|
20
|
+
import { init_AgentInferenceExportTarget } from '../chunk-FT4FGTR6.js';
|
|
21
|
+
export { AgentInferenceCompiler, AgentInferenceExportTarget_default as AgentInferenceExportTarget, createAgentInferenceCompiler, createPythonAgentInferenceCompiler } from '../chunk-FT4FGTR6.js';
|
|
22
|
+
import { vrTraitRegistry, evaluateExpressionIR, parseExpressionToIR, ExpressionParseError } from '../chunk-AUF7HFOO.js';
|
|
23
23
|
import '../chunk-6TRX6AR2.js';
|
|
24
|
-
import '../chunk-
|
|
24
|
+
import '../chunk-Z5V3KA7L.js';
|
|
25
25
|
import { isWebGpuEnvironmentPresent } from '../chunk-K56UVEXV.js';
|
|
26
26
|
import '../chunk-OG3MBHM3.js';
|
|
27
27
|
import '../chunk-2M2DPHKB.js';
|
|
28
28
|
import '../chunk-TDCNJHWO.js';
|
|
29
29
|
import '../chunk-VKCTYLDN.js';
|
|
30
30
|
import '../chunk-YQELFNJV.js';
|
|
31
|
-
export { USDPhysicsCompiler } from '../chunk-
|
|
32
|
-
import { init_SDFCompiler } from '../chunk-
|
|
33
|
-
export { SDFCompiler } from '../chunk-
|
|
34
|
-
import { init_StateCompiler } from '../chunk-
|
|
35
|
-
export { StateCompiler } from '../chunk-
|
|
36
|
-
export { TraitCompositionCompiler } from '../chunk-
|
|
31
|
+
export { USDPhysicsCompiler } from '../chunk-Z574GVI3.js';
|
|
32
|
+
import { init_SDFCompiler } from '../chunk-YBIHSN55.js';
|
|
33
|
+
export { SDFCompiler } from '../chunk-YBIHSN55.js';
|
|
34
|
+
import { init_StateCompiler } from '../chunk-EKNWUSGW.js';
|
|
35
|
+
export { StateCompiler } from '../chunk-EKNWUSGW.js';
|
|
36
|
+
export { TraitCompositionCompiler } from '../chunk-3MQAOMKD.js';
|
|
37
37
|
import '../chunk-DQRJ5RJG.js';
|
|
38
|
-
export { IncrementalCompiler, createIncrementalCompiler } from '../chunk-
|
|
38
|
+
export { IncrementalCompiler, createIncrementalCompiler } from '../chunk-JTDDLCSP.js';
|
|
39
39
|
import '../chunk-4R6SBXDB.js';
|
|
40
|
-
import { init_AndroidCompiler } from '../chunk-
|
|
41
|
-
export { AndroidCompiler } from '../chunk-
|
|
42
|
-
import { init_AndroidXRCompiler, init_AndroidXRTraitMap, AndroidXRCompiler, getTraitMapping, ANDROIDXR_TRAIT_MAP } from '../chunk-
|
|
43
|
-
export { AndroidXRCompiler } from '../chunk-
|
|
40
|
+
import { init_AndroidCompiler } from '../chunk-3AUYBQ5I.js';
|
|
41
|
+
export { AndroidCompiler } from '../chunk-3AUYBQ5I.js';
|
|
42
|
+
import { init_AndroidXRCompiler, init_AndroidXRTraitMap, AndroidXRCompiler, getTraitMapping, ANDROIDXR_TRAIT_MAP } from '../chunk-LJVVOVA6.js';
|
|
43
|
+
export { AndroidXRCompiler } from '../chunk-LJVVOVA6.js';
|
|
44
44
|
import '../chunk-CBSDJF3B.js';
|
|
45
|
-
import { init_IOSCompiler } from '../chunk-
|
|
46
|
-
export { IOSCompiler } from '../chunk-
|
|
47
|
-
import { init_VisionOSCompiler, init_VisionOSTraitMap, getTraitMapping as getTraitMapping$1, listAllTraits, generateTraitCode } from '../chunk-
|
|
48
|
-
export { VisionOSCompiler } from '../chunk-
|
|
49
|
-
import { init_WASMCompiler } from '../chunk-
|
|
50
|
-
export { WASMCompiler } from '../chunk-
|
|
51
|
-
import { init_WebGPUCompiler, WebGPUCompiler } from '../chunk-
|
|
52
|
-
export { WebGPUCompiler } from '../chunk-
|
|
53
|
-
import { init_DTDLCompiler } from '../chunk-
|
|
54
|
-
export { DTDLCompiler } from '../chunk-
|
|
55
|
-
import { init_URDFCompiler } from '../chunk-
|
|
56
|
-
export { URDFCompiler, createURDFCompiler } from '../chunk-
|
|
45
|
+
import { init_IOSCompiler } from '../chunk-7AY66PZP.js';
|
|
46
|
+
export { IOSCompiler } from '../chunk-7AY66PZP.js';
|
|
47
|
+
import { init_VisionOSCompiler, init_VisionOSTraitMap, getTraitMapping as getTraitMapping$1, listAllTraits, generateTraitCode } from '../chunk-ULT2QAV5.js';
|
|
48
|
+
export { VisionOSCompiler } from '../chunk-ULT2QAV5.js';
|
|
49
|
+
import { init_WASMCompiler } from '../chunk-PFZ5WLIP.js';
|
|
50
|
+
export { WASMCompiler } from '../chunk-PFZ5WLIP.js';
|
|
51
|
+
import { init_WebGPUCompiler, WebGPUCompiler } from '../chunk-WQJMSSJT.js';
|
|
52
|
+
export { WebGPUCompiler } from '../chunk-WQJMSSJT.js';
|
|
53
|
+
import { init_DTDLCompiler } from '../chunk-PLK3DLTF.js';
|
|
54
|
+
export { DTDLCompiler } from '../chunk-PLK3DLTF.js';
|
|
55
|
+
import { init_URDFCompiler } from '../chunk-TZM6DKSJ.js';
|
|
56
|
+
export { URDFCompiler, createURDFCompiler } from '../chunk-TZM6DKSJ.js';
|
|
57
57
|
import '../chunk-JBQXZIWB.js';
|
|
58
|
-
import { init_OpenXRCompiler } from '../chunk-
|
|
59
|
-
export { OpenXRCompiler } from '../chunk-
|
|
60
|
-
import { init_VRChatCompiler } from '../chunk-
|
|
61
|
-
export { VRChatCompiler } from '../chunk-
|
|
62
|
-
import { init_UnityCompiler } from '../chunk-
|
|
63
|
-
export { UnityCompiler } from '../chunk-
|
|
64
|
-
import '../chunk-
|
|
58
|
+
import { init_OpenXRCompiler } from '../chunk-AAUQTR7I.js';
|
|
59
|
+
export { OpenXRCompiler } from '../chunk-AAUQTR7I.js';
|
|
60
|
+
import { init_VRChatCompiler } from '../chunk-7HDRN5BW.js';
|
|
61
|
+
export { VRChatCompiler } from '../chunk-7HDRN5BW.js';
|
|
62
|
+
import { init_UnityCompiler } from '../chunk-ZPHCTPR6.js';
|
|
63
|
+
export { UnityCompiler } from '../chunk-ZPHCTPR6.js';
|
|
64
|
+
import '../chunk-2UP2J3OK.js';
|
|
65
65
|
import { init_PlatformConditionalCompilerMixin } from '../chunk-DJNH4HFW.js';
|
|
66
66
|
export { PlatformConditionalCompilerMixin, createPlatformTarget, filterCompositionForPlatform, matchesPlatformConstraint, normalizePlatformName } from '../chunk-DJNH4HFW.js';
|
|
67
|
-
import { init_GodotCompiler } from '../chunk-
|
|
68
|
-
export { GodotCompiler } from '../chunk-
|
|
69
|
-
import '../chunk-
|
|
67
|
+
import { init_GodotCompiler } from '../chunk-ETIJOUIO.js';
|
|
68
|
+
export { GodotCompiler } from '../chunk-ETIJOUIO.js';
|
|
69
|
+
import '../chunk-TW2AH2ZS.js';
|
|
70
70
|
import { init_PerceptualColorPass } from '../chunk-UCVZXQ63.js';
|
|
71
71
|
export { SCIENTIFIC_COLOR_MAPS, analyzePerceptualColor, applyPerceptualColorPass, buildPerceptualGradient, buildPerceptualPalette, hexToSrgb, normalizeHexColor, srgbToHex } from '../chunk-UCVZXQ63.js';
|
|
72
|
-
import { init_CompilerBase, init_CompilerDocumentationGenerator, CompilerBase } from '../chunk-
|
|
73
|
-
export { CompilerBase, CompilerDocumentationGenerator, UnauthorizedCompilerAccessError, createTestCompilerToken, isCapabilityTokenCredential } from '../chunk-
|
|
74
|
-
import { init_traitDocs, getTraitDoc, getAllTraitNames } from '../chunk-
|
|
72
|
+
import { init_CompilerBase, init_CompilerDocumentationGenerator, CompilerBase } from '../chunk-QC3CAVFN.js';
|
|
73
|
+
export { CompilerBase, CompilerDocumentationGenerator, UnauthorizedCompilerAccessError, createTestCompilerToken, isCapabilityTokenCredential } from '../chunk-QC3CAVFN.js';
|
|
74
|
+
import { init_traitDocs, getTraitDoc, getAllTraitNames } from '../chunk-IA7FCSRY.js';
|
|
75
75
|
import '../chunk-5CURIYGT.js';
|
|
76
76
|
import '../chunk-33YHE7HX.js';
|
|
77
77
|
import '../chunk-GLEJ2TLS.js';
|
|
78
78
|
import '../chunk-SYTRBGDS.js';
|
|
79
79
|
import '../chunk-UNMVGZVW.js';
|
|
80
|
-
import '../chunk-
|
|
81
|
-
import '../chunk-
|
|
80
|
+
import '../chunk-QS27OYBU.js';
|
|
81
|
+
import '../chunk-ANMME7XT.js';
|
|
82
82
|
import '../chunk-7I74JWPW.js';
|
|
83
83
|
import '../chunk-2I3XAGMF.js';
|
|
84
84
|
import '../chunk-MD4IVS42.js';
|
|
85
85
|
import { ExpressionEvaluator } from '../chunk-XL6INWKT.js';
|
|
86
|
-
import '../chunk-
|
|
86
|
+
import '../chunk-6OQHLNTT.js';
|
|
87
87
|
import '../chunk-PHENW4MQ.js';
|
|
88
88
|
import '../chunk-ZVFHPMC5.js';
|
|
89
89
|
import { init_safeJsonParse, readJson } from '../chunk-RAN3ECAS.js';
|
|
90
90
|
import '../chunk-W4GABFCR.js';
|
|
91
91
|
import { ANSCapabilityPath } from '@holoscript/core-types/ans';
|
|
92
|
+
import { createHash } from 'crypto';
|
|
92
93
|
|
|
93
94
|
// src/compiler/index.ts
|
|
94
95
|
init_CompilerBase();
|
|
95
96
|
init_CompilerDocumentationGenerator();
|
|
97
|
+
|
|
98
|
+
// src/compiler/SceneIRTsxEmitter.ts
|
|
99
|
+
var GENERATED_HEADER = "// @generated by HoloScript SceneIRTsxEmitter -- DO NOT EDIT";
|
|
100
|
+
function emitSceneIRTsx(root, options = {}) {
|
|
101
|
+
const componentName = sanitizeComponentName(options.componentName ?? root.id ?? "HoloScene");
|
|
102
|
+
const sceneConstName = `${componentName}SceneIR`;
|
|
103
|
+
const imports = [
|
|
104
|
+
options.includeCanvas ?? true ? "import { Canvas } from '@react-three/fiber';" : "",
|
|
105
|
+
"import { HoloSceneIRRenderer } from '@holoscript/r3f-renderer';"
|
|
106
|
+
].filter(Boolean);
|
|
107
|
+
const source = options.sourcePath ? `// source: ${options.sourcePath}
|
|
108
|
+
` : "";
|
|
109
|
+
if (options.includeCanvas === false) {
|
|
110
|
+
return [
|
|
111
|
+
GENERATED_HEADER,
|
|
112
|
+
source.trimEnd(),
|
|
113
|
+
...imports,
|
|
114
|
+
"",
|
|
115
|
+
`const ${sceneConstName} = ${json(root)};`,
|
|
116
|
+
"",
|
|
117
|
+
`export function ${componentName}() {`,
|
|
118
|
+
` return <HoloSceneIRRenderer node={${sceneConstName}} />;`,
|
|
119
|
+
"}",
|
|
120
|
+
"",
|
|
121
|
+
`export default ${componentName};`,
|
|
122
|
+
""
|
|
123
|
+
].filter((line, idx) => line !== "" || idx !== 1).join("\n");
|
|
124
|
+
}
|
|
125
|
+
return [
|
|
126
|
+
GENERATED_HEADER,
|
|
127
|
+
source.trimEnd(),
|
|
128
|
+
...imports,
|
|
129
|
+
"",
|
|
130
|
+
`const ${sceneConstName} = ${json(root)};`,
|
|
131
|
+
"",
|
|
132
|
+
`export function ${componentName}() {`,
|
|
133
|
+
" return (",
|
|
134
|
+
" <Canvas>",
|
|
135
|
+
` <HoloSceneIRRenderer node={${sceneConstName}} />`,
|
|
136
|
+
" </Canvas>",
|
|
137
|
+
" );",
|
|
138
|
+
"}",
|
|
139
|
+
"",
|
|
140
|
+
`export default ${componentName};`,
|
|
141
|
+
""
|
|
142
|
+
].filter((line, idx) => line !== "" || idx !== 1).join("\n");
|
|
143
|
+
}
|
|
144
|
+
function sanitizeComponentName(name) {
|
|
145
|
+
const words = name.replace(/[^A-Za-z0-9_$]+/g, " ").trim().split(/\s+/).filter(Boolean);
|
|
146
|
+
const joined = words.map((word) => word[0].toUpperCase() + word.slice(1)).join("");
|
|
147
|
+
const candidate = joined || "HoloScene";
|
|
148
|
+
return /^[A-Za-z_$]/.test(candidate) ? candidate : `HoloScene${candidate}`;
|
|
149
|
+
}
|
|
150
|
+
function json(value) {
|
|
151
|
+
return JSON.stringify(normalizeValue(value), null, 2).replace(/</g, "\\u003c");
|
|
152
|
+
}
|
|
153
|
+
function normalizeValue(value) {
|
|
154
|
+
if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
|
|
155
|
+
return Array.from(value);
|
|
156
|
+
}
|
|
157
|
+
if (Array.isArray(value)) {
|
|
158
|
+
return value.map((entry) => normalizeValue(entry));
|
|
159
|
+
}
|
|
160
|
+
if (value && typeof value === "object") {
|
|
161
|
+
const out = {};
|
|
162
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
163
|
+
out[key] = normalizeValue(entry);
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/compiler/index.ts
|
|
96
171
|
init_PerceptualColorPass();
|
|
97
172
|
init_UnityCompiler();
|
|
98
173
|
init_GodotCompiler();
|
|
@@ -133,7 +208,7 @@ init_CompilerBase();
|
|
|
133
208
|
// src/compiler/Native2DCompiler.ts
|
|
134
209
|
init_CompilerBase();
|
|
135
210
|
init_safeJsonParse();
|
|
136
|
-
var
|
|
211
|
+
var _Native2DCompiler = class _Native2DCompiler extends CompilerBase {
|
|
137
212
|
constructor() {
|
|
138
213
|
super(...arguments);
|
|
139
214
|
this.compilerName = "Native2DCompiler";
|
|
@@ -146,7 +221,22 @@ var Native2DCompiler = class extends CompilerBase {
|
|
|
146
221
|
this._stateFields = /* @__PURE__ */ new Map();
|
|
147
222
|
this._fetchCalls = [];
|
|
148
223
|
this._hookCalls = [];
|
|
224
|
+
this._computedBindings = [];
|
|
149
225
|
this._options = {};
|
|
226
|
+
/** Honest mode (composition-level `@honest`): every data-bound element MUST carry
|
|
227
|
+
* `@provenance_bound` or the compiler refuses to emit it (HONEST-UNSOURCED). This is
|
|
228
|
+
* the Receipt-Bound Surface constitution — the surface cannot emit an unsourced pixel. */
|
|
229
|
+
this._honestMode = false;
|
|
230
|
+
this._verifiedViewMode = false;
|
|
231
|
+
/** Valid projection roots: composition state keys + @fetch into-slots (pre-scanned). */
|
|
232
|
+
this._projectionRoots = /* @__PURE__ */ new Set();
|
|
233
|
+
/**
|
|
234
|
+
* Verified projections collected during traversal (@verified_view v1): the machine-readable
|
|
235
|
+
* view contract that co-emits with the surface — a portable, hash-bound receipt of what
|
|
236
|
+
* every element renders, so an independent consumer can re-derive it from the ARTIFACT
|
|
237
|
+
* (not the source). Populated by resolveProjection; emitted as `export const holoViewContract`.
|
|
238
|
+
*/
|
|
239
|
+
this._collectedProjections = [];
|
|
150
240
|
}
|
|
151
241
|
// @ts-expect-error During migration
|
|
152
242
|
compile(composition, agentToken, outputPath, options) {
|
|
@@ -166,13 +256,46 @@ var Native2DCompiler = class extends CompilerBase {
|
|
|
166
256
|
this._stateFields.clear();
|
|
167
257
|
this._fetchCalls = [];
|
|
168
258
|
this._hookCalls = [];
|
|
259
|
+
this._computedBindings = [];
|
|
169
260
|
this._options = options || {};
|
|
261
|
+
this._honestMode = (composition?.traits ?? []).some((t) => t?.name === "honest");
|
|
262
|
+
this._verifiedViewMode = (composition?.traits ?? []).some((t) => t?.name === "verified_view");
|
|
170
263
|
const safeName = name.replace(/[^a-zA-Z0-9]/g, "");
|
|
171
264
|
if (composition?.state?.properties) {
|
|
172
265
|
for (const prop of composition.state.properties) {
|
|
173
266
|
this._stateFields.set(prop.key, prop.value ?? null);
|
|
174
267
|
}
|
|
175
268
|
}
|
|
269
|
+
this._projectionRoots = new Set(this._stateFields.keys());
|
|
270
|
+
this._collectedProjections = [];
|
|
271
|
+
const scanProjectionRoots = (objs) => {
|
|
272
|
+
for (const o of objs) {
|
|
273
|
+
for (const t of o.traits ?? []) {
|
|
274
|
+
if (t?.name === "fetch") {
|
|
275
|
+
const into = t.config?.into;
|
|
276
|
+
this._projectionRoots.add(typeof into === "string" && into ? into : "data");
|
|
277
|
+
}
|
|
278
|
+
if (t?.name === "each") {
|
|
279
|
+
const as = t.config?.as;
|
|
280
|
+
this._projectionRoots.add(typeof as === "string" && as ? as : "item");
|
|
281
|
+
}
|
|
282
|
+
if (t?.name === "computed") {
|
|
283
|
+
const nm = t.config?.name;
|
|
284
|
+
if (typeof nm === "string" && nm) this._projectionRoots.add(nm);
|
|
285
|
+
}
|
|
286
|
+
if (t?.name === "hook") {
|
|
287
|
+
const returns = t.config?.returns;
|
|
288
|
+
if (typeof returns === "string") {
|
|
289
|
+
for (const r of returns.split(",").map((s) => s.trim())) {
|
|
290
|
+
if (r) this._projectionRoots.add(r);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (Array.isArray(o.children)) scanProjectionRoots(o.children);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
scanProjectionRoots(objects);
|
|
176
299
|
const jsx = objects.map((obj) => this.generateReactNode(obj)).join("\n ");
|
|
177
300
|
const imports = [];
|
|
178
301
|
const reactImports = /* @__PURE__ */ new Set();
|
|
@@ -190,6 +313,17 @@ var Native2DCompiler = class extends CompilerBase {
|
|
|
190
313
|
for (const h of this._hookCalls) {
|
|
191
314
|
imports.push(`import { ${h.name} } from '${h.import}';`);
|
|
192
315
|
}
|
|
316
|
+
const computedImports = /* @__PURE__ */ new Map();
|
|
317
|
+
for (const b of this._computedBindings) {
|
|
318
|
+
if (b.from && b.uses.length) {
|
|
319
|
+
const set = computedImports.get(b.from) || /* @__PURE__ */ new Set();
|
|
320
|
+
b.uses.forEach((u) => set.add(u));
|
|
321
|
+
computedImports.set(b.from, set);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const [from, uses] of computedImports) {
|
|
325
|
+
imports.push(`import { ${[...uses].join(", ")} } from '${from}';`);
|
|
326
|
+
}
|
|
193
327
|
const stateHooks = [];
|
|
194
328
|
for (const [key, value] of this._stateFields) {
|
|
195
329
|
const capitalKey = key.charAt(0).toUpperCase() + key.slice(1);
|
|
@@ -212,25 +346,36 @@ var Native2DCompiler = class extends CompilerBase {
|
|
|
212
346
|
const hookBindings = [];
|
|
213
347
|
for (const h of this._hookCalls) {
|
|
214
348
|
const members = h.returns.split(",").map((s) => s.trim()).filter((s) => /^[A-Za-z_$][\w$]*$/.test(s));
|
|
349
|
+
const call = `${h.name}(${h.args ?? ""})`;
|
|
215
350
|
hookBindings.push(
|
|
216
|
-
members.length > 0 ? ` const { ${members.join(", ")} } = ${
|
|
351
|
+
members.length > 0 ? ` const { ${members.join(", ")} } = ${call};` : ` ${call};`
|
|
217
352
|
);
|
|
218
353
|
}
|
|
354
|
+
const computedLines = [];
|
|
355
|
+
for (const b of this._computedBindings) {
|
|
356
|
+
computedLines.push(` const ${b.name} = ${b.expr};`);
|
|
357
|
+
}
|
|
358
|
+
const viewContract = this.buildViewContract();
|
|
359
|
+
const rootAttrs = viewContract ? ` data-holo-view-contract="${viewContract.hash}"` : "";
|
|
360
|
+
const contractExport = viewContract ? `
|
|
361
|
+
|
|
362
|
+
export const holoViewContract = ${viewContract.json} as const;` : "";
|
|
219
363
|
return `${imports.join("\n")}
|
|
220
364
|
|
|
221
365
|
// @generated by HoloScript Native2DCompiler \u2014 DO NOT EDIT
|
|
222
366
|
export function ${safeName}Component() {${hookBindings.length > 0 ? "\n" + hookBindings.join("\n") : ""}
|
|
223
367
|
${stateHooks.join("\n")}
|
|
224
368
|
${fetchEffects.join("\n")}
|
|
369
|
+
${computedLines.join("\n")}
|
|
225
370
|
|
|
226
371
|
return (
|
|
227
|
-
<div className="holoscript-2d-root w-full h-full">
|
|
372
|
+
<div className="holoscript-2d-root w-full h-full"${rootAttrs}>
|
|
228
373
|
${jsx}
|
|
229
374
|
</div>
|
|
230
375
|
);
|
|
231
376
|
}
|
|
232
377
|
|
|
233
|
-
export default ${safeName}Component
|
|
378
|
+
export default ${safeName}Component;${contractExport}
|
|
234
379
|
`;
|
|
235
380
|
}
|
|
236
381
|
generateReactNode(obj) {
|
|
@@ -272,8 +417,39 @@ export default ${safeName}Component;
|
|
|
272
417
|
if (/['"`\\]/.test(importPath)) {
|
|
273
418
|
throw new Error(`Native2DCompiler @hook: invalid import path ${JSON.stringify(importPath)}`);
|
|
274
419
|
}
|
|
420
|
+
let hookArgs = "";
|
|
421
|
+
if (traits.hook.args != null) {
|
|
422
|
+
hookArgs = String(traits.hook.args);
|
|
423
|
+
if (!/^[a-zA-Z0-9_$.,(){}\[\]'"/\s*+\-%<>=?:!&|]+$/.test(hookArgs)) {
|
|
424
|
+
throw new Error(`Native2DCompiler @hook: unsafe args ${JSON.stringify(hookArgs)}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
275
427
|
if (!this._hookCalls.some((h) => h.name === hookName)) {
|
|
276
|
-
this._hookCalls.push({
|
|
428
|
+
this._hookCalls.push({
|
|
429
|
+
name: hookName,
|
|
430
|
+
import: importPath,
|
|
431
|
+
returns: String(traits.hook.returns || ""),
|
|
432
|
+
args: hookArgs || void 0
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (traits.computed?.name && traits.computed?.expr) {
|
|
437
|
+
const cname = String(traits.computed.name);
|
|
438
|
+
const expr = String(traits.computed.expr);
|
|
439
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(cname)) {
|
|
440
|
+
throw new Error(`Native2DCompiler @computed: invalid name ${JSON.stringify(cname)}`);
|
|
441
|
+
}
|
|
442
|
+
if (!/^[a-zA-Z0-9_$.,()\[\]'"/\s*+\-%<>=?:!&|]+$/.test(expr)) {
|
|
443
|
+
throw new Error(`Native2DCompiler @computed: unsafe expr ${JSON.stringify(expr)}`);
|
|
444
|
+
}
|
|
445
|
+
const from = traits.computed.from ? String(traits.computed.from) : "";
|
|
446
|
+
if (from && /['"`\\]/.test(from)) {
|
|
447
|
+
throw new Error(`Native2DCompiler @computed: invalid import path ${JSON.stringify(from)}`);
|
|
448
|
+
}
|
|
449
|
+
const usesRaw = traits.computed.uses;
|
|
450
|
+
const uses = (Array.isArray(usesRaw) ? usesRaw : usesRaw ? [usesRaw] : []).map(String).filter((u) => /^[A-Za-z_$][\w$]*$/.test(u));
|
|
451
|
+
if (!this._computedBindings.some((b) => b.name === cname)) {
|
|
452
|
+
this._computedBindings.push({ name: cname, expr, from: from || void 0, uses });
|
|
277
453
|
}
|
|
278
454
|
}
|
|
279
455
|
let tag = traits.theme?.tag || traits.panel?.tag || (typeof obj.type === "string" ? obj.type.toLowerCase() : void 0) || "div";
|
|
@@ -307,6 +483,10 @@ export default ${safeName}Component;
|
|
|
307
483
|
const styles = this.buildStyles(traits);
|
|
308
484
|
const classes = this.buildClasses(traits);
|
|
309
485
|
let props = ``;
|
|
486
|
+
const prov = this.resolveProvenance(traits, obj);
|
|
487
|
+
if (prov) props += prov.propsStr;
|
|
488
|
+
const proj = this.resolveProjection(traits, obj);
|
|
489
|
+
if (proj) props += proj;
|
|
310
490
|
if (traits.theme?.className) {
|
|
311
491
|
classes.push(traits.theme.className);
|
|
312
492
|
}
|
|
@@ -329,10 +509,15 @@ export default ${safeName}Component;
|
|
|
329
509
|
props += ` style={${JSON.stringify(combinedStyles)}}`;
|
|
330
510
|
}
|
|
331
511
|
if (tierExpr) {
|
|
332
|
-
const
|
|
512
|
+
const tierFamilies = this.tierColorFamilies(traits);
|
|
513
|
+
const cleaned = this.stripColorFamilies(
|
|
514
|
+
this.resolveColorConflicts(classes.join(" ")),
|
|
515
|
+
tierFamilies
|
|
516
|
+
);
|
|
517
|
+
const staticPrefix = cleaned ? `${cleaned} ` : "";
|
|
333
518
|
props += ` className={\`${staticPrefix}\${${tierExpr}}\`}`;
|
|
334
519
|
} else if (classes.length > 0) {
|
|
335
|
-
props += ` className="${classes.join(" ")}"`;
|
|
520
|
+
props += ` className="${this.resolveColorConflicts(classes.join(" "))}"`;
|
|
336
521
|
}
|
|
337
522
|
if (traits.theme?.attributes) {
|
|
338
523
|
try {
|
|
@@ -383,7 +568,10 @@ export default ${safeName}Component;
|
|
|
383
568
|
if (stateVar) {
|
|
384
569
|
const setter = `set${stateVar.charAt(0).toUpperCase()}${stateVar.slice(1)}`;
|
|
385
570
|
const valueExpr = subPath ? `${stateVar}.${subPath}` : stateVar;
|
|
386
|
-
|
|
571
|
+
const inputType = String(traits.input?.type || "").toLowerCase();
|
|
572
|
+
const numeric = traits.model.cast === "number" || inputType === "range" || inputType === "number";
|
|
573
|
+
const getter = numeric ? "Number(e.target.value)" : "e.target.value";
|
|
574
|
+
props += ` value={${valueExpr}} onChange={(e) => ${setter}(${getter})}`;
|
|
387
575
|
}
|
|
388
576
|
}
|
|
389
577
|
if (traits.image?.src) props += ` src="${traits.image.src}"`;
|
|
@@ -392,6 +580,9 @@ export default ${safeName}Component;
|
|
|
392
580
|
if (traits.input?.placeholder) props += ` placeholder="${traits.input.placeholder}"`;
|
|
393
581
|
if (traits.input?.type) props += ` type="${traits.input.type}"`;
|
|
394
582
|
if (traits.input?.required) props += ` required`;
|
|
583
|
+
if (traits.input?.min != null) props += ` min="${String(traits.input.min)}"`;
|
|
584
|
+
if (traits.input?.max != null) props += ` max="${String(traits.input.max)}"`;
|
|
585
|
+
if (traits.input?.step != null) props += ` step="${String(traits.input.step)}"`;
|
|
395
586
|
if (traits.button?.type) props += ` type="${traits.button.type}"`;
|
|
396
587
|
const childrenMarkup = (obj.children || obj.objects || []).map((child) => this.generateReactNode(child)).join("\n");
|
|
397
588
|
const content = traits.text?.content || traits.button?.content || traits.link?.content || traits.icon?.name;
|
|
@@ -401,12 +592,23 @@ export default ${safeName}Component;
|
|
|
401
592
|
} else if (content) {
|
|
402
593
|
safeContent = `{\`${content.replace(/`/g, "\\`").replace(/\$/g, "\\$")}\`}`;
|
|
403
594
|
}
|
|
595
|
+
if (prov && safeContent && _Native2DCompiler.PROVENANCE_GLYPH[prov.cls]) {
|
|
596
|
+
safeContent += `<sup className="holo-prov-mark" title=${JSON.stringify(
|
|
597
|
+
`provenance: ${prov.cls}`
|
|
598
|
+
)}>${_Native2DCompiler.PROVENANCE_GLYPH[prov.cls]}</sup>`;
|
|
599
|
+
}
|
|
404
600
|
const each = this.buildEachIterator(traits);
|
|
405
601
|
const keyProp = each ? ` key={i}` : "";
|
|
406
602
|
let element;
|
|
407
603
|
if (tag === "style") {
|
|
408
604
|
const escapedStyle = (content || "").replace(/`/g, "\\`").replace(/\$/g, "\\$");
|
|
409
605
|
element = `<style${keyProp} dangerouslySetInnerHTML={{ __html: \`${escapedStyle}\` }} />`;
|
|
606
|
+
} else if (traits.sparkline?.state) {
|
|
607
|
+
element = this.buildSparklineElement(traits, props, keyProp);
|
|
608
|
+
} else if (traits.chart?.state) {
|
|
609
|
+
element = this.buildChartElement(traits, props, keyProp);
|
|
610
|
+
} else if (traits.live_proof?.claim) {
|
|
611
|
+
element = this.buildLiveProofElement(traits, keyProp);
|
|
410
612
|
} else if (tag === "img" || tag === "input") {
|
|
411
613
|
element = `<${tag}${props}${keyProp} />`;
|
|
412
614
|
} else {
|
|
@@ -913,7 +1115,13 @@ export default ${safeName}Component;
|
|
|
913
1115
|
* : (snap?.fps ?? 0) >= 30 ? "text-amber-400"
|
|
914
1116
|
* : "text-red-400"
|
|
915
1117
|
*
|
|
916
|
-
* Each tier may specify `gte` (>=), `gt` (>), `lte` (<=), and/or `lt` (<)
|
|
1118
|
+
* Each tier may specify `gte` (>=), `gt` (>), `lte` (<=), and/or `lt` (<) for
|
|
1119
|
+
* numeric thresholds, OR `eq`/`neq` for exact-match categories (e.g. a
|
|
1120
|
+
* security audit outcome: `{ eq: "denied", className: "text-red-400" }`).
|
|
1121
|
+
* `eq`/`neq` accept a string or a number; string operands pass the same
|
|
1122
|
+
* injection guard as `@when eq` and are emitted via JSON.stringify. When any
|
|
1123
|
+
* tier compares strings, the bound value is coerced to `''` (not `0`) so the
|
|
1124
|
+
* cascade stays total before state loads without mis-coercing a string to 0.
|
|
917
1125
|
* Conditions on one tier are AND-ed. A tier with no bounds is the
|
|
918
1126
|
* unconditional fallback (its className is the trailing `:` branch). If no
|
|
919
1127
|
* tier is unconditional, an empty-string fallback is appended so the
|
|
@@ -927,8 +1135,11 @@ export default ${safeName}Component;
|
|
|
927
1135
|
String(bind.path || ""),
|
|
928
1136
|
"@bind tiers"
|
|
929
1137
|
);
|
|
930
|
-
const valueRef = `(${baseExpr} ?? 0)`;
|
|
931
1138
|
const tiers = bind.tiers;
|
|
1139
|
+
const usesStringCompare = tiers.some(
|
|
1140
|
+
(t) => typeof t.eq === "string" || typeof t.neq === "string"
|
|
1141
|
+
);
|
|
1142
|
+
const valueRef = usesStringCompare ? `(${baseExpr} ?? '')` : `(${baseExpr} ?? 0)`;
|
|
932
1143
|
const branches = [];
|
|
933
1144
|
for (const tier of tiers) {
|
|
934
1145
|
const className = typeof tier.className === "string" ? tier.className : "";
|
|
@@ -937,6 +1148,16 @@ export default ${safeName}Component;
|
|
|
937
1148
|
if (typeof tier.gt === "number") conditions.push(`${valueRef} > ${tier.gt}`);
|
|
938
1149
|
if (typeof tier.lte === "number") conditions.push(`${valueRef} <= ${tier.lte}`);
|
|
939
1150
|
if (typeof tier.lt === "number") conditions.push(`${valueRef} < ${tier.lt}`);
|
|
1151
|
+
if (typeof tier.eq === "number") conditions.push(`${valueRef} === ${tier.eq}`);
|
|
1152
|
+
else if (typeof tier.eq === "string")
|
|
1153
|
+
conditions.push(
|
|
1154
|
+
`${valueRef} === ${JSON.stringify(this.assertSafeLiteral(tier.eq, "@bind tier eq"))}`
|
|
1155
|
+
);
|
|
1156
|
+
if (typeof tier.neq === "number") conditions.push(`${valueRef} !== ${tier.neq}`);
|
|
1157
|
+
else if (typeof tier.neq === "string")
|
|
1158
|
+
conditions.push(
|
|
1159
|
+
`${valueRef} !== ${JSON.stringify(this.assertSafeLiteral(tier.neq, "@bind tier neq"))}`
|
|
1160
|
+
);
|
|
940
1161
|
branches.push({
|
|
941
1162
|
condition: conditions.length > 0 ? conditions.join(" && ") : null,
|
|
942
1163
|
className
|
|
@@ -959,6 +1180,309 @@ export default ${safeName}Component;
|
|
|
959
1180
|
fallback
|
|
960
1181
|
);
|
|
961
1182
|
}
|
|
1183
|
+
/**
|
|
1184
|
+
* @sparkline: render a bound numeric array as an inline SVG polyline — the
|
|
1185
|
+
* smallest slice of native data-viz (no axes/legend). The points are computed
|
|
1186
|
+
* at render time from the array, normalized into a fixed viewBox
|
|
1187
|
+
* (`width`×`height`, default 100×30) with `preserveAspectRatio="none"` so the
|
|
1188
|
+
* SVG fills its CSS box. `state`(+optional `path`) is the array; `valueKey`
|
|
1189
|
+
* reads a number out of object items (e.g. items `{sizeKb}` → valueKey
|
|
1190
|
+
* "sizeKb"); `stroke` is a themeable line-color class (default
|
|
1191
|
+
* `stroke-studio-accent`). Container styling (`@theme`/`@layout`) rides in on
|
|
1192
|
+
* `props`. Dependency-free, injection-safe: identifiers are validated and the
|
|
1193
|
+
* points expression uses string concatenation (no inner template literals).
|
|
1194
|
+
*/
|
|
1195
|
+
buildSparklineElement(traits, props, keyProp) {
|
|
1196
|
+
const sp = traits.sparkline;
|
|
1197
|
+
const state = this.assertSafeDotPath(String(sp.state), "@sparkline state");
|
|
1198
|
+
const path = sp.path ? String(sp.path) : "";
|
|
1199
|
+
if (path) this.assertSafeDotPath(path, "@sparkline path");
|
|
1200
|
+
const arrayRef = this.buildStatePathExpr(state, path, "@sparkline");
|
|
1201
|
+
const W = Number.isInteger(sp.width) && sp.width > 0 ? sp.width : 100;
|
|
1202
|
+
const H = Number.isInteger(sp.height) && sp.height > 0 ? sp.height : 30;
|
|
1203
|
+
let valueExpr = "d";
|
|
1204
|
+
if (sp.valueKey != null) {
|
|
1205
|
+
const vk = String(sp.valueKey);
|
|
1206
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(vk)) {
|
|
1207
|
+
throw new Error(`Native2DCompiler @sparkline: invalid valueKey ${JSON.stringify(vk)}`);
|
|
1208
|
+
}
|
|
1209
|
+
valueExpr = `d?.${vk}`;
|
|
1210
|
+
}
|
|
1211
|
+
const stroke = typeof sp.stroke === "string" ? this.assertSafeLiteral(sp.stroke, "@sparkline stroke") : "stroke-studio-accent";
|
|
1212
|
+
const strokeWidth = /^[0-9]+(\.[0-9]+)?$/.test(String(sp.strokeWidth ?? "")) ? String(sp.strokeWidth) : "1.5";
|
|
1213
|
+
const points = `((__a) => { const __v = (__a ?? []).map((d) => Number(${valueExpr}) || 0); if (!__v.length) return ''; const __mn = Math.min(...__v), __mx = Math.max(...__v), __r = (__mx - __mn) || 1, __sx = __v.length > 1 ? ${W} / (__v.length - 1) : 0; return __v.map((y, i) => (i * __sx).toFixed(2) + ',' + (${H} - ((y - __mn) / __r) * ${H}).toFixed(2)).join(' '); })(${arrayRef})`;
|
|
1214
|
+
return `<svg${props}${keyProp} data-baseline="min" viewBox="0 0 ${W} ${H}" preserveAspectRatio="none">
|
|
1215
|
+
<polyline fill="none" className="${stroke}" strokeWidth="${strokeWidth}" points={${points}} />
|
|
1216
|
+
</svg>`;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* @chart: the fuller native data-viz primitive — a bar / line / area chart with
|
|
1220
|
+
* a baseline axis and (bar) crisp category labels. Unlike @sparkline (fill-the-box,
|
|
1221
|
+
* no axes), @chart uses a fixed-aspect viewBox with margins so `<text>` labels stay
|
|
1222
|
+
* undistorted. Geometry is computed at render time from the bound array; a bar chart
|
|
1223
|
+
* emits one `<rect>` per item, line/area emit a `<polyline>`/`<polygon>` over the
|
|
1224
|
+
* plot region. Injection-safe: `state`/`path`/`valueKey`/`labelKey` are validated
|
|
1225
|
+
* identifiers/dot-paths, `stroke`/`fill` pass the literal guard, and the point math
|
|
1226
|
+
* uses string concatenation (no inner template literals).
|
|
1227
|
+
*
|
|
1228
|
+
* `@chart { kind: "bar"|"line"|"area", state, path?, valueKey?, labelKey?,
|
|
1229
|
+
* width?, height?, stroke?, fill? }`
|
|
1230
|
+
*/
|
|
1231
|
+
buildChartElement(traits, props, keyProp) {
|
|
1232
|
+
const ch = traits.chart;
|
|
1233
|
+
const kind = ch.kind === "line" || ch.kind === "area" ? ch.kind : "bar";
|
|
1234
|
+
const state = this.assertSafeDotPath(String(ch.state), "@chart state");
|
|
1235
|
+
const path = ch.path ? String(ch.path) : "";
|
|
1236
|
+
if (path) this.assertSafeDotPath(path, "@chart path");
|
|
1237
|
+
const arrayRef = this.buildStatePathExpr(state, path, "@chart");
|
|
1238
|
+
let valueExpr = "d";
|
|
1239
|
+
if (ch.valueKey != null) {
|
|
1240
|
+
const vk = String(ch.valueKey);
|
|
1241
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(vk)) {
|
|
1242
|
+
throw new Error(`Native2DCompiler @chart: invalid valueKey ${JSON.stringify(vk)}`);
|
|
1243
|
+
}
|
|
1244
|
+
valueExpr = `d?.${vk}`;
|
|
1245
|
+
}
|
|
1246
|
+
let labelKey = "";
|
|
1247
|
+
if (ch.labelKey != null) {
|
|
1248
|
+
labelKey = String(ch.labelKey);
|
|
1249
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(labelKey)) {
|
|
1250
|
+
throw new Error(`Native2DCompiler @chart: invalid labelKey ${JSON.stringify(labelKey)}`);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
let classKey = "";
|
|
1254
|
+
if (ch.classKey != null) {
|
|
1255
|
+
classKey = String(ch.classKey);
|
|
1256
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(classKey)) {
|
|
1257
|
+
throw new Error(`Native2DCompiler @chart: invalid classKey ${JSON.stringify(classKey)}`);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
const W = Number.isInteger(ch.width) && ch.width > 0 ? ch.width : 280;
|
|
1261
|
+
const H = Number.isInteger(ch.height) && ch.height > 0 ? ch.height : 140;
|
|
1262
|
+
const stroke = typeof ch.stroke === "string" ? this.assertSafeLiteral(ch.stroke, "@chart stroke") : "stroke-studio-accent";
|
|
1263
|
+
const fill = typeof ch.fill === "string" ? this.assertSafeLiteral(ch.fill, "@chart fill") : "fill-studio-accent";
|
|
1264
|
+
const PX = 6;
|
|
1265
|
+
const PT = 8;
|
|
1266
|
+
const PB = labelKey && kind === "bar" ? 16 : 6;
|
|
1267
|
+
const plotW = W - 2 * PX;
|
|
1268
|
+
const plotH = H - PT - PB;
|
|
1269
|
+
const baselineY = H - PB;
|
|
1270
|
+
const baseline = `<line x1="${PX}" y1="${baselineY}" x2="${W - PX}" y2="${baselineY}" className="stroke-studio-border" strokeWidth="0.5" />`;
|
|
1271
|
+
const provDefs = classKey ? `<defs><pattern id="holo-hatch" width="4" height="4" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="4" className="stroke-studio-accent" strokeWidth="1.2" /></pattern><pattern id="holo-dots" width="3" height="3" patternUnits="userSpaceOnUse"><circle cx="1.5" cy="1.5" r="0.6" className="fill-studio-accent" /></pattern></defs>` : "";
|
|
1272
|
+
let body;
|
|
1273
|
+
if (kind === "bar") {
|
|
1274
|
+
const label = labelKey ? `<text x={__x + __bw / 2} y={${H - 4}} textAnchor="middle" className="fill-studio-muted" fontSize="6">{String(d?.${labelKey} ?? '')}</text>` : "";
|
|
1275
|
+
const provFill = classKey ? ` fill={d?.${classKey} === "inferred" ? "url(#holo-hatch)" : d?.${classKey} === "generative" ? "url(#holo-dots)" : undefined} data-provenance-class={String(d?.${classKey} ?? "")}` : "";
|
|
1276
|
+
body = `{((__a) => { const __d = (__a ?? []); const __v = __d.map((d) => Number(${valueExpr}) || 0); const __max = Math.max(1, ...__v); const __n = __d.length || 1; const __slot = ${plotW} / __n; const __bw = Math.max(1, Math.min(__slot * 0.62, __slot - 1)); return __d.map((d, i) => { const __h = Math.max(0, Number(${valueExpr}) || 0) / __max * ${plotH}; const __x = ${PX} + i * __slot + (__slot - __bw) / 2; const __y = ${baselineY} - __h; return (<g key={i}><rect x={__x} y={__y} width={__bw} height={__h} className="${fill}"${provFill} rx="0.5" />${label}</g>); }); })(${arrayRef})}`;
|
|
1277
|
+
} else {
|
|
1278
|
+
const norm = this._honestMode ? `const __mn = 0, __r = Math.max(1, ...__v), ` : `const __mn = Math.min(...__v), __mx = Math.max(...__v), __r = (__mx - __mn) || 1, `;
|
|
1279
|
+
const yExpr = this._honestMode ? "Math.max(0, y)" : "y";
|
|
1280
|
+
const pts = `((__a) => { const __v = (__a ?? []).map((d) => Number(${valueExpr}) || 0); if (!__v.length) return ''; ` + norm + `__sx = __v.length > 1 ? ${plotW} / (__v.length - 1) : 0; return __v.map((y, i) => (${PX} + i * __sx).toFixed(2) + ',' + (${baselineY} - ((${yExpr} - __mn) / __r) * ${plotH}).toFixed(2)).join(' '); })(${arrayRef})`;
|
|
1281
|
+
const line = `<polyline fill="none" className="${stroke}" strokeWidth="1.5" points={${pts}} />`;
|
|
1282
|
+
if (kind === "area") {
|
|
1283
|
+
const areaPts = `((__p) => __p ? __p + ' ' + ${W - PX} + ',' + ${baselineY} + ' ' + ${PX} + ',' + ${baselineY} : '')(${pts})`;
|
|
1284
|
+
body = `<polygon className="${fill}" fillOpacity="0.25" points={${areaPts}} />
|
|
1285
|
+
${line}`;
|
|
1286
|
+
} else {
|
|
1287
|
+
body = line;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const baselineAttr = ` data-baseline="${kind === "bar" || this._honestMode ? "zero" : "min"}"`;
|
|
1291
|
+
const clampedAttr = kind === "bar" || this._honestMode ? ` data-clamped={String(((__a) => (__a ?? []).some((d) => (Number(${valueExpr}) || 0) < 0))(${arrayRef}))}` : "";
|
|
1292
|
+
return `<svg${props}${keyProp}${baselineAttr}${clampedAttr} viewBox="0 0 ${W} ${H}">
|
|
1293
|
+
${provDefs}
|
|
1294
|
+
${baseline}
|
|
1295
|
+
${body}
|
|
1296
|
+
</svg>`;
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Receipt-Bound Surface enforcement + emission (@honest / @provenance_bound).
|
|
1300
|
+
*
|
|
1301
|
+
* A "data-bound" element (one that renders a value from state via @bind/@chart/
|
|
1302
|
+
* @sparkline/@model) must, in honest mode, carry `@provenance_bound {source, class}`
|
|
1303
|
+
* — otherwise the compiler THROWS (HONEST-UNSOURCED): the surface cannot emit an
|
|
1304
|
+
* unsourced pixel. When provenance IS present, this returns the inline receipt
|
|
1305
|
+
* attributes (`data-holo-provenance` = a machine-readable {source,class,confidence}
|
|
1306
|
+
* an independent consumer can re-derive against, `data-provenance-class`) plus the
|
|
1307
|
+
* class so the caller can add a visible honesty glyph to text values. An unknown
|
|
1308
|
+
* class is rejected (fail toward lower trust — never silently upgraded to measured).
|
|
1309
|
+
* Returns null when the element is not provenance-bound (no attributes, no glyph).
|
|
1310
|
+
*/
|
|
1311
|
+
/**
|
|
1312
|
+
* @verified_view / @projects — the admission gate for agent-authored
|
|
1313
|
+
* surfaces (slice 4 of the Receipt-Bound Surface, honest v0).
|
|
1314
|
+
*
|
|
1315
|
+
* `@projects { node: "stats.sessions" }` is the AUTHOR'S CLAIM of which
|
|
1316
|
+
* world-model node an element renders. This compiler owns the actual data
|
|
1317
|
+
* path (@bind/@chart/@sparkline/@model → state[.path]), so the claim is
|
|
1318
|
+
* verified as compiler-owned DATA-FLOW — the two sides are independently
|
|
1319
|
+
* authored, which is exactly what the shallow "source is non-empty" version
|
|
1320
|
+
* (rejected as theater in the slice-4 warning) lacks. Checks, all fatal
|
|
1321
|
+
* (VIEW-UNGROUNDED):
|
|
1322
|
+
* 1. a declared projection must MATCH the element's actual bound path —
|
|
1323
|
+
* "the agent says sessions but wired revenue" fails to compile;
|
|
1324
|
+
* 2. a projection must root in a REAL node (composition state or a @fetch
|
|
1325
|
+
* into-slot) — a hallucinated node fails to compile;
|
|
1326
|
+
* 3. a projection on an element with NO data binding is a lie by
|
|
1327
|
+
* construction — fails to compile;
|
|
1328
|
+
* 4. under composition-level `@verified_view`, every data-bound element
|
|
1329
|
+
* MUST declare a projection (mirrors HONEST-UNSOURCED).
|
|
1330
|
+
* Checks 1–3 are always-on (a lying receipt is worse than none); check 4 is
|
|
1331
|
+
* the mode. Verified elements emit `data-holo-projects` so independent
|
|
1332
|
+
* consumers can re-check the claim against the artifact. HONEST SCOPE:
|
|
1333
|
+
* runtime re-derivation against a live graph.holo world model (the full
|
|
1334
|
+
* research-spec `projects:` semantics + SimulationContract co-emission) is
|
|
1335
|
+
* v1+ — this v0 proves what the compiler can prove, statically.
|
|
1336
|
+
*/
|
|
1337
|
+
resolveProjection(traits, obj) {
|
|
1338
|
+
const nm = () => String(obj.name ?? "element");
|
|
1339
|
+
const src = traits.bind?.state ? { state: traits.bind.state, path: traits.bind.path } : traits.chart?.state ? { state: traits.chart.state, path: traits.chart.path } : traits.sparkline?.state ? { state: traits.sparkline.state, path: traits.sparkline.path } : traits.model?.state ? { state: traits.model.state, path: traits.model.path } : (
|
|
1340
|
+
// @each renders a LIST from state — the element's provenance is the bound array.
|
|
1341
|
+
traits.each?.state ? { state: traits.each.state, path: traits.each.path } : null
|
|
1342
|
+
);
|
|
1343
|
+
const actualPath = src ? `${String(src.state)}${src.path ? "." + String(src.path) : ""}` : null;
|
|
1344
|
+
const pj = traits.projects;
|
|
1345
|
+
if (!pj) {
|
|
1346
|
+
if (this._verifiedViewMode && actualPath) {
|
|
1347
|
+
throw new Error(
|
|
1348
|
+
`Native2DCompiler @verified_view: VIEW-UNGROUNDED \u2014 "${nm()}" renders data without @projects {node}; an agent-authored surface ships only if every element declares what it renders`
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
return null;
|
|
1352
|
+
}
|
|
1353
|
+
const node = this.assertSafeDotPath(String(pj.node ?? ""), "@projects node");
|
|
1354
|
+
if (!actualPath) {
|
|
1355
|
+
throw new Error(
|
|
1356
|
+
`Native2DCompiler @projects: VIEW-UNGROUNDED \u2014 "${nm()}" claims to project "${node}" but has no data binding at all; the claim is a lie by construction`
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
if (node !== actualPath) {
|
|
1360
|
+
throw new Error(
|
|
1361
|
+
`Native2DCompiler @projects: VIEW-UNGROUNDED \u2014 "${nm()}" claims to project "${node}" but is actually bound to "${actualPath}"; the surface would lie about its source`
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
const root = node.split(".")[0];
|
|
1365
|
+
if (!this._projectionRoots.has(root)) {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
`Native2DCompiler @projects: VIEW-UNGROUNDED \u2014 "${nm()}" projects "${node}" but no state node "${root}" exists (hallucinated node; declared state keys and @fetch slots: ${[...this._projectionRoots].join(", ") || "none"})`
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
const b = traits.bind;
|
|
1371
|
+
const identity = !!b?.state && b.precision === void 0 && b.prefix === void 0 && b.suffix === void 0 && !b.tiers && !traits.chart && !traits.sparkline && !traits.each && !traits.model;
|
|
1372
|
+
let transform;
|
|
1373
|
+
if (b?.state && !traits.chart && !traits.sparkline && !traits.each && !traits.model && (b.precision !== void 0 || b.prefix !== void 0 || b.suffix !== void 0)) {
|
|
1374
|
+
transform = {};
|
|
1375
|
+
if (typeof b.precision === "number") transform.precision = b.precision;
|
|
1376
|
+
if (b.prefix !== void 0) transform.prefix = String(b.prefix);
|
|
1377
|
+
if (b.suffix !== void 0) transform.suffix = String(b.suffix);
|
|
1378
|
+
}
|
|
1379
|
+
let entity;
|
|
1380
|
+
if (pj.entity !== void 0) {
|
|
1381
|
+
const e = String(pj.entity);
|
|
1382
|
+
if (!/^[A-Za-z0-9_.:-]+$/.test(e)) {
|
|
1383
|
+
throw new Error(
|
|
1384
|
+
`Native2DCompiler @projects: invalid entity id ${JSON.stringify(e)} on "${nm()}" (allowed: alphanumerics and . : _ -)`
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
entity = e;
|
|
1388
|
+
}
|
|
1389
|
+
this._collectedProjections.push({
|
|
1390
|
+
element: nm(),
|
|
1391
|
+
node,
|
|
1392
|
+
...entity ? { entity } : {},
|
|
1393
|
+
identity,
|
|
1394
|
+
...transform ? { transform } : {}
|
|
1395
|
+
});
|
|
1396
|
+
return ` data-holo-projects="${node}"`;
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* @verified_view v1 — build the co-emitted view contract: a portable, sha256-bound JSON
|
|
1400
|
+
* receipt of every verified projection plus the valid state roots. The contract travels
|
|
1401
|
+
* WITH the compiled artifact (`export const holoViewContract` + a `data-holo-view-contract`
|
|
1402
|
+
* hash on the root), so an independent consumer re-derives the projection set from the
|
|
1403
|
+
* emitted `data-holo-projects` attributes and checks it against this contract — the two
|
|
1404
|
+
* emission paths cannot silently drift, and the surface's honesty becomes one verifiable
|
|
1405
|
+
* receipt instead of N scattered attributes (W.772). Returns null outside @verified_view
|
|
1406
|
+
* mode (un-gated surfaces carry no contract). Canonical: projections sorted by
|
|
1407
|
+
* (element, node) and roots sorted, so the hash is stable across traversal order.
|
|
1408
|
+
*
|
|
1409
|
+
* NOTE (v1 scope): this makes the projection contract portable and independently
|
|
1410
|
+
* re-checkable from the ARTIFACT — the foundation for runtime re-derivation. Verifying the
|
|
1411
|
+
* bound value against a LIVE graph.holo world model at runtime is the deeper v1 still to be
|
|
1412
|
+
* designed.
|
|
1413
|
+
*/
|
|
1414
|
+
buildViewContract() {
|
|
1415
|
+
if (!this._verifiedViewMode) return null;
|
|
1416
|
+
const projections = [...this._collectedProjections].sort(
|
|
1417
|
+
(a, b) => a.element === b.element ? a.node.localeCompare(b.node) : a.element.localeCompare(b.element)
|
|
1418
|
+
);
|
|
1419
|
+
const stateRoots = [...this._projectionRoots].sort();
|
|
1420
|
+
const body = { version: _Native2DCompiler.VIEW_CONTRACT_VERSION, projections, stateRoots };
|
|
1421
|
+
const hash = createHash("sha256").update(JSON.stringify(body)).digest("hex");
|
|
1422
|
+
return { json: JSON.stringify({ ...body, contractHash: hash }), hash };
|
|
1423
|
+
}
|
|
1424
|
+
resolveProvenance(traits, obj) {
|
|
1425
|
+
const dataBound = !!(traits.bind?.state || traits.chart?.state && !traits.chart?.classKey || traits.sparkline?.state || traits.model);
|
|
1426
|
+
const pb = traits.provenance_bound;
|
|
1427
|
+
if (this._honestMode && dataBound && !pb) {
|
|
1428
|
+
const nm = String(obj.name ?? "element");
|
|
1429
|
+
throw new Error(
|
|
1430
|
+
`Native2DCompiler @honest: HONEST-UNSOURCED \u2014 "${nm}" renders data without @provenance_bound {source, class}; the surface cannot emit an unsourced pixel`
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
if (!pb) return null;
|
|
1434
|
+
const cls = String(pb.class ?? "");
|
|
1435
|
+
if (!_Native2DCompiler.PROVENANCE_CLASSES.has(cls)) {
|
|
1436
|
+
throw new Error(
|
|
1437
|
+
`Native2DCompiler @provenance_bound: invalid class ${JSON.stringify(cls)} \u2014 expected measured|derived|inferred|generative (unknown classes fail toward lower trust, never default to measured)`
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
const source = this.assertSafeLiteral(String(pb.source ?? ""), "@provenance_bound source");
|
|
1441
|
+
const receipt = { source, class: cls };
|
|
1442
|
+
if (pb.confidence != null) {
|
|
1443
|
+
const c = Number(pb.confidence);
|
|
1444
|
+
if (Number.isFinite(c) && c >= 0 && c <= 1) receipt.confidence = c;
|
|
1445
|
+
}
|
|
1446
|
+
const propsStr = ` data-holo-provenance='${JSON.stringify(receipt)}' data-provenance-class="${cls}"`;
|
|
1447
|
+
return { propsStr, cls };
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* @live_proof: the falsifiable surface — the receipt is load-bearing RENDER STATE.
|
|
1451
|
+
*
|
|
1452
|
+
* `@live_proof { claim, label? }` declares a boolean claim over state (a theorem the
|
|
1453
|
+
* element asserts). It compiles to a self-styled verdict badge whose colour + text
|
|
1454
|
+
* FLIP live as the claim breaks: `✓ <label> holds` (green) vs `✗ <label> FALSIFIED`
|
|
1455
|
+
* (red). Because the claim re-evaluates on every render, dragging a bound parameter
|
|
1456
|
+
* (via a sibling @model slider) past the point where the theorem fails flips the
|
|
1457
|
+
* badge in-band — FALSIFIED is a render state, not a console log.
|
|
1458
|
+
*
|
|
1459
|
+
* DISCIPLINE (W.767/W.769): the claim must re-derive from INDEPENDENT state fields
|
|
1460
|
+
* (e.g. `capacity >= load * factor`), never re-read the displayed value — a
|
|
1461
|
+
* self-referential claim proves nothing. The compiler cannot enforce independence,
|
|
1462
|
+
* but the emitted `data-proof-claim` makes the predicate auditable. The claim is
|
|
1463
|
+
* injection-safe (same char-class as @computed; no backticks/semicolons).
|
|
1464
|
+
*
|
|
1465
|
+
* HONESTY LABEL (@verified_view v1 groundwork): the emitted `data-proof-state` re-runs the
|
|
1466
|
+
* SAME `cond` the badge renders from, so today it proves DISPLAY-FAITHFULNESS (the badge
|
|
1467
|
+
* matches its own formula), NOT independently-verified TRUTH of the claim. We refuse to let
|
|
1468
|
+
* that masquerade — `data-proof-independence="self-referential"` marks it so no consumer (or
|
|
1469
|
+
* the moat's own claims) mistakes it for a real proof. A v1 runtime verifier that re-derives
|
|
1470
|
+
* the claim's inputs through a path the render did not author (an external state store or a
|
|
1471
|
+
* solver/StateAuthority oracle) flips this to `"verified"`. Until that oracle exists, honest
|
|
1472
|
+
* labeling is the only non-theatre option (see research/2026-07-10_verified-view-v1-design.md).
|
|
1473
|
+
*/
|
|
1474
|
+
buildLiveProofElement(traits, keyProp) {
|
|
1475
|
+
const lp = traits.live_proof;
|
|
1476
|
+
const claim = String(lp.claim ?? "");
|
|
1477
|
+
if (!claim.trim() || !/^[a-zA-Z0-9_$.,()\[\]'"/\s*+\-%<>=?:!&|]+$/.test(claim)) {
|
|
1478
|
+
throw new Error(`Native2DCompiler @live_proof: unsafe or empty claim ${JSON.stringify(claim)}`);
|
|
1479
|
+
}
|
|
1480
|
+
const label = lp.label != null ? this.assertSafeLiteral(String(lp.label), "@live_proof label") : "Claim";
|
|
1481
|
+
const cond = `(${claim})`;
|
|
1482
|
+
return `<div${keyProp} data-proof-claim={${JSON.stringify(claim)}} data-proof-independence="self-referential" data-proof-state={${cond} ? "pass" : "falsified"} className={\`rounded-md p-2 text-xs font-semibold \${${cond} ? "bg-studio-success/10 text-studio-success" : "bg-studio-error/10 text-studio-error"}\`}>
|
|
1483
|
+
{${cond} ? "\u2713 ${label} holds" : "\u2717 ${label} FALSIFIED"}
|
|
1484
|
+
</div>`;
|
|
1485
|
+
}
|
|
962
1486
|
buildHTMLBindAttributes(traits, staticClassName) {
|
|
963
1487
|
const bind = traits.bind;
|
|
964
1488
|
if (!bind?.state) return "";
|
|
@@ -1214,6 +1738,78 @@ export default ${safeName}Component;
|
|
|
1214
1738
|
}
|
|
1215
1739
|
return styles;
|
|
1216
1740
|
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Resolve conflicting Tailwind COLOR utilities in a STATIC class string, keeping the LAST
|
|
1743
|
+
* of each color family. A node's @theme className is appended AFTER the variant defaults
|
|
1744
|
+
* (buildClasses), so this lets an authored token color win over a hardcoded default color
|
|
1745
|
+
* (e.g. @theme `text-studio-muted` overrides the caption default `text-gray-500`) — which
|
|
1746
|
+
* class actually wins otherwise depends on Tailwind's CSS emission order, not the class
|
|
1747
|
+
* attribute, so the default's raw color leaks unpredictably. Only color families are deduped
|
|
1748
|
+
* (text/bg/border/ring/…, incl. variant prefixes like hover:/focus:); sizes, spacing, and
|
|
1749
|
+
* layout are untouched, and text-<size> / border-<width> / ring-<width> are never colors.
|
|
1750
|
+
*/
|
|
1751
|
+
/**
|
|
1752
|
+
* The Tailwind COLOR family a token belongs to (incl. variant prefixes like
|
|
1753
|
+
* hover:/focus:), or null for a non-color token. Shared by conflict-resolution
|
|
1754
|
+
* and tier-family stripping. text-<size> / text-<align> / border|ring|divide-<width>
|
|
1755
|
+
* and arbitrary values ([10px], [#fff]) are NOT colors.
|
|
1756
|
+
*/
|
|
1757
|
+
colorFamilyKey(t) {
|
|
1758
|
+
const m = t.match(
|
|
1759
|
+
/^((?:[a-z-]+:)*)(text|bg|border|ring|ring-offset|divide|accent|fill|stroke|placeholder|caret|decoration|from|to|via)-(.+)$/
|
|
1760
|
+
);
|
|
1761
|
+
if (!m) return null;
|
|
1762
|
+
const [, variant, prop, rest] = m;
|
|
1763
|
+
if (rest.startsWith("[")) return null;
|
|
1764
|
+
if (prop === "text") {
|
|
1765
|
+
if (_Native2DCompiler.TEXT_SIZE_TOKENS.has(`text-${rest}`)) return null;
|
|
1766
|
+
if (["left", "center", "right", "justify", "start", "end"].includes(rest)) return null;
|
|
1767
|
+
}
|
|
1768
|
+
if (prop === "border" || prop === "divide") {
|
|
1769
|
+
if (/^([xytrbles]|\d+|[xytrbles]-\d+)$/.test(rest)) return null;
|
|
1770
|
+
}
|
|
1771
|
+
if (prop === "ring" && /^(\d+|inset)$/.test(rest)) return null;
|
|
1772
|
+
return `${variant}${prop}`;
|
|
1773
|
+
}
|
|
1774
|
+
resolveColorConflicts(classStr) {
|
|
1775
|
+
const tokens = classStr.split(/\s+/).filter(Boolean);
|
|
1776
|
+
const lastIndex = /* @__PURE__ */ new Map();
|
|
1777
|
+
tokens.forEach((t, i) => {
|
|
1778
|
+
const k = this.colorFamilyKey(t);
|
|
1779
|
+
if (k) lastIndex.set(k, i);
|
|
1780
|
+
});
|
|
1781
|
+
return tokens.filter((t, i) => {
|
|
1782
|
+
const k = this.colorFamilyKey(t);
|
|
1783
|
+
return k === null || lastIndex.get(k) === i;
|
|
1784
|
+
}).join(" ");
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* The set of color families a @bind's tier classNames assign — so the caller
|
|
1788
|
+
* can strip those families from the static prefix (the dynamic tier cascade
|
|
1789
|
+
* owns them, and the static default would otherwise leak a raw color).
|
|
1790
|
+
*/
|
|
1791
|
+
tierColorFamilies(traits) {
|
|
1792
|
+
const families = /* @__PURE__ */ new Set();
|
|
1793
|
+
const tiers = traits?.bind?.tiers;
|
|
1794
|
+
if (!Array.isArray(tiers)) return families;
|
|
1795
|
+
for (const tier of tiers) {
|
|
1796
|
+
const cn = typeof tier?.className === "string" ? tier.className : "";
|
|
1797
|
+
for (const tok of cn.split(/\s+/).filter(Boolean)) {
|
|
1798
|
+
const k = this.colorFamilyKey(tok);
|
|
1799
|
+
if (k) families.add(k);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
return families;
|
|
1803
|
+
}
|
|
1804
|
+
/** Drop every token whose color family is in `families` (removes a static
|
|
1805
|
+
* default color that a dynamic @bind tier cascade will always override). */
|
|
1806
|
+
stripColorFamilies(classStr, families) {
|
|
1807
|
+
if (families.size === 0) return classStr;
|
|
1808
|
+
return classStr.split(/\s+/).filter(Boolean).filter((t) => {
|
|
1809
|
+
const k = this.colorFamilyKey(t);
|
|
1810
|
+
return k === null || !families.has(k);
|
|
1811
|
+
}).join(" ");
|
|
1812
|
+
}
|
|
1217
1813
|
buildClasses(traits) {
|
|
1218
1814
|
const classes = [];
|
|
1219
1815
|
if (traits.text) {
|
|
@@ -1256,6 +1852,48 @@ export default ${safeName}Component;
|
|
|
1256
1852
|
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
1257
1853
|
}
|
|
1258
1854
|
};
|
|
1855
|
+
/** Version tag for the co-emitted `holoViewContract` (@verified_view v1). */
|
|
1856
|
+
_Native2DCompiler.VIEW_CONTRACT_VERSION = "holo-view-contract-v1";
|
|
1857
|
+
/**
|
|
1858
|
+
* 2D data-value provenance vocabulary, ordered by TRUST (highest → lowest),
|
|
1859
|
+
* aligned to the shipped 3D PointProvenanceClass idiom (observed | interpolated |
|
|
1860
|
+
* nlos-inferred | generative-extended). An UNKNOWN class is REJECTED, never
|
|
1861
|
+
* silently upgraded to `measured` — provenance fails toward lower trust, so a
|
|
1862
|
+
* typo can never make an inferred value read as sensor-attested.
|
|
1863
|
+
*/
|
|
1864
|
+
_Native2DCompiler.PROVENANCE_CLASSES = /* @__PURE__ */ new Set([
|
|
1865
|
+
"measured",
|
|
1866
|
+
// source-attested: a real sensor reading, DB value, on-chain fact
|
|
1867
|
+
"derived",
|
|
1868
|
+
// computed from measured values by a transparent bounded function
|
|
1869
|
+
"inferred",
|
|
1870
|
+
// model/heuristic estimate (a forecast) — honest that it is a guess
|
|
1871
|
+
"generative"
|
|
1872
|
+
// produced by a generative model (LLM-authored value)
|
|
1873
|
+
]);
|
|
1874
|
+
/** Visible honesty glyph appended to a non-`measured` text value (measured = trusted, no marker). */
|
|
1875
|
+
_Native2DCompiler.PROVENANCE_GLYPH = {
|
|
1876
|
+
measured: "",
|
|
1877
|
+
derived: "\xB0",
|
|
1878
|
+
inferred: "~",
|
|
1879
|
+
generative: "\u2726"
|
|
1880
|
+
};
|
|
1881
|
+
_Native2DCompiler.TEXT_SIZE_TOKENS = /* @__PURE__ */ new Set([
|
|
1882
|
+
"text-xs",
|
|
1883
|
+
"text-sm",
|
|
1884
|
+
"text-base",
|
|
1885
|
+
"text-lg",
|
|
1886
|
+
"text-xl",
|
|
1887
|
+
"text-2xl",
|
|
1888
|
+
"text-3xl",
|
|
1889
|
+
"text-4xl",
|
|
1890
|
+
"text-5xl",
|
|
1891
|
+
"text-6xl",
|
|
1892
|
+
"text-7xl",
|
|
1893
|
+
"text-8xl",
|
|
1894
|
+
"text-9xl"
|
|
1895
|
+
]);
|
|
1896
|
+
var Native2DCompiler = _Native2DCompiler;
|
|
1259
1897
|
|
|
1260
1898
|
// src/compiler/NextJSCompiler.ts
|
|
1261
1899
|
function findTrait(composition, traitName2) {
|
|
@@ -1584,6 +2222,1306 @@ init_HoloMCPCompiler();
|
|
|
1584
2222
|
init_AgentInferenceExportTarget();
|
|
1585
2223
|
init_OmnigentAgentYamlCompiler();
|
|
1586
2224
|
init_DaimonSeedCompiler();
|
|
2225
|
+
|
|
2226
|
+
// src/compiler/HSIIRCompiler.ts
|
|
2227
|
+
init_CompilerBase();
|
|
2228
|
+
var HSI_IR_SCHEMA_VERSION = "holoscript.hsi-ir.v0.1.0";
|
|
2229
|
+
var HSI_TRACE_SCHEMA_VERSION = "holoscript.hsi-trace.v0.1.0";
|
|
2230
|
+
var HSI_LEARNING_GRAPH_SCHEMA_VERSION = "holoscript.learning-graph.v0.1.0";
|
|
2231
|
+
var HSI_AUDIT_SCHEMA_VERSION = "holoscript.hsi-audit.v0.1.0";
|
|
2232
|
+
var HSIAdmissionError = class extends Error {
|
|
2233
|
+
constructor(code, message) {
|
|
2234
|
+
super(`[${code}] ${message}`);
|
|
2235
|
+
this.name = "HSIAdmissionError";
|
|
2236
|
+
this.code = code;
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
function hsiStableStringify(value) {
|
|
2240
|
+
if (value === null || typeof value !== "object") {
|
|
2241
|
+
return JSON.stringify(value) ?? "null";
|
|
2242
|
+
}
|
|
2243
|
+
if (Array.isArray(value)) {
|
|
2244
|
+
return `[${value.map((item) => hsiStableStringify(item)).join(",")}]`;
|
|
2245
|
+
}
|
|
2246
|
+
const record = value;
|
|
2247
|
+
const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
|
|
2248
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${hsiStableStringify(record[key])}`).join(",")}}`;
|
|
2249
|
+
}
|
|
2250
|
+
function hsiSha256(value) {
|
|
2251
|
+
return `sha256:${createHash("sha256").update(hsiStableStringify(value)).digest("hex")}`;
|
|
2252
|
+
}
|
|
2253
|
+
function hsiSourceTextDigest(source) {
|
|
2254
|
+
return `sha256:${createHash("sha256").update(source.replace(/\r\n/g, "\n")).digest("hex")}`;
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// src/compiler/HSIIRCompiler.ts
|
|
2258
|
+
var HSI_OBSERVATION_MEDIATOR_EDGE = "shields";
|
|
2259
|
+
var ASSIGN_OPERATORS = /* @__PURE__ */ new Set(["=", "+=", "-=", "*=", "/="]);
|
|
2260
|
+
var BINARY_OPERATORS = /* @__PURE__ */ new Set([
|
|
2261
|
+
"+",
|
|
2262
|
+
"-",
|
|
2263
|
+
"*",
|
|
2264
|
+
"/",
|
|
2265
|
+
"%",
|
|
2266
|
+
"===",
|
|
2267
|
+
"!==",
|
|
2268
|
+
"==",
|
|
2269
|
+
"!=",
|
|
2270
|
+
"<",
|
|
2271
|
+
">",
|
|
2272
|
+
"<=",
|
|
2273
|
+
">=",
|
|
2274
|
+
"&&",
|
|
2275
|
+
"||"
|
|
2276
|
+
]);
|
|
2277
|
+
function spanOf(node) {
|
|
2278
|
+
const loc = node?.loc;
|
|
2279
|
+
if (!loc) return void 0;
|
|
2280
|
+
return {
|
|
2281
|
+
line: loc.start.line,
|
|
2282
|
+
column: loc.start.column,
|
|
2283
|
+
endLine: loc.end.line,
|
|
2284
|
+
endColumn: loc.end.column
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
function scalarType(value) {
|
|
2288
|
+
if (value === null) return "null";
|
|
2289
|
+
return typeof value;
|
|
2290
|
+
}
|
|
2291
|
+
function asScalar(value, where) {
|
|
2292
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
2293
|
+
return value;
|
|
2294
|
+
}
|
|
2295
|
+
throw new HSIAdmissionError(
|
|
2296
|
+
"unsupported-value",
|
|
2297
|
+
`${where}: HS-Core v0 admits scalar values only, got ${Array.isArray(value) ? "array" : typeof value}`
|
|
2298
|
+
);
|
|
2299
|
+
}
|
|
2300
|
+
function asTriple(value, where) {
|
|
2301
|
+
let arr;
|
|
2302
|
+
if (Array.isArray(value)) {
|
|
2303
|
+
arr = value;
|
|
2304
|
+
} else if (typeof value === "object" && value !== null && "x" in value) {
|
|
2305
|
+
const vec = value;
|
|
2306
|
+
arr = [vec["x"], vec["y"], vec["z"]];
|
|
2307
|
+
}
|
|
2308
|
+
if (!arr || arr.length !== 3 || !arr.every((n) => typeof n === "number" && Number.isFinite(n))) {
|
|
2309
|
+
throw new HSIAdmissionError("invalid-spatial", `${where}: position/scale must be a finite numeric triple`);
|
|
2310
|
+
}
|
|
2311
|
+
return [arr[0], arr[1], arr[2]];
|
|
2312
|
+
}
|
|
2313
|
+
function collectSlots(ir, into) {
|
|
2314
|
+
switch (ir.kind) {
|
|
2315
|
+
case "Identifier":
|
|
2316
|
+
into.add(ir.name);
|
|
2317
|
+
return;
|
|
2318
|
+
case "BinaryExpression":
|
|
2319
|
+
collectSlots(ir.left, into);
|
|
2320
|
+
collectSlots(ir.right, into);
|
|
2321
|
+
return;
|
|
2322
|
+
case "UnaryExpression":
|
|
2323
|
+
collectSlots(ir.argument, into);
|
|
2324
|
+
return;
|
|
2325
|
+
case "MemberExpression":
|
|
2326
|
+
collectSlots(ir.object, into);
|
|
2327
|
+
return;
|
|
2328
|
+
case "CallExpression":
|
|
2329
|
+
for (const arg of ir.arguments) collectSlots(arg, into);
|
|
2330
|
+
return;
|
|
2331
|
+
case "Literal":
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
function assertSlots(ir, allowed, where) {
|
|
2336
|
+
const used = /* @__PURE__ */ new Set();
|
|
2337
|
+
collectSlots(ir, used);
|
|
2338
|
+
for (const name of used) {
|
|
2339
|
+
if (!allowed.has(name)) {
|
|
2340
|
+
throw new HSIAdmissionError("unknown-slot", `${where}: expression reads undeclared slot "${name}"`);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
return [...used].sort();
|
|
2344
|
+
}
|
|
2345
|
+
function lowerHoloExpression(expr, where) {
|
|
2346
|
+
switch (expr.type) {
|
|
2347
|
+
case "Literal":
|
|
2348
|
+
return { kind: "Literal", value: expr.value };
|
|
2349
|
+
case "Identifier":
|
|
2350
|
+
return { kind: "Identifier", name: expr.name };
|
|
2351
|
+
case "BinaryExpression": {
|
|
2352
|
+
if (!BINARY_OPERATORS.has(expr.operator)) {
|
|
2353
|
+
throw new HSIAdmissionError("unsupported-operator", `${where}: operator "${expr.operator}"`);
|
|
2354
|
+
}
|
|
2355
|
+
return {
|
|
2356
|
+
kind: "BinaryExpression",
|
|
2357
|
+
operator: expr.operator,
|
|
2358
|
+
left: lowerHoloExpression(expr.left, where),
|
|
2359
|
+
right: lowerHoloExpression(expr.right, where)
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
case "UnaryExpression":
|
|
2363
|
+
return {
|
|
2364
|
+
kind: "UnaryExpression",
|
|
2365
|
+
operator: expr.operator,
|
|
2366
|
+
argument: lowerHoloExpression(expr.argument, where)
|
|
2367
|
+
};
|
|
2368
|
+
case "MemberExpression": {
|
|
2369
|
+
if (expr.computed) {
|
|
2370
|
+
throw new HSIAdmissionError("unsupported-expression", `${where}: computed member access`);
|
|
2371
|
+
}
|
|
2372
|
+
return {
|
|
2373
|
+
kind: "MemberExpression",
|
|
2374
|
+
object: lowerHoloExpression(expr.object, where),
|
|
2375
|
+
property: expr.property
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
default:
|
|
2379
|
+
throw new HSIAdmissionError("unsupported-expression", `${where}: ${expr.type} is outside HS-Core v0`);
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
function lowerEntities(composition) {
|
|
2383
|
+
const templates = /* @__PURE__ */ new Map();
|
|
2384
|
+
for (const template of composition.templates ?? []) {
|
|
2385
|
+
if (templates.has(template.name)) {
|
|
2386
|
+
throw new HSIAdmissionError("duplicate-template", `template "${template.name}" declared twice`);
|
|
2387
|
+
}
|
|
2388
|
+
templates.set(template.name, template);
|
|
2389
|
+
}
|
|
2390
|
+
const entities = /* @__PURE__ */ new Map();
|
|
2391
|
+
for (const obj of composition.objects ?? []) {
|
|
2392
|
+
if (entities.has(obj.name)) {
|
|
2393
|
+
throw new HSIAdmissionError("duplicate-entity", `entity "${obj.name}" declared twice`);
|
|
2394
|
+
}
|
|
2395
|
+
if ((obj.children ?? []).length > 0) {
|
|
2396
|
+
throw new HSIAdmissionError("unsupported-value", `entity "${obj.name}": nested children are outside HS-Core v0`);
|
|
2397
|
+
}
|
|
2398
|
+
const components = {};
|
|
2399
|
+
const traitNames = /* @__PURE__ */ new Set();
|
|
2400
|
+
let template;
|
|
2401
|
+
if (obj.template !== void 0) {
|
|
2402
|
+
template = templates.get(obj.template);
|
|
2403
|
+
if (!template) {
|
|
2404
|
+
throw new HSIAdmissionError("unknown-archetype", `entity "${obj.name}" uses missing template "${obj.template}"`);
|
|
2405
|
+
}
|
|
2406
|
+
for (const prop of template.properties ?? []) {
|
|
2407
|
+
if (prop.key === "position" || prop.key === "scale") continue;
|
|
2408
|
+
components[prop.key] = asScalar(prop.value, `template "${template.name}".${prop.key}`);
|
|
2409
|
+
}
|
|
2410
|
+
for (const trait of template.traits ?? []) traitNames.add(trait.name);
|
|
2411
|
+
}
|
|
2412
|
+
let position;
|
|
2413
|
+
let scale;
|
|
2414
|
+
for (const prop of obj.properties ?? []) {
|
|
2415
|
+
if (prop.key === "position") {
|
|
2416
|
+
position = asTriple(prop.value, `entity "${obj.name}".position`);
|
|
2417
|
+
continue;
|
|
2418
|
+
}
|
|
2419
|
+
if (prop.key === "scale") {
|
|
2420
|
+
scale = asTriple(prop.value, `entity "${obj.name}".scale`);
|
|
2421
|
+
continue;
|
|
2422
|
+
}
|
|
2423
|
+
components[prop.key] = asScalar(prop.value, `entity "${obj.name}".${prop.key}`);
|
|
2424
|
+
}
|
|
2425
|
+
for (const trait of obj.traits ?? []) traitNames.add(trait.name);
|
|
2426
|
+
const opaqueValue = components["opaque"];
|
|
2427
|
+
const opacity = opaqueValue === void 0 ? "unknown" : opaqueValue === true ? "opaque" : "transparent";
|
|
2428
|
+
delete components["opaque"];
|
|
2429
|
+
const role = typeof components["role"] === "string" ? components["role"] : void 0;
|
|
2430
|
+
const entity = {
|
|
2431
|
+
id: `entity:${obj.name}`,
|
|
2432
|
+
name: obj.name,
|
|
2433
|
+
opacity,
|
|
2434
|
+
components,
|
|
2435
|
+
traits: [...traitNames].sort(),
|
|
2436
|
+
sourceSpan: spanOf(obj)
|
|
2437
|
+
};
|
|
2438
|
+
if (obj.template !== void 0) entity.archetype = obj.template;
|
|
2439
|
+
if (role !== void 0) entity.role = role;
|
|
2440
|
+
if (position === void 0 && obj.position !== void 0) {
|
|
2441
|
+
position = asTriple(obj.position, `entity "${obj.name}".position`);
|
|
2442
|
+
}
|
|
2443
|
+
if (scale === void 0 && obj.scale !== void 0) {
|
|
2444
|
+
scale = asTriple(obj.scale, `entity "${obj.name}".scale`);
|
|
2445
|
+
}
|
|
2446
|
+
if (position !== void 0) entity.position = position;
|
|
2447
|
+
if (scale !== void 0) entity.scale = scale;
|
|
2448
|
+
entities.set(obj.name, entity);
|
|
2449
|
+
}
|
|
2450
|
+
return { templates, entities };
|
|
2451
|
+
}
|
|
2452
|
+
function lowerRelations(composition, entities) {
|
|
2453
|
+
const relations = [];
|
|
2454
|
+
const connections = composition.connections ?? [];
|
|
2455
|
+
for (const conn of connections) {
|
|
2456
|
+
for (const endpoint of [conn.from, conn.to]) {
|
|
2457
|
+
if (!entities.has(endpoint)) {
|
|
2458
|
+
throw new HSIAdmissionError(
|
|
2459
|
+
"unknown-relation-endpoint",
|
|
2460
|
+
`connection ${conn.from} -> ${conn.to} references undeclared entity "${endpoint}"`
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
const edgeType = conn.edgeType ?? "connects";
|
|
2465
|
+
relations.push({
|
|
2466
|
+
id: `relation:${conn.from}->${conn.to}:${edgeType}`,
|
|
2467
|
+
from: conn.from,
|
|
2468
|
+
to: conn.to,
|
|
2469
|
+
edgeType,
|
|
2470
|
+
sourceSpan: spanOf(conn)
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
return relations;
|
|
2474
|
+
}
|
|
2475
|
+
function lowerState(composition) {
|
|
2476
|
+
const fields = [];
|
|
2477
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2478
|
+
for (const prop of composition.state?.properties ?? []) {
|
|
2479
|
+
if (seen.has(prop.key)) {
|
|
2480
|
+
throw new HSIAdmissionError("duplicate-state-key", `state key "${prop.key}" declared twice`);
|
|
2481
|
+
}
|
|
2482
|
+
seen.add(prop.key);
|
|
2483
|
+
const baseline = asScalar(prop.value, `state.${prop.key}`);
|
|
2484
|
+
fields.push({
|
|
2485
|
+
id: `state:world.${prop.key}`,
|
|
2486
|
+
key: prop.key,
|
|
2487
|
+
baseline,
|
|
2488
|
+
valueType: scalarType(baseline),
|
|
2489
|
+
reactive: prop.reactive === true,
|
|
2490
|
+
sourceSpan: spanOf(prop)
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
return fields;
|
|
2494
|
+
}
|
|
2495
|
+
function deriveObservationPolicy(entities, relations) {
|
|
2496
|
+
const rules = [];
|
|
2497
|
+
const observers = [...entities.values()].filter((e) => e.role === "agent");
|
|
2498
|
+
const mediatorsByTarget = /* @__PURE__ */ new Map();
|
|
2499
|
+
for (const rel of relations) {
|
|
2500
|
+
if (rel.edgeType !== HSI_OBSERVATION_MEDIATOR_EDGE) continue;
|
|
2501
|
+
const list = mediatorsByTarget.get(rel.to) ?? [];
|
|
2502
|
+
list.push(entities.get(rel.from));
|
|
2503
|
+
mediatorsByTarget.set(rel.to, list);
|
|
2504
|
+
}
|
|
2505
|
+
for (const observer of observers) {
|
|
2506
|
+
for (const observed of entities.values()) {
|
|
2507
|
+
if (observed.name === observer.name) continue;
|
|
2508
|
+
if (observed.role === "barrier") continue;
|
|
2509
|
+
const mediators = (mediatorsByTarget.get(observed.name) ?? []).slice().sort((a, b) => a.name.localeCompare(b.name));
|
|
2510
|
+
const access = mediators.some((m) => m.opacity === "opaque") ? "blocked" : mediators.some((m) => m.opacity === "unknown") ? "unknown" : "visible";
|
|
2511
|
+
rules.push({
|
|
2512
|
+
id: `obs:${observer.name}->${observed.name}`,
|
|
2513
|
+
observer: observer.name,
|
|
2514
|
+
observed: observed.name,
|
|
2515
|
+
access,
|
|
2516
|
+
mediators: mediators.map((m) => m.name)
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
return rules;
|
|
2521
|
+
}
|
|
2522
|
+
function lowerStatements(statements, handlerEvent, stateSlots, counter) {
|
|
2523
|
+
const effects = [];
|
|
2524
|
+
for (const stmt of statements) {
|
|
2525
|
+
const ordinal = counter.n++;
|
|
2526
|
+
const id = `effect:${handlerEvent}#${ordinal}`;
|
|
2527
|
+
switch (stmt.type) {
|
|
2528
|
+
case "Assignment": {
|
|
2529
|
+
if (!ASSIGN_OPERATORS.has(stmt.operator)) {
|
|
2530
|
+
throw new HSIAdmissionError("unsupported-operator", `${id}: assignment operator "${stmt.operator}"`);
|
|
2531
|
+
}
|
|
2532
|
+
const target = stmt.target.startsWith("state.") ? stmt.target.slice("state.".length) : stmt.target;
|
|
2533
|
+
if (!stateSlots.has(target)) {
|
|
2534
|
+
throw new HSIAdmissionError("unknown-assignment-target", `${id}: "${stmt.target}" is not a declared state key`);
|
|
2535
|
+
}
|
|
2536
|
+
const value = lowerHoloExpression(stmt.value, id);
|
|
2537
|
+
assertSlots(value, stateSlots, id);
|
|
2538
|
+
effects.push({ kind: "assign", id, target, operator: stmt.operator, value, sourceSpan: spanOf(stmt) });
|
|
2539
|
+
break;
|
|
2540
|
+
}
|
|
2541
|
+
case "EmitStatement": {
|
|
2542
|
+
effects.push({ kind: "emit", id, event: stmt.event, sourceSpan: spanOf(stmt) });
|
|
2543
|
+
break;
|
|
2544
|
+
}
|
|
2545
|
+
case "IfStatement": {
|
|
2546
|
+
const condition = lowerHoloExpression(stmt.condition, id);
|
|
2547
|
+
assertSlots(condition, stateSlots, id);
|
|
2548
|
+
effects.push({
|
|
2549
|
+
kind: "branch",
|
|
2550
|
+
id,
|
|
2551
|
+
condition,
|
|
2552
|
+
then: lowerStatements(stmt.consequent, handlerEvent, stateSlots, counter),
|
|
2553
|
+
else: lowerStatements(stmt.alternate ?? [], handlerEvent, stateSlots, counter),
|
|
2554
|
+
sourceSpan: spanOf(stmt)
|
|
2555
|
+
});
|
|
2556
|
+
break;
|
|
2557
|
+
}
|
|
2558
|
+
default:
|
|
2559
|
+
throw new HSIAdmissionError("unsupported-statement", `${id}: ${stmt.type} is outside HS-Core v0`);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
return effects;
|
|
2563
|
+
}
|
|
2564
|
+
function lowerEventHandlers(composition, stateSlots) {
|
|
2565
|
+
const logic = composition.logic;
|
|
2566
|
+
const handlers = [];
|
|
2567
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2568
|
+
for (const handler of logic?.handlers ?? []) {
|
|
2569
|
+
if (seen.has(handler.event)) {
|
|
2570
|
+
throw new HSIAdmissionError("duplicate-handler", `handler "${handler.event}" declared twice`);
|
|
2571
|
+
}
|
|
2572
|
+
seen.add(handler.event);
|
|
2573
|
+
handlers.push({
|
|
2574
|
+
id: `event:${handler.event}`,
|
|
2575
|
+
event: handler.event,
|
|
2576
|
+
effects: lowerStatements(handler.body, handler.event, stateSlots, { n: 0 }),
|
|
2577
|
+
sourceSpan: spanOf(handler)
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
return handlers;
|
|
2581
|
+
}
|
|
2582
|
+
function lowerMachine(machine) {
|
|
2583
|
+
const stateNames = Object.keys(machine.states ?? {});
|
|
2584
|
+
if (stateNames.length === 0) {
|
|
2585
|
+
throw new HSIAdmissionError("admission-skewed", `state machine "${machine.name}" declares no states`);
|
|
2586
|
+
}
|
|
2587
|
+
if (!stateNames.includes(machine.initialState)) {
|
|
2588
|
+
throw new HSIAdmissionError(
|
|
2589
|
+
"unknown-initial-state",
|
|
2590
|
+
`state machine "${machine.name}": initial state "${machine.initialState}" is not declared`
|
|
2591
|
+
);
|
|
2592
|
+
}
|
|
2593
|
+
const inputs = (machine.inputs ?? []).map((input) => ({
|
|
2594
|
+
id: `input:${machine.name}.${input.name}`,
|
|
2595
|
+
name: input.name,
|
|
2596
|
+
inputType: input.inputType,
|
|
2597
|
+
baseline: input.default !== void 0 ? input.default : input.inputType === "bool" || input.inputType === "trigger" ? false : 0
|
|
2598
|
+
}));
|
|
2599
|
+
const inputSlots = new Set(inputs.map((i) => i.name));
|
|
2600
|
+
const allTransitions = [];
|
|
2601
|
+
for (const t of machine.transitions ?? []) {
|
|
2602
|
+
allTransitions.push({ from: t.from && t.from !== "any" ? t.from : t.from ?? "any", t });
|
|
2603
|
+
}
|
|
2604
|
+
for (const stateName of stateNames) {
|
|
2605
|
+
for (const t of machine.states[stateName]?.transitions ?? []) {
|
|
2606
|
+
allTransitions.push({ from: t.from ?? stateName, t });
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
const ordinals = /* @__PURE__ */ new Map();
|
|
2610
|
+
const transitions = allTransitions.map(({ from, t }, declarationOrdinal) => {
|
|
2611
|
+
if (from !== "any" && !stateNames.includes(from)) {
|
|
2612
|
+
throw new HSIAdmissionError(
|
|
2613
|
+
"unknown-transition-endpoint",
|
|
2614
|
+
`state machine "${machine.name}": transition from undeclared state "${from}"`
|
|
2615
|
+
);
|
|
2616
|
+
}
|
|
2617
|
+
if (!stateNames.includes(t.target)) {
|
|
2618
|
+
throw new HSIAdmissionError(
|
|
2619
|
+
"unknown-transition-endpoint",
|
|
2620
|
+
`state machine "${machine.name}": transition to undeclared state "${t.target}"`
|
|
2621
|
+
);
|
|
2622
|
+
}
|
|
2623
|
+
const baseId = `transition:${machine.name}.${from}->${t.target}`;
|
|
2624
|
+
const ordinal = ordinals.get(baseId) ?? 0;
|
|
2625
|
+
ordinals.set(baseId, ordinal + 1);
|
|
2626
|
+
const id = `${baseId}#${ordinal}`;
|
|
2627
|
+
let guard;
|
|
2628
|
+
if (t.condition) {
|
|
2629
|
+
guard = lowerHoloExpression(t.condition, id);
|
|
2630
|
+
} else if ((t.conditions ?? []).length > 0) {
|
|
2631
|
+
for (const cond of t.conditions) {
|
|
2632
|
+
const clause = {
|
|
2633
|
+
kind: "BinaryExpression",
|
|
2634
|
+
operator: cond.operator,
|
|
2635
|
+
left: { kind: "Identifier", name: cond.parameter },
|
|
2636
|
+
right: { kind: "Literal", value: cond.value }
|
|
2637
|
+
};
|
|
2638
|
+
guard = guard ? { kind: "BinaryExpression", operator: cond.chain === "or" ? "||" : "&&", left: guard, right: clause } : clause;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
if (!guard) {
|
|
2642
|
+
throw new HSIAdmissionError("admission-skewed", `${id}: transition has neither guard nor trigger condition`);
|
|
2643
|
+
}
|
|
2644
|
+
const reads = assertSlots(guard, inputSlots, id);
|
|
2645
|
+
const transition = {
|
|
2646
|
+
id,
|
|
2647
|
+
from,
|
|
2648
|
+
target: t.target,
|
|
2649
|
+
priority: declarationOrdinal,
|
|
2650
|
+
guard,
|
|
2651
|
+
reads,
|
|
2652
|
+
sourceSpan: spanOf(t)
|
|
2653
|
+
};
|
|
2654
|
+
if (t.event !== void 0) transition.event = t.event;
|
|
2655
|
+
return transition;
|
|
2656
|
+
});
|
|
2657
|
+
return {
|
|
2658
|
+
id: `machine:${machine.name}`,
|
|
2659
|
+
name: machine.name,
|
|
2660
|
+
initialState: machine.initialState,
|
|
2661
|
+
states: [...stateNames].sort(),
|
|
2662
|
+
inputs,
|
|
2663
|
+
transitions,
|
|
2664
|
+
sourceSpan: spanOf(machine)
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
function lowerPredicates(composition, stateSlots) {
|
|
2668
|
+
const predicates = [];
|
|
2669
|
+
const slotList = [...stateSlots];
|
|
2670
|
+
const lowerClause = (kind, name, expr) => {
|
|
2671
|
+
const id = `${kind}:${name}`;
|
|
2672
|
+
let ir;
|
|
2673
|
+
try {
|
|
2674
|
+
ir = parseExpressionToIR(expr, slotList);
|
|
2675
|
+
} catch (error) {
|
|
2676
|
+
if (error instanceof ExpressionParseError) {
|
|
2677
|
+
throw new HSIAdmissionError("unparseable-clause", `${id}: ${error.message}`);
|
|
2678
|
+
}
|
|
2679
|
+
throw error;
|
|
2680
|
+
}
|
|
2681
|
+
return { id, name, kind, expression: ir, reads: assertSlots(ir, stateSlots, id), sourceSpan: spanOf(composition.contract) };
|
|
2682
|
+
};
|
|
2683
|
+
for (const clause of composition.contract?.preconditions ?? []) {
|
|
2684
|
+
predicates.push(lowerClause("precondition", clause.name, clause.expr));
|
|
2685
|
+
}
|
|
2686
|
+
for (const clause of composition.contract?.invariants ?? []) {
|
|
2687
|
+
predicates.push(lowerClause("invariant", clause.name, clause.expr));
|
|
2688
|
+
}
|
|
2689
|
+
for (const achievement of composition.achievements ?? []) {
|
|
2690
|
+
const id = `goal:${achievement.name}`;
|
|
2691
|
+
const ir = lowerHoloExpression(achievement.condition, id);
|
|
2692
|
+
predicates.push({
|
|
2693
|
+
id,
|
|
2694
|
+
name: achievement.name,
|
|
2695
|
+
kind: "goal",
|
|
2696
|
+
expression: ir,
|
|
2697
|
+
reads: assertSlots(ir, stateSlots, id),
|
|
2698
|
+
sourceSpan: spanOf(achievement)
|
|
2699
|
+
});
|
|
2700
|
+
}
|
|
2701
|
+
return predicates;
|
|
2702
|
+
}
|
|
2703
|
+
function lowerCompositionToHSIIR(composition, options = {}) {
|
|
2704
|
+
if (!composition || composition.type !== "Composition" || typeof composition.name !== "string" || composition.name.length === 0) {
|
|
2705
|
+
throw new HSIAdmissionError("empty-world", "input is not a named HoloComposition");
|
|
2706
|
+
}
|
|
2707
|
+
const { entities } = lowerEntities(composition);
|
|
2708
|
+
const stateFields = lowerState(composition);
|
|
2709
|
+
if (entities.size === 0 && stateFields.length === 0) {
|
|
2710
|
+
throw new HSIAdmissionError("empty-world", `composition "${composition.name}" declares no entities and no state`);
|
|
2711
|
+
}
|
|
2712
|
+
const relations = lowerRelations(composition, entities);
|
|
2713
|
+
const observationPolicy = deriveObservationPolicy(entities, relations);
|
|
2714
|
+
const stateSlots = new Set(stateFields.map((f) => f.key));
|
|
2715
|
+
const eventHandlers = lowerEventHandlers(composition, stateSlots);
|
|
2716
|
+
const machines = (composition.stateMachines ?? []).map(lowerMachine);
|
|
2717
|
+
const predicates = lowerPredicates(composition, stateSlots);
|
|
2718
|
+
const sourceDigest = options.sourceText !== void 0 ? hsiSourceTextDigest(options.sourceText) : hsiSha256({ compositionName: composition.name, note: "no-source-text-provided" });
|
|
2719
|
+
const byId = (items) => [...items].sort((a, b) => a.id.localeCompare(b.id));
|
|
2720
|
+
const docWithoutDigest = {
|
|
2721
|
+
schemaVersion: HSI_IR_SCHEMA_VERSION,
|
|
2722
|
+
kind: "HSIIR",
|
|
2723
|
+
world: { name: composition.name, sourceDigest },
|
|
2724
|
+
entities: byId([...entities.values()]),
|
|
2725
|
+
relations: byId(relations),
|
|
2726
|
+
state: byId(stateFields),
|
|
2727
|
+
observationPolicy: byId(observationPolicy),
|
|
2728
|
+
eventHandlers: byId(eventHandlers),
|
|
2729
|
+
machines: byId(machines),
|
|
2730
|
+
predicates: byId(predicates),
|
|
2731
|
+
declaredUnknowns: [
|
|
2732
|
+
"action-capabilities",
|
|
2733
|
+
"action-costs",
|
|
2734
|
+
"beliefs",
|
|
2735
|
+
"first-class-observations",
|
|
2736
|
+
"memory-events",
|
|
2737
|
+
"receipt-shape"
|
|
2738
|
+
],
|
|
2739
|
+
provenance: { compiler: "HSIIRCompiler" }
|
|
2740
|
+
};
|
|
2741
|
+
const deterministicDigest = hsiSha256(docWithoutDigest);
|
|
2742
|
+
return {
|
|
2743
|
+
...docWithoutDigest,
|
|
2744
|
+
provenance: { compiler: "HSIIRCompiler", deterministicDigest }
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
var HSIIRCompiler = class extends CompilerBase {
|
|
2748
|
+
constructor(options = {}) {
|
|
2749
|
+
super();
|
|
2750
|
+
this.compilerName = "HSIIRCompiler";
|
|
2751
|
+
this.options = options;
|
|
2752
|
+
}
|
|
2753
|
+
getRequiredCapability() {
|
|
2754
|
+
return ANSCapabilityPath.HSI_IR;
|
|
2755
|
+
}
|
|
2756
|
+
compile(composition, agentToken, outputPath) {
|
|
2757
|
+
this.validateCompilerAccess(agentToken, outputPath);
|
|
2758
|
+
return JSON.stringify(lowerCompositionToHSIIR(composition, this.options), null, 2);
|
|
2759
|
+
}
|
|
2760
|
+
compileToFiles(composition, agentToken = "") {
|
|
2761
|
+
return { [this.defaultOutputFileName()]: this.compile(composition, agentToken) };
|
|
2762
|
+
}
|
|
2763
|
+
defaultOutputFileName() {
|
|
2764
|
+
return "hsi-ir.json";
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
function createHSIIRCompiler(options) {
|
|
2768
|
+
return new HSIIRCompiler(options);
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
// src/compiler/HSIExactTrace.ts
|
|
2772
|
+
function snapshot(world) {
|
|
2773
|
+
const state = {};
|
|
2774
|
+
for (const key of Object.keys(world.state).sort()) state[key] = world.state[key];
|
|
2775
|
+
const machineStates = {};
|
|
2776
|
+
for (const name of [...world.machines.keys()].sort()) {
|
|
2777
|
+
machineStates[name] = world.machines.get(name).current;
|
|
2778
|
+
}
|
|
2779
|
+
return { state, machineStates, stateDigest: hsiSha256({ state, machineStates }) };
|
|
2780
|
+
}
|
|
2781
|
+
function evaluateGuard(guard, context) {
|
|
2782
|
+
return evaluateExpressionIR(guard, context) === true;
|
|
2783
|
+
}
|
|
2784
|
+
function applyOperator(operator, before, operand, where) {
|
|
2785
|
+
if (operator === "=") {
|
|
2786
|
+
if (operand === null || typeof operand === "string" || typeof operand === "number" || typeof operand === "boolean") {
|
|
2787
|
+
return operand;
|
|
2788
|
+
}
|
|
2789
|
+
throw new HSIAdmissionError("unsupported-value", `${where}: assignment produced a non-scalar value`);
|
|
2790
|
+
}
|
|
2791
|
+
if (typeof before !== "number" || typeof operand !== "number" || !Number.isFinite(operand)) {
|
|
2792
|
+
throw new HSIAdmissionError(
|
|
2793
|
+
"unsupported-value",
|
|
2794
|
+
`${where}: compound assignment requires numeric operands (got ${typeof before} ${operator} ${typeof operand})`
|
|
2795
|
+
);
|
|
2796
|
+
}
|
|
2797
|
+
switch (operator) {
|
|
2798
|
+
case "+=":
|
|
2799
|
+
return before + operand;
|
|
2800
|
+
case "-=":
|
|
2801
|
+
return before - operand;
|
|
2802
|
+
case "*=":
|
|
2803
|
+
return before * operand;
|
|
2804
|
+
case "/=": {
|
|
2805
|
+
if (operand === 0) {
|
|
2806
|
+
throw new HSIAdmissionError("unsupported-value", `${where}: division by zero`);
|
|
2807
|
+
}
|
|
2808
|
+
return before / operand;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
function runEffects(effects, world, records, emitted) {
|
|
2813
|
+
for (const effect of effects) {
|
|
2814
|
+
switch (effect.kind) {
|
|
2815
|
+
case "assign": {
|
|
2816
|
+
const before = world.state[effect.target];
|
|
2817
|
+
const operand = evaluateExpressionIR(effect.value, world.state);
|
|
2818
|
+
const after = applyOperator(effect.operator, before, operand, effect.id);
|
|
2819
|
+
world.state[effect.target] = after;
|
|
2820
|
+
records.push({ sourceId: effect.id, target: effect.target, before, after });
|
|
2821
|
+
break;
|
|
2822
|
+
}
|
|
2823
|
+
case "emit": {
|
|
2824
|
+
emitted.push(effect.event);
|
|
2825
|
+
break;
|
|
2826
|
+
}
|
|
2827
|
+
case "branch": {
|
|
2828
|
+
const taken = evaluateGuard(effect.condition, world.state);
|
|
2829
|
+
runEffects(taken ? effect.then : effect.else, world, records, emitted);
|
|
2830
|
+
break;
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
function evaluateTransitions(world, ir) {
|
|
2836
|
+
const fired = [];
|
|
2837
|
+
for (const machineIR of ir.machines) {
|
|
2838
|
+
const runtime = world.machines.get(machineIR.name);
|
|
2839
|
+
const context = { ...runtime.inputs };
|
|
2840
|
+
for (const trigger of runtime.triggers) context[trigger] = true;
|
|
2841
|
+
const candidates = [...machineIR.transitions].sort((a, b) => a.priority - b.priority);
|
|
2842
|
+
for (const transition of candidates) {
|
|
2843
|
+
if (transition.from !== "any" && transition.from !== runtime.current) continue;
|
|
2844
|
+
if (!transition.guard) continue;
|
|
2845
|
+
if (evaluateGuard(transition.guard, context)) {
|
|
2846
|
+
fired.push({
|
|
2847
|
+
machine: machineIR.name,
|
|
2848
|
+
transitionId: transition.id,
|
|
2849
|
+
from: runtime.current,
|
|
2850
|
+
to: transition.target
|
|
2851
|
+
});
|
|
2852
|
+
runtime.current = transition.target;
|
|
2853
|
+
break;
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
runtime.triggers.clear();
|
|
2857
|
+
}
|
|
2858
|
+
return fired;
|
|
2859
|
+
}
|
|
2860
|
+
function checkInvariants(world, ir) {
|
|
2861
|
+
const violations = [];
|
|
2862
|
+
for (const predicate of ir.predicates) {
|
|
2863
|
+
if (predicate.kind !== "invariant") continue;
|
|
2864
|
+
if (!evaluateGuard(predicate.expression, world.state)) violations.push(predicate.id);
|
|
2865
|
+
}
|
|
2866
|
+
return violations;
|
|
2867
|
+
}
|
|
2868
|
+
function runExactTrace(ir, scenario) {
|
|
2869
|
+
const world = { state: {}, machines: /* @__PURE__ */ new Map() };
|
|
2870
|
+
for (const field of ir.state) world.state[field.key] = field.baseline;
|
|
2871
|
+
for (const machine of ir.machines) {
|
|
2872
|
+
const inputs = {};
|
|
2873
|
+
for (const input of machine.inputs) {
|
|
2874
|
+
inputs[input.name] = input.inputType === "trigger" ? false : input.baseline;
|
|
2875
|
+
}
|
|
2876
|
+
world.machines.set(machine.name, {
|
|
2877
|
+
name: machine.name,
|
|
2878
|
+
current: machine.initialState,
|
|
2879
|
+
inputs,
|
|
2880
|
+
triggers: /* @__PURE__ */ new Set()
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
const preconditionResults = ir.predicates.filter((p) => p.kind === "precondition").map((p) => ({ id: p.id, holds: evaluateGuard(p.expression, world.state) }));
|
|
2884
|
+
const initial = snapshot(world);
|
|
2885
|
+
const steps = [];
|
|
2886
|
+
let valid = preconditionResults.every((r) => r.holds);
|
|
2887
|
+
scenario.forEach((step, ordinal) => {
|
|
2888
|
+
const effects = [];
|
|
2889
|
+
const emitted = [];
|
|
2890
|
+
let transitions = [];
|
|
2891
|
+
switch (step.kind) {
|
|
2892
|
+
case "set-input": {
|
|
2893
|
+
const machine = world.machines.get(step.machine);
|
|
2894
|
+
if (!machine || !(step.input in machine.inputs)) {
|
|
2895
|
+
throw new HSIAdmissionError(
|
|
2896
|
+
"unknown-slot",
|
|
2897
|
+
`scenario step ${ordinal}: unknown machine input ${step.machine}.${step.input}`
|
|
2898
|
+
);
|
|
2899
|
+
}
|
|
2900
|
+
machine.inputs[step.input] = step.value;
|
|
2901
|
+
transitions = evaluateTransitions(world, ir);
|
|
2902
|
+
break;
|
|
2903
|
+
}
|
|
2904
|
+
case "fire-trigger": {
|
|
2905
|
+
const machine = world.machines.get(step.machine);
|
|
2906
|
+
const declared = ir.machines.find((m) => m.name === step.machine)?.inputs.find((i) => i.name === step.input && i.inputType === "trigger");
|
|
2907
|
+
if (!machine || !declared) {
|
|
2908
|
+
throw new HSIAdmissionError(
|
|
2909
|
+
"unknown-slot",
|
|
2910
|
+
`scenario step ${ordinal}: unknown trigger ${step.machine}.${step.input}`
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
machine.triggers.add(step.input);
|
|
2914
|
+
transitions = evaluateTransitions(world, ir);
|
|
2915
|
+
break;
|
|
2916
|
+
}
|
|
2917
|
+
case "fire-event": {
|
|
2918
|
+
const handler = ir.eventHandlers.find((h) => h.event === step.event);
|
|
2919
|
+
if (!handler) {
|
|
2920
|
+
throw new HSIAdmissionError(
|
|
2921
|
+
"unknown-slot",
|
|
2922
|
+
`scenario step ${ordinal}: no handler for event "${step.event}"`
|
|
2923
|
+
);
|
|
2924
|
+
}
|
|
2925
|
+
runEffects(handler.effects, world, effects, emitted);
|
|
2926
|
+
break;
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
const invariantViolations = checkInvariants(world, ir);
|
|
2930
|
+
if (invariantViolations.length > 0) valid = false;
|
|
2931
|
+
steps.push({
|
|
2932
|
+
ordinal,
|
|
2933
|
+
step,
|
|
2934
|
+
transitions,
|
|
2935
|
+
effects,
|
|
2936
|
+
emitted,
|
|
2937
|
+
invariantViolations,
|
|
2938
|
+
stateDigest: snapshot(world).stateDigest
|
|
2939
|
+
});
|
|
2940
|
+
});
|
|
2941
|
+
const final = snapshot(world);
|
|
2942
|
+
const traceWithoutDigest = {
|
|
2943
|
+
schemaVersion: HSI_TRACE_SCHEMA_VERSION,
|
|
2944
|
+
kind: "HSITrace",
|
|
2945
|
+
worldDigest: ir.provenance.deterministicDigest,
|
|
2946
|
+
preconditionResults,
|
|
2947
|
+
initial,
|
|
2948
|
+
steps,
|
|
2949
|
+
final,
|
|
2950
|
+
valid
|
|
2951
|
+
};
|
|
2952
|
+
return { ...traceWithoutDigest, deterministicDigest: hsiSha256(traceWithoutDigest) };
|
|
2953
|
+
}
|
|
2954
|
+
function splitFor(nodeId, worldDigest) {
|
|
2955
|
+
const digest = createHash("sha256").update(`${nodeId}|${worldDigest}`).digest();
|
|
2956
|
+
return digest[0] % 4 === 0 ? "eval" : "train";
|
|
2957
|
+
}
|
|
2958
|
+
function collectAssignTargets(effects, into, reads) {
|
|
2959
|
+
for (const effect of effects) {
|
|
2960
|
+
if (effect.kind === "assign") {
|
|
2961
|
+
into.add(effect.target);
|
|
2962
|
+
const stack = [effect.value];
|
|
2963
|
+
while (stack.length > 0) {
|
|
2964
|
+
const node = stack.pop();
|
|
2965
|
+
if (node.kind === "Identifier") reads.add(node.name);
|
|
2966
|
+
else if (node.kind === "BinaryExpression") stack.push(node.left, node.right);
|
|
2967
|
+
else if (node.kind === "UnaryExpression") stack.push(node.argument);
|
|
2968
|
+
else if (node.kind === "MemberExpression") stack.push(node.object);
|
|
2969
|
+
else if (node.kind === "CallExpression") stack.push(...node.arguments);
|
|
2970
|
+
}
|
|
2971
|
+
} else if (effect.kind === "branch") {
|
|
2972
|
+
collectAssignTargets(effect.then, into, reads);
|
|
2973
|
+
collectAssignTargets(effect.else, into, reads);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
function projectLearningGraph(ir) {
|
|
2978
|
+
const worldDigest = ir.provenance.deterministicDigest;
|
|
2979
|
+
const nodes = [];
|
|
2980
|
+
const edges = [];
|
|
2981
|
+
const addNode = (node) => {
|
|
2982
|
+
nodes.push({ ...node, split: splitFor(node.id, worldDigest) });
|
|
2983
|
+
};
|
|
2984
|
+
const addEdge = (edgeType, from, to, label) => {
|
|
2985
|
+
const edge = { id: `edge:${edgeType}:${from}->${to}`, edgeType, from, to };
|
|
2986
|
+
if (label !== void 0) edge.label = label;
|
|
2987
|
+
edges.push(edge);
|
|
2988
|
+
};
|
|
2989
|
+
const worldId = `entity:${ir.world.name}`;
|
|
2990
|
+
addNode({ id: worldId, nodeType: "entity", baseline: null, unknowns: [] });
|
|
2991
|
+
for (const entity of ir.entities) {
|
|
2992
|
+
addNode({
|
|
2993
|
+
id: entity.id,
|
|
2994
|
+
nodeType: "entity",
|
|
2995
|
+
baseline: null,
|
|
2996
|
+
unknowns: entity.opacity === "unknown" ? ["opacity"] : [],
|
|
2997
|
+
sourceSpan: entity.sourceSpan
|
|
2998
|
+
});
|
|
2999
|
+
addEdge("contains", worldId, entity.id);
|
|
3000
|
+
}
|
|
3001
|
+
for (const field of ir.state) {
|
|
3002
|
+
addNode({
|
|
3003
|
+
id: field.id,
|
|
3004
|
+
nodeType: "state",
|
|
3005
|
+
baseline: field.baseline,
|
|
3006
|
+
unknowns: [],
|
|
3007
|
+
sourceSpan: field.sourceSpan
|
|
3008
|
+
});
|
|
3009
|
+
addEdge("contains", worldId, field.id);
|
|
3010
|
+
}
|
|
3011
|
+
for (const rule of ir.observationPolicy) {
|
|
3012
|
+
addNode({
|
|
3013
|
+
id: rule.id,
|
|
3014
|
+
nodeType: "observation",
|
|
3015
|
+
baseline: null,
|
|
3016
|
+
unknowns: rule.access === "unknown" ? ["access"] : []
|
|
3017
|
+
});
|
|
3018
|
+
addEdge("observes", `entity:${rule.observer}`, rule.id, rule.access);
|
|
3019
|
+
addEdge("refers-to", rule.id, `entity:${rule.observed}`);
|
|
3020
|
+
for (const mediator of rule.mediators) {
|
|
3021
|
+
addEdge("refers-to", rule.id, `entity:${mediator}`, "mediator");
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
for (const handler of ir.eventHandlers) {
|
|
3025
|
+
addNode({ id: handler.id, nodeType: "event", baseline: null, unknowns: [], sourceSpan: handler.sourceSpan });
|
|
3026
|
+
const targets = /* @__PURE__ */ new Set();
|
|
3027
|
+
const reads = /* @__PURE__ */ new Set();
|
|
3028
|
+
collectAssignTargets(handler.effects, targets, reads);
|
|
3029
|
+
for (const target of [...targets].sort()) {
|
|
3030
|
+
addEdge("affects", handler.id, `state:world.${target}`);
|
|
3031
|
+
}
|
|
3032
|
+
for (const read of [...reads].sort()) {
|
|
3033
|
+
if (!targets.has(read)) addEdge("requires", handler.id, `state:world.${read}`);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
for (const machine of ir.machines) {
|
|
3037
|
+
for (const transition of machine.transitions) {
|
|
3038
|
+
addNode({
|
|
3039
|
+
id: transition.id,
|
|
3040
|
+
nodeType: "action",
|
|
3041
|
+
baseline: null,
|
|
3042
|
+
unknowns: [],
|
|
3043
|
+
sourceSpan: transition.sourceSpan
|
|
3044
|
+
});
|
|
3045
|
+
for (const read of transition.reads) {
|
|
3046
|
+
addEdge("requires", transition.id, `input:${machine.name}.${read}`);
|
|
3047
|
+
}
|
|
3048
|
+
addEdge("causes", transition.id, `machineState:${machine.name}.${transition.target}`);
|
|
3049
|
+
if (transition.from !== "any") {
|
|
3050
|
+
addEdge("precedes", `machineState:${machine.name}.${transition.from}`, transition.id);
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
for (const stateName of machine.states) {
|
|
3054
|
+
addNode({ id: `machineState:${machine.name}.${stateName}`, nodeType: "state", baseline: null, unknowns: [] });
|
|
3055
|
+
addEdge("contains", worldId, `machineState:${machine.name}.${stateName}`);
|
|
3056
|
+
}
|
|
3057
|
+
for (const input of machine.inputs) {
|
|
3058
|
+
addNode({ id: input.id, nodeType: "state", baseline: input.baseline, unknowns: [] });
|
|
3059
|
+
addEdge("contains", worldId, input.id);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
for (const predicate of ir.predicates) {
|
|
3063
|
+
addNode({
|
|
3064
|
+
id: predicate.id,
|
|
3065
|
+
nodeType: "objective",
|
|
3066
|
+
baseline: null,
|
|
3067
|
+
unknowns: [],
|
|
3068
|
+
sourceSpan: predicate.sourceSpan
|
|
3069
|
+
});
|
|
3070
|
+
for (const read of predicate.reads) {
|
|
3071
|
+
addEdge("refers-to", predicate.id, `state:world.${read}`);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
nodes.sort((a, b) => a.id.localeCompare(b.id));
|
|
3075
|
+
edges.sort((a, b) => a.id.localeCompare(b.id));
|
|
3076
|
+
const graphWithoutDigest = {
|
|
3077
|
+
schemaVersion: HSI_LEARNING_GRAPH_SCHEMA_VERSION,
|
|
3078
|
+
kind: "HSILearningGraph",
|
|
3079
|
+
worldDigest,
|
|
3080
|
+
emittedFrom: worldDigest,
|
|
3081
|
+
nodes,
|
|
3082
|
+
edges,
|
|
3083
|
+
interventions: [
|
|
3084
|
+
"intervention:flip-opacity",
|
|
3085
|
+
"intervention:raise-guard-threshold"
|
|
3086
|
+
]
|
|
3087
|
+
};
|
|
3088
|
+
return { ...graphWithoutDigest, deterministicDigest: hsiSha256(graphWithoutDigest) };
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
// src/compiler/HSIAuditVerifier.ts
|
|
3092
|
+
function renamed(name, map) {
|
|
3093
|
+
return map[name] ?? name;
|
|
3094
|
+
}
|
|
3095
|
+
function renameExpression(expr, map) {
|
|
3096
|
+
switch (expr.type) {
|
|
3097
|
+
case "Identifier":
|
|
3098
|
+
expr.name = renamed(expr.name, map);
|
|
3099
|
+
return;
|
|
3100
|
+
case "BinaryExpression":
|
|
3101
|
+
renameExpression(expr.left, map);
|
|
3102
|
+
renameExpression(expr.right, map);
|
|
3103
|
+
return;
|
|
3104
|
+
case "UnaryExpression":
|
|
3105
|
+
renameExpression(expr.argument, map);
|
|
3106
|
+
return;
|
|
3107
|
+
case "MemberExpression":
|
|
3108
|
+
renameExpression(expr.object, map);
|
|
3109
|
+
return;
|
|
3110
|
+
case "CallExpression":
|
|
3111
|
+
renameExpression(expr.callee, map);
|
|
3112
|
+
for (const arg of expr.arguments) renameExpression(arg, map);
|
|
3113
|
+
return;
|
|
3114
|
+
default:
|
|
3115
|
+
return;
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
function renameStatements(statements, map) {
|
|
3119
|
+
for (const stmt of statements) {
|
|
3120
|
+
switch (stmt.type) {
|
|
3121
|
+
case "Assignment": {
|
|
3122
|
+
const target = stmt.target.startsWith("state.") ? stmt.target.slice("state.".length) : stmt.target;
|
|
3123
|
+
const prefix = stmt.target.startsWith("state.") ? "state." : "";
|
|
3124
|
+
stmt.target = `${prefix}${renamed(target, map)}`;
|
|
3125
|
+
renameExpression(stmt.value, map);
|
|
3126
|
+
break;
|
|
3127
|
+
}
|
|
3128
|
+
case "IfStatement":
|
|
3129
|
+
renameExpression(stmt.condition, map);
|
|
3130
|
+
renameStatements(stmt.consequent, map);
|
|
3131
|
+
renameStatements(stmt.alternate ?? [], map);
|
|
3132
|
+
break;
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
function renameRawExpr(expr, map) {
|
|
3137
|
+
return expr.replace(/[A-Za-z_][A-Za-z0-9_]*/g, (token) => renamed(token, map));
|
|
3138
|
+
}
|
|
3139
|
+
function renameComposition(composition, map) {
|
|
3140
|
+
for (const template of composition.templates ?? []) {
|
|
3141
|
+
template.name = renamed(template.name, map);
|
|
3142
|
+
}
|
|
3143
|
+
for (const obj of composition.objects ?? []) {
|
|
3144
|
+
obj.name = renamed(obj.name, map);
|
|
3145
|
+
if (obj.template !== void 0) obj.template = renamed(obj.template, map);
|
|
3146
|
+
}
|
|
3147
|
+
const connections = composition.connections;
|
|
3148
|
+
for (const conn of connections ?? []) {
|
|
3149
|
+
conn.from = renamed(conn.from, map);
|
|
3150
|
+
conn.to = renamed(conn.to, map);
|
|
3151
|
+
}
|
|
3152
|
+
for (const prop of composition.state?.properties ?? []) {
|
|
3153
|
+
prop.key = renamed(prop.key, map);
|
|
3154
|
+
}
|
|
3155
|
+
const logic = composition.logic;
|
|
3156
|
+
for (const handler of logic?.handlers ?? []) {
|
|
3157
|
+
renameStatements(handler.body, map);
|
|
3158
|
+
}
|
|
3159
|
+
for (const machine of composition.stateMachines ?? []) {
|
|
3160
|
+
machine.name = renamed(machine.name, map);
|
|
3161
|
+
machine.initialState = renamed(machine.initialState, map);
|
|
3162
|
+
const renamedStates = {};
|
|
3163
|
+
for (const [stateName, stateNode] of Object.entries(machine.states ?? {})) {
|
|
3164
|
+
stateNode.name = renamed(stateName, map);
|
|
3165
|
+
renamedStates[renamed(stateName, map)] = stateNode;
|
|
3166
|
+
for (const t of stateNode.transitions ?? []) {
|
|
3167
|
+
if (t.from && t.from !== "any") t.from = renamed(t.from, map);
|
|
3168
|
+
t.target = renamed(t.target, map);
|
|
3169
|
+
if (t.condition) renameExpression(t.condition, map);
|
|
3170
|
+
for (const cond of t.conditions ?? []) cond.parameter = renamed(cond.parameter, map);
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
machine.states = renamedStates;
|
|
3174
|
+
for (const input of machine.inputs ?? []) {
|
|
3175
|
+
input.name = renamed(input.name, map);
|
|
3176
|
+
}
|
|
3177
|
+
for (const t of machine.transitions ?? []) {
|
|
3178
|
+
if (t.from && t.from !== "any") t.from = renamed(t.from, map);
|
|
3179
|
+
t.target = renamed(t.target, map);
|
|
3180
|
+
if (t.condition) renameExpression(t.condition, map);
|
|
3181
|
+
for (const cond of t.conditions ?? []) cond.parameter = renamed(cond.parameter, map);
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
if (composition.contract) {
|
|
3185
|
+
for (const clause of [...composition.contract.preconditions, ...composition.contract.invariants]) {
|
|
3186
|
+
clause.expr = renameRawExpr(clause.expr, map);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
for (const achievement of composition.achievements ?? []) {
|
|
3190
|
+
renameExpression(achievement.condition, map);
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
var ID_TOKEN_PATTERN = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
3194
|
+
function renameIdString(id, map) {
|
|
3195
|
+
return id.replace(ID_TOKEN_PATTERN, (token) => renamed(token, map));
|
|
3196
|
+
}
|
|
3197
|
+
function renameTrace(trace, map) {
|
|
3198
|
+
const renameSnapshot = (snap) => {
|
|
3199
|
+
const state = {};
|
|
3200
|
+
for (const [key, value] of Object.entries(snap.state)) state[renamed(key, map)] = value;
|
|
3201
|
+
const machineStates = {};
|
|
3202
|
+
for (const [machine, current] of Object.entries(snap.machineStates)) {
|
|
3203
|
+
machineStates[renamed(machine, map)] = renamed(current, map);
|
|
3204
|
+
}
|
|
3205
|
+
return { state, machineStates, stateDigest: hsiSha256({ state: sortRecord(state), machineStates: sortRecord(machineStates) }) };
|
|
3206
|
+
};
|
|
3207
|
+
const sortRecord = (record) => {
|
|
3208
|
+
const out = {};
|
|
3209
|
+
for (const key of Object.keys(record).sort()) out[key] = record[key];
|
|
3210
|
+
return out;
|
|
3211
|
+
};
|
|
3212
|
+
const stepsRenamed = trace.steps.map((step) => {
|
|
3213
|
+
const scenario = step.step.kind === "fire-event" ? step.step : step.step.kind === "set-input" ? { kind: "set-input", machine: renamed(step.step.machine, map), input: renamed(step.step.input, map), value: step.step.value } : { kind: "fire-trigger", machine: renamed(step.step.machine, map), input: renamed(step.step.input, map) };
|
|
3214
|
+
return {
|
|
3215
|
+
...step,
|
|
3216
|
+
step: scenario,
|
|
3217
|
+
transitions: step.transitions.map((t) => ({
|
|
3218
|
+
machine: renamed(t.machine, map),
|
|
3219
|
+
transitionId: renameIdString(t.transitionId, map),
|
|
3220
|
+
from: renamed(t.from, map),
|
|
3221
|
+
to: renamed(t.to, map)
|
|
3222
|
+
})),
|
|
3223
|
+
effects: step.effects.map((e) => ({ ...e, target: renamed(e.target, map), sourceId: renameIdString(e.sourceId, map) })),
|
|
3224
|
+
invariantViolations: step.invariantViolations.map((id) => renameIdString(id, map)),
|
|
3225
|
+
stateDigest: ""
|
|
3226
|
+
};
|
|
3227
|
+
});
|
|
3228
|
+
const initial = renameSnapshot(trace.initial);
|
|
3229
|
+
const final = renameSnapshot(trace.final);
|
|
3230
|
+
const traceWithoutDigest = {
|
|
3231
|
+
schemaVersion: trace.schemaVersion,
|
|
3232
|
+
kind: trace.kind,
|
|
3233
|
+
worldDigest: "",
|
|
3234
|
+
preconditionResults: trace.preconditionResults.map((r) => ({ id: renameIdString(r.id, map), holds: r.holds })),
|
|
3235
|
+
initial,
|
|
3236
|
+
steps: stepsRenamed,
|
|
3237
|
+
final,
|
|
3238
|
+
valid: trace.valid
|
|
3239
|
+
};
|
|
3240
|
+
return { ...traceWithoutDigest, deterministicDigest: "" };
|
|
3241
|
+
}
|
|
3242
|
+
function behavioralProjection(trace) {
|
|
3243
|
+
return {
|
|
3244
|
+
preconditionResults: trace.preconditionResults,
|
|
3245
|
+
initial: { state: trace.initial.state, machineStates: trace.initial.machineStates },
|
|
3246
|
+
steps: trace.steps.map((s) => ({
|
|
3247
|
+
ordinal: s.ordinal,
|
|
3248
|
+
step: s.step,
|
|
3249
|
+
transitions: s.transitions,
|
|
3250
|
+
effects: s.effects,
|
|
3251
|
+
emitted: s.emitted,
|
|
3252
|
+
invariantViolations: s.invariantViolations
|
|
3253
|
+
})),
|
|
3254
|
+
final: { state: trace.final.state, machineStates: trace.final.machineStates },
|
|
3255
|
+
valid: trace.valid
|
|
3256
|
+
};
|
|
3257
|
+
}
|
|
3258
|
+
function reorderComposition(composition) {
|
|
3259
|
+
composition.templates = [...composition.templates ?? []].reverse();
|
|
3260
|
+
composition.objects = [...composition.objects ?? []].reverse();
|
|
3261
|
+
const holder = composition;
|
|
3262
|
+
if (holder.connections) holder.connections = [...holder.connections].reverse();
|
|
3263
|
+
if (composition.state?.properties) {
|
|
3264
|
+
composition.state.properties = [...composition.state.properties].reverse();
|
|
3265
|
+
}
|
|
3266
|
+
if (composition.contract) {
|
|
3267
|
+
composition.contract.invariants = [...composition.contract.invariants].reverse();
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
function applyIntervention(composition, intervention) {
|
|
3271
|
+
if (intervention.kind === "set-opacity") {
|
|
3272
|
+
const obj = (composition.objects ?? []).find((o) => o.name === intervention.entity);
|
|
3273
|
+
if (!obj) {
|
|
3274
|
+
throw new HSIAdmissionError("admission-skewed", `intervention targets unknown entity "${intervention.entity}"`);
|
|
3275
|
+
}
|
|
3276
|
+
const existing = obj.properties.findIndex((p) => p.key === "opaque");
|
|
3277
|
+
if (existing >= 0) obj.properties.splice(existing, 1);
|
|
3278
|
+
if (intervention.opaque !== void 0) {
|
|
3279
|
+
obj.properties.push({ type: "ObjectProperty", key: "opaque", value: intervention.opaque });
|
|
3280
|
+
} else {
|
|
3281
|
+
const template = (composition.templates ?? []).find((t) => t.name === obj.template);
|
|
3282
|
+
if (template) {
|
|
3283
|
+
template.properties = (template.properties ?? []).filter((p) => p.key !== "opaque");
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
return;
|
|
3287
|
+
}
|
|
3288
|
+
const machine = (composition.stateMachines ?? []).find((m) => m.name === intervention.machine);
|
|
3289
|
+
if (!machine) {
|
|
3290
|
+
throw new HSIAdmissionError("admission-skewed", `intervention targets unknown machine "${intervention.machine}"`);
|
|
3291
|
+
}
|
|
3292
|
+
const transitions = [
|
|
3293
|
+
...machine.transitions ?? [],
|
|
3294
|
+
...Object.values(machine.states ?? {}).flatMap((s) => s.transitions ?? [])
|
|
3295
|
+
];
|
|
3296
|
+
const transition = transitions.find(
|
|
3297
|
+
(t) => (t.from ?? "") === intervention.fromState && t.target === intervention.toState
|
|
3298
|
+
);
|
|
3299
|
+
if (!transition) {
|
|
3300
|
+
throw new HSIAdmissionError(
|
|
3301
|
+
"admission-skewed",
|
|
3302
|
+
`intervention targets unknown transition ${intervention.fromState} -> ${intervention.toState}`
|
|
3303
|
+
);
|
|
3304
|
+
}
|
|
3305
|
+
if (transition.condition?.type === "BinaryExpression" && transition.condition.right.type === "Literal") {
|
|
3306
|
+
transition.condition.right.value = intervention.value;
|
|
3307
|
+
}
|
|
3308
|
+
for (const cond of transition.conditions ?? []) {
|
|
3309
|
+
if (typeof cond.value === "number") cond.value = intervention.value;
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
function generateAuditCases(ir) {
|
|
3313
|
+
const cases = [
|
|
3314
|
+
{
|
|
3315
|
+
id: "audit:admission-empty",
|
|
3316
|
+
kind: "admission-empty",
|
|
3317
|
+
description: "An empty composition (no entities, no state) must fail admission.",
|
|
3318
|
+
expectation: "HSIAdmissionError(empty-world)"
|
|
3319
|
+
},
|
|
3320
|
+
{
|
|
3321
|
+
id: "audit:admission-missing-archetype",
|
|
3322
|
+
kind: "admission-skewed",
|
|
3323
|
+
description: "An object using an undeclared template must fail admission.",
|
|
3324
|
+
expectation: "HSIAdmissionError(unknown-archetype)"
|
|
3325
|
+
},
|
|
3326
|
+
{
|
|
3327
|
+
id: "audit:admission-dangling-relation",
|
|
3328
|
+
kind: "admission-skewed",
|
|
3329
|
+
description: "A connection to an undeclared entity must fail admission.",
|
|
3330
|
+
expectation: "HSIAdmissionError(unknown-relation-endpoint)"
|
|
3331
|
+
},
|
|
3332
|
+
{
|
|
3333
|
+
id: "audit:alpha-rename",
|
|
3334
|
+
kind: "alpha-rename",
|
|
3335
|
+
description: "Consistent renaming of declared names preserves behavior (trace modulo names).",
|
|
3336
|
+
expectation: "behavioralProjection(renameTrace(base)) === behavioralProjection(trace(renamed))"
|
|
3337
|
+
},
|
|
3338
|
+
{
|
|
3339
|
+
id: "audit:declaration-reorder",
|
|
3340
|
+
kind: "declaration-reorder",
|
|
3341
|
+
description: "Reordering independent declarations yields a byte-identical IR.",
|
|
3342
|
+
expectation: "deterministicDigest(base) === deterministicDigest(reordered)"
|
|
3343
|
+
},
|
|
3344
|
+
{
|
|
3345
|
+
id: "audit:regenerate-determinism",
|
|
3346
|
+
kind: "regenerate-determinism",
|
|
3347
|
+
description: "Re-lowering and re-tracing from the same source is byte-identical.",
|
|
3348
|
+
expectation: "all artifact digests equal across two independent regenerations"
|
|
3349
|
+
}
|
|
3350
|
+
];
|
|
3351
|
+
const opaqueMediator = ir.entities.find(
|
|
3352
|
+
(e) => e.opacity === "opaque" && ir.observationPolicy.some((r) => r.mediators.includes(e.name) && r.access === "blocked")
|
|
3353
|
+
);
|
|
3354
|
+
if (opaqueMediator) {
|
|
3355
|
+
cases.push({
|
|
3356
|
+
id: "audit:counterfactual-opacity",
|
|
3357
|
+
kind: "counterfactual-intervention",
|
|
3358
|
+
description: `Making "${opaqueMediator.name}" transparent must change at least one observation access verdict.`,
|
|
3359
|
+
intervention: { kind: "set-opacity", id: "intervention:flip-opacity", entity: opaqueMediator.name, opacity: "transparent" },
|
|
3360
|
+
expectation: "observationPolicy access verdicts differ from base IR"
|
|
3361
|
+
});
|
|
3362
|
+
}
|
|
3363
|
+
const guardedTransition = ir.machines.flatMap((m) => m.transitions.map((t) => ({ machine: m.name, t }))).find(({ t }) => t.guard?.kind === "BinaryExpression" && t.guard.right.kind === "Literal" && typeof t.guard.right.value === "number");
|
|
3364
|
+
if (guardedTransition) {
|
|
3365
|
+
cases.push({
|
|
3366
|
+
id: "audit:counterfactual-guard",
|
|
3367
|
+
kind: "counterfactual-intervention",
|
|
3368
|
+
description: `Raising the numeric guard of ${guardedTransition.t.id} beyond scenario inputs must change the exact trace.`,
|
|
3369
|
+
intervention: {
|
|
3370
|
+
kind: "set-transition-guard-literal",
|
|
3371
|
+
id: "intervention:raise-guard-threshold",
|
|
3372
|
+
machine: guardedTransition.machine,
|
|
3373
|
+
transitionId: guardedTransition.t.id,
|
|
3374
|
+
value: 1e6
|
|
3375
|
+
},
|
|
3376
|
+
expectation: "trace deterministicDigest differs and at least one step fires different transitions"
|
|
3377
|
+
});
|
|
3378
|
+
}
|
|
3379
|
+
return cases;
|
|
3380
|
+
}
|
|
3381
|
+
function clone(value) {
|
|
3382
|
+
return structuredClone(value);
|
|
3383
|
+
}
|
|
3384
|
+
function expectAdmissionFailure(fn, expectedCode) {
|
|
3385
|
+
try {
|
|
3386
|
+
fn();
|
|
3387
|
+
return "fail";
|
|
3388
|
+
} catch (error) {
|
|
3389
|
+
return error instanceof HSIAdmissionError && error.code === expectedCode ? "pass" : "fail";
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
function runHSIAudit(input) {
|
|
3393
|
+
const { composition, sourceText, scenario, renameMap } = input;
|
|
3394
|
+
const baseIR = lowerCompositionToHSIIR(clone(composition), { sourceText });
|
|
3395
|
+
const baseTrace = runExactTrace(baseIR, scenario);
|
|
3396
|
+
const cases = generateAuditCases(baseIR);
|
|
3397
|
+
const checks = [];
|
|
3398
|
+
for (const auditCase of cases) {
|
|
3399
|
+
let status = "fail";
|
|
3400
|
+
let detail = auditCase.expectation;
|
|
3401
|
+
try {
|
|
3402
|
+
switch (auditCase.id) {
|
|
3403
|
+
case "audit:admission-empty": {
|
|
3404
|
+
const empty = {
|
|
3405
|
+
...clone(composition),
|
|
3406
|
+
name: "EmptyWorld",
|
|
3407
|
+
templates: [],
|
|
3408
|
+
objects: [],
|
|
3409
|
+
state: void 0,
|
|
3410
|
+
stateMachines: [],
|
|
3411
|
+
contract: void 0,
|
|
3412
|
+
achievements: []
|
|
3413
|
+
};
|
|
3414
|
+
empty.connections = [];
|
|
3415
|
+
empty.logic = void 0;
|
|
3416
|
+
status = expectAdmissionFailure(() => lowerCompositionToHSIIR(empty), "empty-world");
|
|
3417
|
+
break;
|
|
3418
|
+
}
|
|
3419
|
+
case "audit:admission-missing-archetype": {
|
|
3420
|
+
const skewed = clone(composition);
|
|
3421
|
+
if (skewed.objects.length === 0) throw new Error("fixture has no objects");
|
|
3422
|
+
skewed.objects[0].template = "GhostTemplate";
|
|
3423
|
+
status = expectAdmissionFailure(() => lowerCompositionToHSIIR(skewed), "unknown-archetype");
|
|
3424
|
+
break;
|
|
3425
|
+
}
|
|
3426
|
+
case "audit:admission-dangling-relation": {
|
|
3427
|
+
const skewed = clone(composition);
|
|
3428
|
+
const holder = skewed;
|
|
3429
|
+
holder.connections = [...holder.connections ?? [], { from: "NoSuchEntity", to: "AlsoMissing" }];
|
|
3430
|
+
status = expectAdmissionFailure(() => lowerCompositionToHSIIR(skewed), "unknown-relation-endpoint");
|
|
3431
|
+
break;
|
|
3432
|
+
}
|
|
3433
|
+
case "audit:alpha-rename": {
|
|
3434
|
+
const renamedComposition = clone(composition);
|
|
3435
|
+
renameComposition(renamedComposition, renameMap);
|
|
3436
|
+
const renamedIR = lowerCompositionToHSIIR(renamedComposition);
|
|
3437
|
+
const renamedScenario = scenario.map(
|
|
3438
|
+
(step) => step.kind === "fire-event" ? step : step.kind === "set-input" ? { ...step, machine: renameMap[step.machine] ?? step.machine, input: renameMap[step.input] ?? step.input } : { ...step, machine: renameMap[step.machine] ?? step.machine, input: renameMap[step.input] ?? step.input }
|
|
3439
|
+
);
|
|
3440
|
+
const renamedTrace = runExactTrace(renamedIR, renamedScenario);
|
|
3441
|
+
const expected = hsiStableStringify(behavioralProjection(renameTrace(baseTrace, renameMap)));
|
|
3442
|
+
const actual = hsiStableStringify(behavioralProjection(renamedTrace));
|
|
3443
|
+
status = expected === actual ? "pass" : "fail";
|
|
3444
|
+
if (status === "fail") detail = "renamed trace diverged from rename-projected base trace";
|
|
3445
|
+
break;
|
|
3446
|
+
}
|
|
3447
|
+
case "audit:declaration-reorder": {
|
|
3448
|
+
const reordered = clone(composition);
|
|
3449
|
+
reorderComposition(reordered);
|
|
3450
|
+
const reorderedIR = lowerCompositionToHSIIR(reordered, { sourceText });
|
|
3451
|
+
status = reorderedIR.provenance.deterministicDigest === baseIR.provenance.deterministicDigest ? "pass" : "fail";
|
|
3452
|
+
if (status === "fail") detail = "reordered IR digest differs from base IR digest";
|
|
3453
|
+
break;
|
|
3454
|
+
}
|
|
3455
|
+
case "audit:regenerate-determinism": {
|
|
3456
|
+
const secondIR = lowerCompositionToHSIIR(clone(composition), { sourceText });
|
|
3457
|
+
const secondTrace = runExactTrace(secondIR, scenario);
|
|
3458
|
+
status = secondIR.provenance.deterministicDigest === baseIR.provenance.deterministicDigest && secondTrace.deterministicDigest === baseTrace.deterministicDigest ? "pass" : "fail";
|
|
3459
|
+
if (status === "fail") detail = "regeneration produced different digests";
|
|
3460
|
+
break;
|
|
3461
|
+
}
|
|
3462
|
+
case "audit:counterfactual-opacity": {
|
|
3463
|
+
const intervention = auditCase.intervention;
|
|
3464
|
+
if (intervention.kind !== "set-opacity") throw new Error("case/intervention mismatch");
|
|
3465
|
+
const intervened = clone(composition);
|
|
3466
|
+
applyIntervention(intervened, {
|
|
3467
|
+
id: intervention.id,
|
|
3468
|
+
kind: "set-opacity",
|
|
3469
|
+
entity: intervention.entity,
|
|
3470
|
+
opaque: intervention.opacity === "opaque" ? true : intervention.opacity === "transparent" ? false : void 0
|
|
3471
|
+
});
|
|
3472
|
+
const intervenedIR = lowerCompositionToHSIIR(intervened);
|
|
3473
|
+
const baseAccess = hsiStableStringify(baseIR.observationPolicy);
|
|
3474
|
+
const newAccess = hsiStableStringify(intervenedIR.observationPolicy);
|
|
3475
|
+
status = baseAccess !== newAccess ? "pass" : "fail";
|
|
3476
|
+
if (status === "fail") detail = "opacity intervention did not change any observation verdict";
|
|
3477
|
+
break;
|
|
3478
|
+
}
|
|
3479
|
+
case "audit:counterfactual-guard": {
|
|
3480
|
+
const intervention = auditCase.intervention;
|
|
3481
|
+
if (intervention.kind !== "set-transition-guard-literal") throw new Error("case/intervention mismatch");
|
|
3482
|
+
const parsed = /transition:([^.]+)\.([^-]+)->([^#]+)#/.exec(intervention.transitionId);
|
|
3483
|
+
if (!parsed) throw new Error(`unparseable transition id ${intervention.transitionId}`);
|
|
3484
|
+
const intervened = clone(composition);
|
|
3485
|
+
applyIntervention(intervened, {
|
|
3486
|
+
id: intervention.id,
|
|
3487
|
+
kind: "set-guard-literal",
|
|
3488
|
+
machine: parsed[1],
|
|
3489
|
+
fromState: parsed[2],
|
|
3490
|
+
toState: parsed[3],
|
|
3491
|
+
value: intervention.value
|
|
3492
|
+
});
|
|
3493
|
+
const intervenedIR = lowerCompositionToHSIIR(intervened);
|
|
3494
|
+
const intervenedTrace = runExactTrace(intervenedIR, scenario);
|
|
3495
|
+
const digestDiffers = intervenedTrace.deterministicDigest !== baseTrace.deterministicDigest;
|
|
3496
|
+
const stepDiffers = baseTrace.steps.some(
|
|
3497
|
+
(step, i) => hsiStableStringify(step.transitions) !== hsiStableStringify(intervenedTrace.steps[i]?.transitions)
|
|
3498
|
+
);
|
|
3499
|
+
status = digestDiffers && stepDiffers ? "pass" : "fail";
|
|
3500
|
+
if (status === "fail") detail = "guard intervention did not change the exact trace";
|
|
3501
|
+
break;
|
|
3502
|
+
}
|
|
3503
|
+
default:
|
|
3504
|
+
detail = `unrecognized audit case ${auditCase.id}`;
|
|
3505
|
+
}
|
|
3506
|
+
} catch (error) {
|
|
3507
|
+
status = "fail";
|
|
3508
|
+
detail = `${detail} \u2014 threw: ${error.message}`;
|
|
3509
|
+
}
|
|
3510
|
+
checks.push({ caseId: auditCase.id, status, detail });
|
|
3511
|
+
}
|
|
3512
|
+
const overall = checks.every((c) => c.status === "pass") ? "pass" : "fail";
|
|
3513
|
+
const manifestWithoutDigest = {
|
|
3514
|
+
schemaVersion: HSI_AUDIT_SCHEMA_VERSION,
|
|
3515
|
+
kind: "HSIAuditManifest",
|
|
3516
|
+
worldDigest: baseIR.provenance.deterministicDigest,
|
|
3517
|
+
verifier: "HSIAuditVerifier",
|
|
3518
|
+
checks,
|
|
3519
|
+
overall
|
|
3520
|
+
};
|
|
3521
|
+
return { ...manifestWithoutDigest, deterministicDigest: hsiSha256(manifestWithoutDigest) };
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
// src/compiler/index.ts
|
|
1587
3525
|
init_PlatformConditionalCompilerMixin();
|
|
1588
3526
|
|
|
1589
3527
|
// src/compiler/authority/AuthoritySymbolGraph.ts
|
|
@@ -3180,7 +5118,7 @@ async function runTier3CpuDirectExecutor(op) {
|
|
|
3180
5118
|
}
|
|
3181
5119
|
async function runTier1BrowserSnn(op) {
|
|
3182
5120
|
try {
|
|
3183
|
-
const snnWebgpu = await import('../dist-
|
|
5121
|
+
const snnWebgpu = await import('../dist-ZEZXRCBG.js');
|
|
3184
5122
|
const { GPUContext, LIFSimulator, DEFAULT_LIF_PARAMS } = snnWebgpu;
|
|
3185
5123
|
const ctx = new GPUContext();
|
|
3186
5124
|
await ctx.initialize();
|
|
@@ -3285,8 +5223,8 @@ async function sha256Hex(input) {
|
|
|
3285
5223
|
const digest = await subtle.digest("SHA-256", new TextEncoder().encode(input));
|
|
3286
5224
|
return bytesToHex(new Uint8Array(digest));
|
|
3287
5225
|
}
|
|
3288
|
-
const { createHash } = await import('crypto');
|
|
3289
|
-
return
|
|
5226
|
+
const { createHash: createHash4 } = await import('crypto');
|
|
5227
|
+
return createHash4("sha256").update(input).digest("hex");
|
|
3290
5228
|
}
|
|
3291
5229
|
function bytesToHex(bytes) {
|
|
3292
5230
|
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
@@ -4099,6 +6037,18 @@ platformPluginRegistry.register(new AndroidARCorePlugin());
|
|
|
4099
6037
|
var SOVEREIGN_TARGETS = [
|
|
4100
6038
|
"webgpu",
|
|
4101
6039
|
// WebGPUCompiler → WGSL compute+render shaders on our own WebGPU device (WebGPURenderer)
|
|
6040
|
+
"audio",
|
|
6041
|
+
// SpatialAudioCompiler → our own Web Audio graph (HRTF PannerNode + Convolver reverb + filters); no third-party audio engine
|
|
6042
|
+
"desktop-gpu",
|
|
6043
|
+
// DesktopGPUCompiler → standalone Rust wgpu project (Vulkan/Metal/DX12), renders the scene offscreen on the machine's own GPU; no browser, no third-party engine. Verified on Jetson Orin/Vulkan.
|
|
6044
|
+
"pathtrace",
|
|
6045
|
+
// PathTracerCompiler → standalone Rust wgpu COMPUTE path tracer (cosine-weighted GI, emissive area lights, multi-sample) → tonemapped PNG; our own tracer, no Cycles/OptiX. Verified on Jetson Orin/Vulkan.
|
|
6046
|
+
"pathtrace-cpu",
|
|
6047
|
+
// CpuPathTracer → the no-GPU fallback: pure-TS CPU path tracer (same algorithm + shared scene extraction) → PNG. Runs offline GI anywhere Node/JS runs (server/CI/old device). The "runs anywhere / no-WebGPU compute fallback" gap.
|
|
6048
|
+
"media",
|
|
6049
|
+
// MediaPipelineCompiler → renders an animated turntable of the scene (CPU) and encodes it to an APNG with our OWN encoder (node:zlib only, no ffmpeg/codec/muxer). The video/media-pipeline gap; a moving picture from a .holo.
|
|
6050
|
+
"physics-sim",
|
|
6051
|
+
// ComputePhysicsCompiler → WGSL COMPUTE rigid-body solver (gravity + sphere/sphere + sphere/AABB collisions, restitution, double-buffered) that steps on the GPU + renders the settled state. Our own solver, no third-party physics engine. Consumes the collider vocabulary; the DYNAMICS layer on top of PhysicsColliderCompiler. Verified on Jetson Orin/Vulkan.
|
|
4102
6052
|
"character-webgpu",
|
|
4103
6053
|
// CharacterWebGPUCompiler → authored .holo character → CharacterDrawSpec run by our renderCharacter (sovereign skinned-character path)
|
|
4104
6054
|
"nir",
|
|
@@ -4140,6 +6090,8 @@ var BRIDGE_TARGETS = [
|
|
|
4140
6090
|
// Meta Quest (Horizon OS / Meta Spatial SDK) — emits a native Kotlin app to Meta's runtime
|
|
4141
6091
|
"ios",
|
|
4142
6092
|
"visionos",
|
|
6093
|
+
"r3f",
|
|
6094
|
+
// SceneIR -> generated TSX shell consumed by React Three Fiber; HoloScript owns SceneIR, R3F is the host runtime bridge
|
|
4143
6095
|
"usd",
|
|
4144
6096
|
"usdz",
|
|
4145
6097
|
"fmu",
|
|
@@ -4320,9 +6272,69 @@ var SOVEREIGN_ENGINES = [
|
|
|
4320
6272
|
tests: true,
|
|
4321
6273
|
promoted: false,
|
|
4322
6274
|
note: "Signed-distance-field \u2192 raymarch WGSL/GLSL. Native GPU renderer codegen."
|
|
6275
|
+
},
|
|
6276
|
+
{
|
|
6277
|
+
id: "spatial-audio",
|
|
6278
|
+
name: "SpatialAudioCompiler",
|
|
6279
|
+
file: "packages/core/src/compiler/SpatialAudioCompiler.ts",
|
|
6280
|
+
kind: "compiler",
|
|
6281
|
+
maturity: "real",
|
|
6282
|
+
tests: true,
|
|
6283
|
+
promoted: false,
|
|
6284
|
+
note: "The audio peer of WebGPU: emits our OWN Web Audio graph (HRTF PannerNode + Convolver reverb from synthesized rt60 impulses + directivity/occlusion/portal filters) from @audio_source/@audio_listener/@reverb_zone/@audio_material/@audio_occlusion/@audio_portal traits. Sovereign \u2014 no FMOD/Wwise/Resonance. Fills the #1 spatial-computing gap (no audio compiler existed)."
|
|
6285
|
+
},
|
|
6286
|
+
{
|
|
6287
|
+
id: "desktop-gpu",
|
|
6288
|
+
name: "DesktopGPUCompiler",
|
|
6289
|
+
file: "packages/core/src/compiler/DesktopGPUCompiler.ts",
|
|
6290
|
+
kind: "compiler",
|
|
6291
|
+
maturity: "real",
|
|
6292
|
+
tests: true,
|
|
6293
|
+
promoted: false,
|
|
6294
|
+
note: `Sovereign native-desktop GPU: emits a self-contained Rust wgpu project (Vulkan/Metal/DX12) that renders the scene offscreen to a PNG on the machine's OWN GPU \u2014 standalone, no browser, no third-party engine. WebGPU is browser/runtime-bound; this is the "runs standalone at native perf" gap. Matrices computed in the compiler; geometry generators + wgpu host emitted. VERIFIED on real hardware: Jetson Orin (Tegra) via Vulkan rendered a 6-object scene offscreen to PNG (adapter=NVIDIA Tegra Orin, backend=Vulkan). Consumes the same geometry-registry + geometry-purpose vocabulary as render/physics/audio.`
|
|
6295
|
+
},
|
|
6296
|
+
{
|
|
6297
|
+
id: "path-tracer",
|
|
6298
|
+
name: "PathTracerCompiler",
|
|
6299
|
+
file: "packages/core/src/compiler/PathTracerCompiler.ts",
|
|
6300
|
+
kind: "compiler",
|
|
6301
|
+
maturity: "real",
|
|
6302
|
+
tests: true,
|
|
6303
|
+
promoted: false,
|
|
6304
|
+
note: "Sovereign OFFLINE path-traced render \u2014 the film-grade-still gap the rasterizers (WebGPU/desktop-gpu) do not fill. Emits a self-contained Rust wgpu COMPUTE program: our OWN GPU path tracer (no Cycles/OptiX) that traces cosine-weighted diffuse bounces against analytic sphere/box primitives, accumulates many samples, and tonemaps to PNG. Global illumination \u2014 soft shadows, colour bleeding, emissive area lights. VERIFIED on Jetson Orin (Tegra/Vulkan): a Cornell-box scene rendered with visible GI colour-bleed + soft shadows. Fifth consumer of the shared geometry-registry + geometry-purpose vocabulary."
|
|
6305
|
+
},
|
|
6306
|
+
{
|
|
6307
|
+
id: "cpu-path-tracer",
|
|
6308
|
+
name: "CpuPathTracer",
|
|
6309
|
+
file: "packages/core/src/compiler/CpuPathTracer.ts",
|
|
6310
|
+
kind: "runtime",
|
|
6311
|
+
maturity: "real",
|
|
6312
|
+
tests: true,
|
|
6313
|
+
promoted: false,
|
|
6314
|
+
note: 'The sovereign no-GPU FALLBACK: a pure-TypeScript CPU path tracer that RUNS (not emits) the same GI algorithm as PathTracerCompiler, sharing extractRaytraceScene so the two render the same scene. Renders offline global-illumination stills anywhere Node/JS runs \u2014 server, CI, old device, no WebGPU/GPU needed. Ships its own PNG encoder (node:zlib), zero deps. Fills the "runs anywhere / no-WebGPU compute fallback" gap the audit named (compiler-wasm is a parser, not a compute backend).'
|
|
6315
|
+
},
|
|
6316
|
+
{
|
|
6317
|
+
id: "media-pipeline",
|
|
6318
|
+
name: "MediaPipelineCompiler",
|
|
6319
|
+
file: "packages/core/src/compiler/MediaPipelineCompiler.ts",
|
|
6320
|
+
kind: "runtime",
|
|
6321
|
+
maturity: "real",
|
|
6322
|
+
tests: true,
|
|
6323
|
+
promoted: false,
|
|
6324
|
+
note: "The sovereign VIDEO / media-pipeline: renders an animated turntable of the scene (orbiting camera, fast CPU flat projector over the shared raytrace-scene extraction) and encodes the frame sequence to an APNG with our OWN encoder (acTL/fcTL/fdAT via node:zlib) \u2014 no ffmpeg / codec / muxer dependency. A moving picture from a .holo, runs anywhere Node runs. Fills the media-pipeline gap the audit named; browser-native WebCodecs H.264/WebM is the follow-on encode path."
|
|
6325
|
+
},
|
|
6326
|
+
{
|
|
6327
|
+
id: "compute-physics",
|
|
6328
|
+
name: "ComputePhysicsCompiler",
|
|
6329
|
+
file: "packages/core/src/compiler/ComputePhysicsCompiler.ts",
|
|
6330
|
+
kind: "compiler",
|
|
6331
|
+
maturity: "real",
|
|
6332
|
+
tests: true,
|
|
6333
|
+
promoted: false,
|
|
6334
|
+
note: "The sovereign GPU rigid-body SIMULATION \u2014 the dynamics layer PhysicsColliderCompiler was missing. Emits a Rust wgpu program with a WGSL COMPUTE solver: gravity + sphere/sphere + sphere/static-AABB collisions with restitution + friction, double-buffered (gather, race-free), stepped N times on the GPU; then renders the settled state to PNG. Our own solver, no third-party physics engine. One .holo drives BOTH render and sim (Grok + Claude converged design). Dynamic bodies = @rigid_body spheres; statics = scene boxes. VERIFIED on Jetson Orin (Tegra/Vulkan): spheres dropped above a floor fall, collide and settle. Hooks the physics-discovery / provenance goals."
|
|
4323
6335
|
}
|
|
4324
6336
|
];
|
|
4325
6337
|
|
|
4326
|
-
export { AgentTrustLedger, AlphaTracker, AndroidARCorePlugin, AuthoritySymbolGraph, BRIDGE_TARGETS, COLYSEUS_CONSTANT_TIERS, COLYSEUS_EMIT_FILE, COLYSEUS_SCHEMA_FIELD_TIERS, DEFAULT_DISPATCH_LATENCY_OPERATION, DispatchPolicy, DispatchTier, MCPConfigCompiler, MultiLayerCompiler, NATIVE_COMPILE_MODES, NextJSCompiler, ProvenanceBoundsChecker, SOVEREIGN_ENGINES, SOVEREIGN_TARGETS, Vector2DCompiler, WebGPUWGSLPlugin, buildColyseusAuthorityGraph, buildColyseusCrossFileContext, byzantineResilientMerge, createAuthoritySymbolGraph, createDefaultDispatchLatencyScenarios, createProvenanceBoundsChecker, createTier3CpuDirectOutput, detectWasmRuntime, formatDispatchLatencyBenchmarkReport, generateTraitForTarget, getAllManifests, getTraitInfo, isBridgeTarget, isSovereignTarget, listTraitsForTarget, manifestFromPlugin, mergeSocialCausalModels, mostRestrictiveTier, platformPluginRegistry, queryTrait, recommendDispatchPolicyDefaults, runCompilerWasmSnnEmulator, runDispatchPolicyLatencyBenchmark, splitServerAuthority, targetSovereignty, traitExists };
|
|
6338
|
+
export { AgentTrustLedger, AlphaTracker, AndroidARCorePlugin, AuthoritySymbolGraph, BRIDGE_TARGETS, COLYSEUS_CONSTANT_TIERS, COLYSEUS_EMIT_FILE, COLYSEUS_SCHEMA_FIELD_TIERS, DEFAULT_DISPATCH_LATENCY_OPERATION, DispatchPolicy, DispatchTier, HSIAdmissionError, HSIIRCompiler, HSI_AUDIT_SCHEMA_VERSION, HSI_IR_SCHEMA_VERSION, HSI_LEARNING_GRAPH_SCHEMA_VERSION, HSI_OBSERVATION_MEDIATOR_EDGE, HSI_TRACE_SCHEMA_VERSION, MCPConfigCompiler, MultiLayerCompiler, NATIVE_COMPILE_MODES, NextJSCompiler, ProvenanceBoundsChecker, SOVEREIGN_ENGINES, SOVEREIGN_TARGETS, Vector2DCompiler, WebGPUWGSLPlugin, applyIntervention, behavioralProjection, buildColyseusAuthorityGraph, buildColyseusCrossFileContext, byzantineResilientMerge, createAuthoritySymbolGraph, createDefaultDispatchLatencyScenarios, createHSIIRCompiler, createProvenanceBoundsChecker, createTier3CpuDirectOutput, detectWasmRuntime, emitSceneIRTsx, formatDispatchLatencyBenchmarkReport, generateAuditCases, generateTraitForTarget, getAllManifests, getTraitInfo, hsiSha256, hsiSourceTextDigest, hsiStableStringify, isBridgeTarget, isSovereignTarget, listTraitsForTarget, lowerCompositionToHSIIR, lowerHoloExpression, manifestFromPlugin, mergeSocialCausalModels, mostRestrictiveTier, platformPluginRegistry, projectLearningGraph, queryTrait, recommendDispatchPolicyDefaults, renameComposition, renameTrace, reorderComposition, runCompilerWasmSnnEmulator, runDispatchPolicyLatencyBenchmark, runExactTrace, runHSIAudit, splitServerAuthority, targetSovereignty, traitExists };
|
|
4327
6339
|
//# sourceMappingURL=index.js.map
|
|
4328
6340
|
//# sourceMappingURL=index.js.map
|