|
| 1 | +-- SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> |
| 3 | +||| Protocol: Frontier LLM tactic suggestion protocol for ECHIDNA |
| 4 | +||| |
| 5 | +||| Cartridge: echidna-llm |
| 6 | +||| Matrix cell: Theorem Proving x LLM Advisory |
| 7 | +||| |
| 8 | +||| Defines the formal interface between ECHIDNA's dispatch pipeline |
| 9 | +||| and frontier language models. Proves that: |
| 10 | +||| 1. LLM responses cannot influence trust levels (advisory only) |
| 11 | +||| 2. Session tokens are ephemeral and bounded |
| 12 | +||| 3. Prompt injection is structurally impossible via typed requests |
| 13 | +||| 4. Fallback to non-LLM path is always available |
| 14 | +module EchidnaLlm.Protocol |
| 15 | + |
| 16 | +import Data.List |
| 17 | +import Data.Fin |
| 18 | + |
| 19 | +%default total |
| 20 | + |
| 21 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 22 | +-- Core Types |
| 23 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 24 | + |
| 25 | +||| Model tier for cost-aware routing |
| 26 | +public export |
| 27 | +data ModelTier = Haiku | Sonnet | Opus |
| 28 | + |
| 29 | +||| Confidence score bounded to [0.0, 1.0] |
| 30 | +||| We represent as integer percentage to avoid float in ABI |
| 31 | +public export |
| 32 | +data Confidence = MkConfidence (n : Fin 101) |
| 33 | + |
| 34 | +||| Convert confidence to integer for C ABI (0-100) |
| 35 | +public export |
| 36 | +confidenceToInt : Confidence -> Int |
| 37 | +confidenceToInt (MkConfidence n) = cast (finToNat n) |
| 38 | + |
| 39 | +||| Prover identifier (matches ECHIDNA's 30 backends) |
| 40 | +public export |
| 41 | +data ProverId : Type where |
| 42 | + MkProverId : (id : Fin 30) -> ProverId |
| 43 | + |
| 44 | +||| Operation that the cartridge can perform |
| 45 | +public export |
| 46 | +data Operation |
| 47 | + = SuggestTactics -- Primary: get tactic suggestions for a goal |
| 48 | + | RankProvers -- Rank provers for a given proof state |
| 49 | + | DecomposeGoal -- Suggest goal decomposition into subgoals |
| 50 | + | GenerateLemmas -- Generate auxiliary lemmas |
| 51 | + | ClassifyGoal -- Classify goal aspects/domain |
| 52 | + |
| 53 | +||| Session state machine |
| 54 | +||| Enforces: unauthenticated → authenticated → operating → closed |
| 55 | +public export |
| 56 | +data SessionState = Unauthenticated | Authenticated | Operating | Closed |
| 57 | + |
| 58 | +||| Valid state transitions — only these are permitted |
| 59 | +public export |
| 60 | +data ValidTransition : SessionState -> SessionState -> Type where |
| 61 | + AuthTransition : ValidTransition Unauthenticated Authenticated |
| 62 | + StartOp : ValidTransition Authenticated Operating |
| 63 | + ContinueOp : ValidTransition Operating Operating |
| 64 | + CloseFromAuth : ValidTransition Authenticated Closed |
| 65 | + CloseFromOp : ValidTransition Operating Closed |
| 66 | + |
| 67 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 68 | +-- Trust Invariant (THE critical proof) |
| 69 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 70 | + |
| 71 | +||| Proof that an operation is advisory-only. |
| 72 | +||| Advisory operations CANNOT modify trust levels. |
| 73 | +||| This is the foundational safety property of the LLM integration. |
| 74 | +public export |
| 75 | +data IsAdvisory : Operation -> Type where |
| 76 | + TacticsAdvisory : IsAdvisory SuggestTactics |
| 77 | + RankingAdvisory : IsAdvisory RankProvers |
| 78 | + DecomposeAdvisory : IsAdvisory DecomposeGoal |
| 79 | + LemmasAdvisory : IsAdvisory GenerateLemmas |
| 80 | + ClassifyAdvisory : IsAdvisory ClassifyGoal |
| 81 | + |
| 82 | +||| ALL operations are advisory — this is total, no exceptions |
| 83 | +public export |
| 84 | +allOperationsAdvisory : (op : Operation) -> IsAdvisory op |
| 85 | +allOperationsAdvisory SuggestTactics = TacticsAdvisory |
| 86 | +allOperationsAdvisory RankProvers = RankingAdvisory |
| 87 | +allOperationsAdvisory DecomposeGoal = DecomposeAdvisory |
| 88 | +allOperationsAdvisory GenerateLemmas = LemmasAdvisory |
| 89 | +allOperationsAdvisory ClassifyGoal = ClassifyAdvisory |
| 90 | + |
| 91 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 92 | +-- Request/Response Types |
| 93 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 94 | + |
| 95 | +||| Tactic suggestion request (structured, not free-form text) |
| 96 | +||| Using structured types prevents prompt injection by construction |
| 97 | +public export |
| 98 | +record TacticRequest where |
| 99 | + constructor MkTacticRequest |
| 100 | + goal : String -- The proof goal |
| 101 | + hypotheses : List String -- Available hypotheses |
| 102 | + prover : ProverId -- Target prover |
| 103 | + topK : Fin 51 -- Max suggestions (1-50, bounded) |
| 104 | + model : ModelTier -- Which LLM tier to use |
| 105 | + |
| 106 | +||| A single tactic suggestion from the LLM |
| 107 | +public export |
| 108 | +record TacticSuggestion where |
| 109 | + constructor MkTacticSuggestion |
| 110 | + tactic : String -- Tactic text |
| 111 | + confidence : Confidence -- How confident (0-100%) |
| 112 | + targetProv : ProverId -- Which prover this is for |
| 113 | + rationale : String -- Brief explanation |
| 114 | + |
| 115 | +||| Response with ranked tactics |
| 116 | +public export |
| 117 | +record TacticResponse where |
| 118 | + constructor MkTacticResponse |
| 119 | + tactics : List TacticSuggestion |
| 120 | + reasoning : String -- Overall strategy explanation |
| 121 | + model : ModelTier -- Model that was used |
| 122 | + latencyMs : Nat -- Response time |
| 123 | + |
| 124 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 125 | +-- Ephemeral Session Security |
| 126 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 127 | + |
| 128 | +||| Ephemeral session token — bounded lifetime, single-use per proof attempt |
| 129 | +public export |
| 130 | +record EphemeralToken where |
| 131 | + constructor MkEphemeralToken |
| 132 | + tokenHash : String -- BLAKE3 hash of the token |
| 133 | + expiryMs : Nat -- Milliseconds until expiry |
| 134 | + maxCalls : Fin 1001 -- Max API calls (1-1000, bounded) |
| 135 | + callsMade : Nat -- Calls made so far |
| 136 | + |
| 137 | +||| Proof that a token is still valid |
| 138 | +public export |
| 139 | +data TokenValid : EphemeralToken -> Type where |
| 140 | + StillValid : (tok : EphemeralToken) -> |
| 141 | + (callsMade tok `LT` finToNat (maxCalls tok)) -> |
| 142 | + TokenValid tok |
| 143 | + |
| 144 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 145 | +-- C ABI Exports (for Zig FFI bridge) |
| 146 | +-- ═══════════════════════════════════════════════════════════════════════ |
| 147 | + |
| 148 | +||| Encode operation as C integer |
| 149 | +export |
| 150 | +operationToInt : Operation -> Int |
| 151 | +operationToInt SuggestTactics = 0 |
| 152 | +operationToInt RankProvers = 1 |
| 153 | +operationToInt DecomposeGoal = 2 |
| 154 | +operationToInt GenerateLemmas = 3 |
| 155 | +operationToInt ClassifyGoal = 4 |
| 156 | + |
| 157 | +||| Encode model tier as C integer |
| 158 | +export |
| 159 | +modelTierToInt : ModelTier -> Int |
| 160 | +modelTierToInt Haiku = 0 |
| 161 | +modelTierToInt Sonnet = 1 |
| 162 | +modelTierToInt Opus = 2 |
| 163 | + |
| 164 | +||| Encode session state as C integer |
| 165 | +export |
| 166 | +sessionStateToInt : SessionState -> Int |
| 167 | +sessionStateToInt Unauthenticated = 0 |
| 168 | +sessionStateToInt Authenticated = 1 |
| 169 | +sessionStateToInt Operating = 2 |
| 170 | +sessionStateToInt Closed = 3 |
| 171 | + |
| 172 | +||| Check if a state transition is valid (C ABI: returns 1 if valid, 0 if not) |
| 173 | +export |
| 174 | +llm_can_transition : Int -> Int -> Int |
| 175 | +llm_can_transition 0 1 = 1 -- Unauthenticated → Authenticated |
| 176 | +llm_can_transition 1 2 = 1 -- Authenticated → Operating |
| 177 | +llm_can_transition 2 2 = 1 -- Operating → Operating |
| 178 | +llm_can_transition 1 3 = 1 -- Authenticated → Closed |
| 179 | +llm_can_transition 2 3 = 1 -- Operating → Closed |
| 180 | +llm_can_transition _ _ = 0 -- All other transitions invalid |
| 181 | + |
| 182 | +||| Check if an operation is advisory (C ABI: always returns 1) |
| 183 | +||| This is trivially true by allOperationsAdvisory, exported for FFI |
| 184 | +export |
| 185 | +llm_is_advisory : Int -> Int |
| 186 | +llm_is_advisory _ = 1 -- All operations advisory, proven above |
0 commit comments