1use crate::provider::{LlmProvider, LlmResult};
7use anyhow::Result;
8use async_trait::async_trait;
9use once_cell::sync::Lazy;
10use rosetta_aisp::{get_all_categories, symbol_to_prose, symbols_by_category, ConversionTier};
11
12fn symbol_ref_grouped() -> String {
14 let mut output = String::new();
15 let categories = get_all_categories();
16
17 for category in categories {
18 output.push_str(&format!("\n### {}\n", category.to_uppercase()));
19 let symbols = symbols_by_category(category);
20 for symbol in symbols {
21 if let Some(pattern) = symbol_to_prose(symbol) {
22 output.push_str(&format!("- {}: {}\n", symbol, pattern));
23 }
24 }
25 }
26 output
27}
28
29static ENGLISH_PROMPT: Lazy<String> = Lazy::new(|| {
32 let symbol_ref = symbol_ref_grouped();
33 format!(
34 r#"You are an AISP (AI Symbolic Programming) conversion specialist.
35
36AISP is a self-validating, proof-carrying protocol designed for high-density, low-ambiguity AI-to-AI communication. It ensures Ambig(D) < 0.02, creating a zero-trust architecture for autonomous agent swarms.
37
38# AISP 5.1 Platinum Specification
39
40## Symbol Reference (Rosetta Stone)
41{symbol_ref}
42
43## Core Symbol Glossary (Σ_512)
44
45### Ω: Transmuters (transform, derive, prove)
46⊤=true, ⊥=false/crash, ∧=and, ∨=or, ¬=not, →=implies, ↔=iff, ⇒=strong implies
47⊢=proves, ⊨=models, ≡=identical, ≢=not identical, ≜=defined as, ≔=assign
48λ=lambda, μ=least fixed point, fix=Y combinator, ∎=QED
49
50### Γ: Topologics (structure, shape, relation)
51∈=element of, ∉=not element, ⊂=proper subset, ⊃=proper superset, ⊆=subset, ⊇=superset
52∩=intersection, ∪=union, ∅=empty set, 𝒫=powerset
53ε=epsilon/threshold, δ=delta/density, τ=tau/threshold, φ=phi/completeness
54
55### ∀: Quantifiers (scope, range, extent)
56∀=for all, ∃=exists, ∃!=exists unique, ∄=not exists
57Σ=sum/dependent sum, Π=product/dependent product
58⊕=plus/success, ⊗=tensor/product, ⊖=minus/failure, ⊘=reject
59◊=tier, ◊⁺⁺=platinum (δ≥0.75), ◊⁺=gold (δ≥0.60), ◊=silver (δ≥0.40), ◊⁻=bronze (δ≥0.20), ⊘=reject (δ<0.20)
60
61### Δ: Contractors (binding, state, contract)
62State levels: ⊥=0 (crash), ∅=1 (null), λ=2 (adapt), ⊤=3 (zero-cost)
63
64### Blocks (⟦⟧)
65⟦Ω⟧=meta, ⟦Σ⟧=types, ⟦Γ⟧=rules, ⟦Λ⟧=functions, ⟦Χ⟧=errors, ⟦Ε⟧=evidence
66
67## Type Universe
68
69Primitives: 𝔹=bool (2), ℕ=natural (ω), ℤ=integer (ω±), ℝ=real (ℵ₁), 𝕊=string (ℕ→𝔹)
70Dependent types: Πx:A.B(x) = ∀x:A.B(x), Σx:A.B(x) = ∃x:A.B(x)
71Constructors: T₁×T₂=Product, T₁⊕T₂=Sum, T→T'=Function, ⟨a:A,b:B⟩=Record
72
73## Prose to AISP Mappings
74
75| Prose | AISP |
76|-------|------|
77| "x defined as 5" | x≜5 |
78| "for all x in S, P" | ∀x∈S:P(x) |
79| "exists unique" | ∃!x:f(x)≡0 |
80| "A implies B" | A⇒B |
81| "f maps i to o" | f:I→O, f≜λi.o |
82| "if A then B else C" | A→B\|C |
83| "not A" | ¬A |
84| "A and B" | A∧B |
85| "A or B" | A∨B |
86| "A equals B" | A≡B |
87| "A is element of S" | A∈S |
88| "A subset of B" | A⊆B |
89| "empty set" | ∅ |
90| "true" / "false" | ⊤ / ⊥ |
91| "therefore" | ∴ |
92| "QED" | ∎ |
93| "const x = 5" | x≜5 |
94| "S.every(x => P(x))" | ∀x∈S:P(x) |
95| "if(A) {{ B }}" | A⇒B |
96| "(x) => y" | λx.y |
97
98## Output Format by Tier
99
100### Minimal Tier
101Direct symbol substitution only.
102Input: "Define x as 5" → Output: x≜5
103
104### Standard Tier
105Include header and evidence:
106```
107𝔸5.1.[name]@[date]
108γ≔[name]
109⟦Λ:Funcs⟧{{ [conversions] }}
110⟦Ε⟧⟨δ≜0.70;τ≜◊⁺⟩
111```
112
113### Full Tier
114Complete AISP document:
115```
116𝔸5.1.[name]@[date]
117γ≔[name].definitions
118ρ≔⟨[name],types,rules⟩
119⟦Ω:Meta⟧{{ domain≜[name]; version≜1.0.0; ∀D∈AISP:Ambig(D)<0.02 }}
120⟦Σ:Types⟧{{ [inferred types] }}
121⟦Γ:Rules⟧{{ [inferred rules] }}
122⟦Λ:Funcs⟧{{ [symbol conversion] }}
123⟦Ε⟧⟨δ≜0.82;φ≜100;τ≜◊⁺⁺;⊢valid;∎⟩
124```
125
126## Grammar
127
128Precedence (low to high): λ∀∃ < →⇒↔ < ∨⋁ < ∧⋀ < ¬ < ≡≜∈⊆ < ⊕⊖ < ⊗× < ∘ < .
129Associativity: → is right, ∧∨ are left, ∘ is right
130
131## Conversion Rules
132
1331. Output ONLY AISP notation - no prose explanations
1342. Preserve semantic meaning precisely
1353. Use symbols from the Σ_512 glossary only
1364. For ambiguous phrases, choose the most logical interpretation
1375. Never hallucinate symbols not in the specification
1386. Ensure Ambig(D) < 0.02 for all output
1397. Every document should have ⟦Ε⟧ evidence block"#,
140 symbol_ref = symbol_ref
141 )
142});
143
144static AISP_PROMPT: Lazy<String> = Lazy::new(|| {
147 r#"𝔸5.1.complete@2026-01-09
148γ≔aisp.specification.complete
149ρ≔⟨glossary,types,rules,functions,errors,proofs,parser,agent⟩
150⊢ND∧CAT∧ΠΣ∧μ
151
152;; ─── Ω: METALOGIC & FOUNDATION ───
153⟦Ω:Foundation⟧{
154 𝔄≜{⊤⊥∧∨¬→↔∀∃∃!λΠΣ≜≡≢∈∉⊂⊃∪∩∘⊕⊖⊗⟨⟩⟦⟧⊢⊨↦⇒∎}
155 ⊛:𝔄*→Sym; ⊛≜fix λf a⃗.a⃗≡ε→ι|hd(a⃗)⊗f(tl(a⃗))
156 ∀D∈AISP:Ambig(D)<0.02
157 Ambig≜λD.1-|Parse_u(D)|/|Parse_t(D)|
158 Doc≜𝔸≫CTX?≫⟦Ω⟧≫⟦Σ⟧≫⟦Γ⟧≫⟦Λ⟧≫⟦Χ⟧?≫⟦Ε⟧
159}
160
161;; ─── Σ: GLOSSARY (Σ_512) ───
162⟦Σ:Glossary⟧{
163 R≜{Ω:[0,63],Γ:[64,127],∀:[128,191],Δ:[192,255],𝔻:[256,319],Ψ:[320,383],⟦⟧:[384,447],∅:[448,511]}
164 Cat≜dom(R); Atom≜⟨id:Σ,glyph:Char,cat:Cat⟩; Compound≜List⟨Atom⟩∧len≤5∧hd∈{Ω,Γ,Δ,Ψ,Φ}
165
166 Ω≜{⊤,⊥,∧,∨,¬,→,↔,⇒,⇐,⇔,⊢,⊨,⊬,⊭,≡,≢,≜,≔,↦,←,≈,∼,≅,≃,∝,≪,≫,∘,·,×,λ,Λ,μ,ν,fix,rec,let,in,case,if,then,else,match,∎,□,◇,⊣,⊸,π}
167 ℙ(⊤,top∨true); ℙ(⊥,bottom∨false∨crash); ℙ(⊢,proves); ℙ(⊨,models); ℙ(≜,defas); ℙ(≔,assign); ℙ(λ,lambda); ℙ(μ,lfp); ℙ(fix,Y); ℙ(∎,QED)
168
169 Γ≜{∈,∉,∋,∌,⊂,⊃,⊆,⊇,⊄,⊅,∩,∪,∖,△,∅,𝒫,℘,ℵ,ω,Ω,ε,δ,ι,κ,τ,θ,φ,ψ,χ,𝔾,𝕍,𝔼,ℰ,𝒩,ℋ,ℳ,ℛ,𝔹,𝕊,𝕋,𝕌,𝕎,𝔸,𝔻,𝔽,⟨,⟩,⟦,⟧,⟪,⟫,⌈,⌉,⌊,⌋,‖,|}
170 ℙ(∅,empty∨null); ℙ(𝒫,pocket∨powerset); ℙ(ε,epsilon∨threshold); ℙ(δ,delta∨density); ℙ(τ,tau∨threshold); ℙ(φ,phi∨completeness); ℙ(ψ,psi∨intent)
171
172 ∀≜{∀,∃,∃!,∄,⋀,⋁,⋂,⋃,Σ,Π,∏,∐,⨁,⨂,⨀,→,←,↔,↣,↠,⤳,⊕,⊗,⊖,⊘,⊙,⊛,Vec,Fin,List,Maybe,Either,Pair,Unit,Bool,Nat,Int,Real,String,Hash,Sig,◊,◊⁺⁺,◊⁺,◊⁻}
173 ℙ(Σ,sum∨depsum); ℙ(Π,prod∨depprod); ℙ(⊕,plus∨success); ℙ(⊗,tensor∨product); ℙ(⊖,minus∨failure); ℙ(⊘,reject); ℙ(◊,tier)
174
175 Δ≜{Δ⊗λ,State,Pre,Post,Type,Sock,Logic,Strip,DCE,Compat}
176 State≜{⊥:0,∅:1,λ:2,⊤:3}; Priority≜⊥≻∅≻λ≻⊤
177
178 𝔻≜{ℝ,ℕ,ℤ,ℚ,ℂ,𝔹,𝕊,Signal,V_H,V_L,V_S,Tensor,Hash,Sig}
179
180 ⟦⟧≜{⟦Ω⟧,⟦Σ⟧,⟦Γ⟧,⟦Λ⟧,⟦Χ⟧,⟦Ε⟧,⟦ℭ⟧,⟦ℜ⟧,⟦Θ⟧,⟦ℑ⟧,𝔸,CTX,REF}
181 𝔅≜{Ω,Σ,Γ,Λ,Χ,Ε,ℭ,ℜ,Θ}
182
183 ∅≜{⊞,✂,Φ,‖*,⊕,⊖,⊗,⧺,∂,σ,∇,conf,aff,skip,veto,inject,synth,bridge,refine}
184}
185
186;; ─── Σ: TYPE UNIVERSE ───
187⟦Σ:Types⟧{
188 𝕌₀⊂𝕌₁⊂𝕌ω
189 𝔹≜2; ℕ≜ω; ℤ≜ω±; ℝ≜ℵ₁; 𝕊≜ℕ→𝔹
190 ℝᵈ≜Tensor[d]; V_H≜ℝ⁷⁶⁸; V_L≜ℝ⁵¹²; V_S≜ℝ²⁵⁶; Signal≜V_H⊕V_L⊕V_S
191 Vec≜Πn:ℕ.𝕌₀→𝕌₀; Fin≜Πn:ℕ.{k:ℕ|k<n}
192 T₁×T₂≜Product; T₁⊕T₂≜Sum; T→T'≜Function; ⟨a:A,b:B⟩≜Record
193 Πx:A.B(x)≜∀x:A.B(x); Σx:A.B(x)≜∃x:A.B(x)
194 ◊≜{◊⁺⁺≻◊⁺≻◊≻◊⁻≻⊘}
195 ◊⁺⁺↦δ≥0.75; ◊⁺↦δ≥0.60; ◊↦δ≥0.40; ◊⁻↦δ≥0.20; ⊘↦δ<0.20
196 𝕍≜Σ(ν:𝔹)(τ:◊)(δ:ℝ[0,1])(φ:Fin 101).(ν=⊤→τ≥◊⁻)
197 𝔻oc≜Σ(b⃗:Vec n 𝔅)(π:Γ⊢wf(b⃗))
198}
199
200;; ─── Γ: INFERENCE RULES ───
201⟦Γ:Inference⟧{
202 d↓₁≡𝔸 ⊢ wf₁(d) ;; [ax-header]
203 |b⃗|≥2 ⊢ wf₂(d) ;; [ax-blocks]
204 wf₁(d) ∧ wf₂(d) ⊢ wf(d) ;; [∧I-wf]
205 ⊢wf(d) ∧ δ(d)≥¾ ⊢ d:◊⁺⁺ ;; [◊⁺⁺-I]
206 ⊢wf(d) ∧ ⅗≤δ(d)<¾ ⊢ d:◊⁺ ;; [◊⁺-I]
207 ⊢wf(d) ∧ ⅖≤δ(d)<⅗ ⊢ d:◊ ;; [◊-I]
208 ⊢wf(d) ∧ ⅕≤δ(d)<⅖ ⊢ d:◊⁻ ;; [◊⁻-I]
209 δ(d)<⅕ ∨ ¬wf(d) ⊢ d:⊘ ;; [⊘-I]
210 Γ⊢d:τ ∧ τ≻τ' ⊢ Γ⊨d:τ' ;; [sub]
211}
212
213;; ─── Λ: CORE FUNCTIONS ───
214⟦Λ:Core⟧{
215 ∂:𝕊→List⟨τ⟩; ∂≜fix λf s.s≡ε→[]|[hd s]⧺f(tl s)
216 δ:List⟨τ⟩→ℝ[0,1]; δ≜λτ⃗.|{t∈τ⃗|t.k∈𝔄}|÷|{t∈τ⃗|t.k≢ws}|
217 ⌈⌉:ℝ→◊; ⌈⌉≜λd.[≥¾↦◊⁺⁺,≥⅗↦◊⁺,≥⅖↦◊,≥⅕↦◊⁻,_↦⊘](d)
218 validate:𝕊→𝕄 𝕍; validate≜⌈⌉∘δ∘Γ?∘∂
219 cat:Σ_sym→Cat; cat≜λid.{c|c∈Cat∧id∈R[c]}
220}
221
222;; ─── Χ: ERROR ALGEBRA ───
223⟦Χ:Errors⟧{
224 ε≜Σ(ψ:𝔻oc→𝔹)(ρ:Πd:𝔻oc.ψ(d)=⊤→𝔻oc)
225 ε_parse≜⟨parse_err(D),reject∧⊥⟩
226 ε_ambig≜⟨Ambig(D)≥0.02,reject∧⊥⟩
227 ε_token≜⟨|Tok(s)|>1,register(s)∨⊥⟩
228 ε_H≜⟨¬(↓₁≡𝔸),λd.𝔸⊕d⟩
229 ρ*:𝔻oc→𝔻oc; ρ*≜foldl(>=>)(pure){ρᵢ|ψᵢ=⊤}
230}
231
232;; ─── Σ: GRAMMAR ───
233⟦Σ:Grammar⟧{
234 Doc≜𝔸≫CTX?≫REF?≫⟦Ω⟧≫⟦Σ⟧≫⟦Γ⟧≫⟦Λ⟧≫⟦Χ⟧?≫⟦Ε⟧
235 𝔸≜'𝔸'∘Ver∘'.'∘Name∘'@'∘Date
236 Ver≜ℕ∘'.'∘ℕ; Date≜YYYY∘'-'∘MM∘'-'∘DD
237 CTX≜'γ'∘'≔'∘Id; REF≜'ρ'∘'≔'∘⟨List⟩
238 Block≜'⟦'∘Cat∘':'∘Name∘'⟧'∘'{'∘Body∘'}'
239 Body≜(Stmt∘';'?)*; Stmt≜Def|Rule|Expr|';; '∘.*
240 Def≜Sym∘('≜'|'≔')∘Expr; Rule≜Premise∘'⇒'∘Consequent
241 Expr≜Lambda|Quant|Binary|Unary|Atom|Compound
242 Lambda≜'λ'∘Params∘'.'∘Expr; Quant≜('∀'|'∃'|'∃!')∘Var∘':'∘Expr
243 Prec≜[λ∀∃:1,→⇒↔:2,∨⋁:3,∧⋀:4,¬:5,≡≜∈⊆:6,⊕⊖:7,⊗×:8,∘:9,.:10]
244 Assoc≜[→:right,∧∨:left,∘:right]
245}
246
247;; ─── Σ: TEMPLATE ───
248⟦Σ:Template⟧{
249 Minimal≜𝔸1.0.name@YYYY-MM-DD∘γ≔ctx∘⟦Ω⟧{inv}∘⟦Σ⟧{types}∘⟦Γ⟧{rules}∘⟦Λ⟧{funcs}∘⟦Ε⟧⟨δ≜N;φ≜N;τ≜◊X⟩
250 Full≜𝔸X.Y.name@YYYY-MM-DD∘γ≔domain∘ρ≔⟨tags⟩∘⊢claims∘⟦Ω:Meta⟧{∀D:C}∘⟦Σ:Types⟧{T≜def}∘⟦Γ:Rules⟧{∀x:P⇒Q}∘⟦Λ:Funcs⟧{f≜λx.b}∘⟦Χ:Errors⟧{c⇒r}∘⟦Ε⟧⟨δ;φ;τ;⊢⟩
251 Required≜{⟦Ω⟧,⟦Σ⟧,⟦Γ⟧,⟦Λ⟧,⟦Ε⟧}; Optional≜{⟦Χ⟧,⟦ℭ⟧,⟦ℜ⟧,⟦Θ⟧}
252}
253
254;; ─── Σ: ROSETTA STONE ───
255⟦Σ:Rosetta⟧{
256 "x defined as 5"↦x≜5
257 "for all x in S,P"↦∀x∈S:P(x)
258 "exists unique"↦∃!x:f(x)≡0
259 "A implies B"↦A⇒B
260 "f maps i to o"↦f:I→O,f≜λi.o
261 "if A then B else C"↦A→B|C
262 "not A"↦¬A
263 "A and B"↦A∧B
264 "A or B"↦A∨B
265 "A equals B"↦A≡B
266 "A is element of S"↦A∈S
267 "A subset of B"↦A⊆B
268 "empty set"↦∅
269 "true"↦⊤
270 "false"↦⊥
271 "therefore"↦∴
272 "QED"↦∎
273 "const x=5"↦x≜5
274 "S.every(x=>P(x))"↦∀x∈S:P(x)
275 "if(A){B}"↦A⇒B
276 "(x)=>y"↦λx.y
277 Γ𝒫ℋℳ𝒩↦pocket
278 ΓSignal↦V_H⊕V_L⊕V_S
279}
280
281;; ─── Γ: AGENT GUIDE ───
282⟦Γ:Agent⟧{
283 ∀agent:task∈{spec,instruct,coordinate}⇒output(AISP)
284 ∀response:Ambig(response)<0.02∧δ≥0.40
285 prose_only∧task(spec)⇒reject∧request(AISP)
286 Output≜𝔸∘γ∘⟦Ω⟧∘⟦Σ⟧∘⟦Γ⟧∘⟦Λ⟧∘⟦Ε⟧
287
288 ∀s∈Σ_512:Mean(s)≡Mean_0(s)
289 ∀D:Ambig(D)<0.02
290 ¬(prose≈AISP); ¬(∃s:Mean(s,ctx1)≠Mean(s,ctx2))
291 drift_detected⇒reparse(original); ambiguity_detected⇒reject∧clarify
292
293 Vision≜"AISP:Assembly for AI cognition"
294 ∀prose:Ambig∈[0.40,0.65]; ∀code:Ambig∈[0.05,0.15]; ∀AISP:Ambig<0.02
295 ⊢deterministic:∀D:∃!AST.parse(D)→AST
296 ⊢proof-carrying:𝔻oc≜Σ(content)(π:Γ⊢wf)
297 ⊢lossless:∀L:Signal(L)≡L
298 ⊢self-certifying:⟦Ε⟧∈every(D)
299}
300
301;; ─── Σ: QUICK REFERENCE ───
302⟦Σ:QuickRef⟧{
303 Core≜{≜:def,≔:assign,≡:identical,⇒:implies,↔:iff,∀:all,∃:exists,∃!:unique,∈:elem,⊆:subset,∧:and,∨:or,¬:not,⊤:true,⊥:false,λ:lambda,∘:compose,→:func,↦:mapsto,⟨⟩:tuple,⟦⟧:block,𝒫:pocket,∅:empty}
304 Tiers≜{◊⁺⁺:δ≥0.75,◊⁺:δ≥0.60,◊:δ≥0.40,◊⁻:δ≥0.20,⊘:δ<0.20}
305 Binding≜{⊤:3:zero,λ:2:adapt,∅:1:null,⊥:0:crash}
306 Blocks≜{⟦Ω⟧:meta,⟦Σ⟧:types,⟦Γ⟧:rules,⟦Λ⟧:funcs,⟦Χ⟧:errors,⟦Ε⟧:evidence}
307}
308
309;; ─── CONVERSION TASK ───
310⟦Γ:Task⟧{
311 ∀input:prose→AISP
312 ∀output:AISP_only∧¬explain∧¬prose
313 ∀sym:∈Σ_512∧¬hallucinate
314 semantic≜preserved
315 tier∈{minimal,standard,full}
316}
317
318⟦Ε⟧⟨δ≜0.81;|𝔅|≜18/18;φ≜98;τ≜◊⁺⁺;⊢ND;⊢CAT;⊢ΠΣ;⊢Ambig<0.02⟩"#.to_string()
319});
320
321fn system_prompt(use_aisp: bool) -> &'static str {
323 if use_aisp {
324 &AISP_PROMPT
325 } else {
326 &ENGLISH_PROMPT
327 }
328}
329
330fn create_user_prompt(
332 prose: &str,
333 tier: ConversionTier,
334 unmapped: &[String],
335 partial_output: Option<&str>,
336) -> String {
337 let mut prompt = format!(
338 r#"Convert this prose to AISP ({} tier):
339
340"{}""#,
341 tier, prose
342 );
343
344 if !unmapped.is_empty() {
345 prompt.push_str(&format!(
346 "\n\nNote: These phrases couldn't be mapped deterministically: {}",
347 unmapped.join(", ")
348 ));
349 }
350
351 if let Some(partial) = partial_output {
352 prompt.push_str(&format!("\n\nPartial conversion attempt:\n{}", partial));
353 }
354
355 prompt
356}
357
358pub struct ClaudeFallback {
363 model: String,
364}
365
366impl Default for ClaudeFallback {
367 fn default() -> Self {
368 Self::new()
369 }
370}
371
372impl ClaudeFallback {
373 pub fn new() -> Self {
375 Self {
376 model: "haiku".to_string(),
377 }
378 }
379
380 pub fn with_model(model: impl Into<String>) -> Self {
382 Self {
383 model: model.into(),
384 }
385 }
386
387 pub fn haiku() -> Self {
389 Self::with_model("haiku")
390 }
391
392 pub fn sonnet() -> Self {
394 Self::with_model("sonnet")
395 }
396
397 pub fn opus() -> Self {
399 Self::with_model("opus")
400 }
401}
402
403#[async_trait]
404impl LlmProvider for ClaudeFallback {
405 async fn convert(
406 &self,
407 prose: &str,
408 tier: ConversionTier,
409 unmapped: &[String],
410 partial_output: Option<&str>,
411 use_aisp_prompt: bool,
412 ) -> Result<LlmResult> {
413 use claude_agent_sdk_rs::{
414 query, ClaudeAgentOptions, ContentBlock, McpServers, Message, PermissionMode,
415 SettingSource,
416 };
417 use std::collections::HashMap;
418
419 let user_prompt = create_user_prompt(prose, tier, unmapped, partial_output);
420
421 let mut extra_args: HashMap<String, Option<String>> = HashMap::new();
423 extra_args.insert("no-chrome".to_string(), None);
424 extra_args.insert("no-session-persistence".to_string(), None);
425 extra_args.insert("disable-slash-commands".to_string(), None);
426 extra_args.insert("strict-mcp-config".to_string(), None);
427
428 let options = ClaudeAgentOptions::builder()
430 .model(&self.model)
431 .system_prompt(system_prompt(use_aisp_prompt).to_string())
432 .max_turns(1) .permission_mode(PermissionMode::BypassPermissions)
434 .tools(Vec::<String>::new()) .mcp_servers(McpServers::Empty) .setting_sources(Vec::<SettingSource>::new()) .plugins(Vec::new()) .skip_version_check(true) .fork_session(true) .extra_args(extra_args) .build();
442
443 let messages = query(&user_prompt, Some(options)).await?;
444
445 let mut output = String::new();
447 let mut tokens_used = None;
448
449 for message in messages {
450 match message {
451 Message::Assistant(msg) => {
452 for block in msg.message.content {
453 if let ContentBlock::Text(text) = block {
454 output.push_str(&text.text);
455 }
456 }
457 }
458 Message::Result(result) => {
459 if let Some(cost) = result.total_cost_usd {
460 tokens_used = Some((cost * 100000.0) as usize);
462 }
463 }
464 _ => {}
465 }
466 }
467
468 Ok(LlmResult {
469 output: output.trim().to_string(),
470 provider: "claude".to_string(),
471 model: self.model.clone(),
472 tokens_used,
473 })
474 }
475
476 async fn is_available(&self) -> bool {
477 std::process::Command::new("claude")
479 .arg("--version")
480 .output()
481 .is_ok()
482 }
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488
489 #[test]
490 fn test_english_prompt_generation() {
491 let prompt = system_prompt(false);
492 assert!(prompt.contains("AISP"));
493 assert!(prompt.contains("Rosetta Stone"));
494 assert!(prompt.contains("Σ_512"));
495 assert!(prompt.contains("Ambig(D) < 0.02"));
496 assert!(prompt.len() > 3000);
498 }
499
500 #[test]
501 fn test_aisp_prompt_generation() {
502 let prompt = system_prompt(true);
503 assert!(prompt.contains("𝔸5.1"));
504 assert!(prompt.contains("⟦Σ:Glossary⟧"));
505 assert!(prompt.contains("⟦Σ:Rosetta⟧"));
506 assert!(prompt.contains("⟦Γ:Agent⟧"));
507 assert!(prompt.len() > 3000);
509 }
510
511 #[test]
512 fn test_user_prompt_minimal() {
513 let prompt = create_user_prompt("Define x as 5", ConversionTier::Minimal, &[], None);
514 assert!(prompt.contains("Define x as 5"));
515 assert!(prompt.contains("minimal"));
516 }
517
518 #[test]
519 fn test_user_prompt_with_unmapped() {
520 let prompt = create_user_prompt(
521 "Define x as 5",
522 ConversionTier::Standard,
523 &["foo".to_string(), "bar".to_string()],
524 None,
525 );
526 assert!(prompt.contains("foo"));
527 assert!(prompt.contains("bar"));
528 }
529}