Skip to main content

sage_codegen/
generator.rs

1//! Main code generator.
2
3use crate::emit::Emitter;
4use sage_loader::{ModuleTree, SupervisionConfig};
5use sage_parser::{
6    AgentDecl, BinOp, Block, ConstDecl, EffectHandlerDecl, EnumDecl, EventKind, Expr, ExternFnDecl,
7    FnDecl, Ident, Literal, MockValue, Program, ProtocolDecl, RecordDecl, RestartPolicy, Stmt,
8    StringPart, SupervisionStrategy, SupervisorDecl, TestDecl, TypeExpr, UnaryOp,
9};
10
11/// How to specify the sage-runtime dependency in generated Cargo.toml.
12#[derive(Debug, Clone)]
13pub enum RuntimeDep {
14    /// Use the published crates.io version.
15    CratesIo { version: String },
16    /// Use a local path (for development).
17    Path { path: String },
18}
19
20impl Default for RuntimeDep {
21    fn default() -> Self {
22        // Default to crates.io with the current version
23        Self::CratesIo {
24            version: env!("CARGO_PKG_VERSION").to_string(),
25        }
26    }
27}
28
29impl RuntimeDep {
30    /// Generate the Cargo.toml dependency line.
31    fn to_cargo_dep(&self, feature: Option<&str>) -> String {
32        match self {
33            RuntimeDep::CratesIo { version } => {
34                if let Some(feat) = feature {
35                    format!("sage-runtime = {{ version = \"{version}\", features = [\"{feat}\"] }}")
36                } else {
37                    format!("sage-runtime = \"{version}\"")
38                }
39            }
40            RuntimeDep::Path { path } => {
41                if let Some(feat) = feature {
42                    format!("sage-runtime = {{ path = \"{path}\", features = [\"{feat}\"] }}")
43                } else {
44                    format!("sage-runtime = {{ path = \"{path}\" }}")
45                }
46            }
47        }
48    }
49}
50
51/// Persistence backend configuration for @persistent fields.
52#[derive(Debug, Clone, Default)]
53pub enum PersistenceBackend {
54    /// In-memory storage (no persistence across restarts).
55    #[default]
56    Memory,
57    /// SQLite database storage.
58    Sqlite { path: String },
59    /// PostgreSQL database storage.
60    Postgres { url: String },
61    /// File-based JSON storage.
62    File { path: String },
63}
64
65impl PersistenceBackend {
66    /// Get the feature flag name for this backend, if any.
67    fn feature_flag(&self) -> Option<&'static str> {
68        match self {
69            PersistenceBackend::Memory => None,
70            PersistenceBackend::Sqlite { .. } => Some("persistence-sqlite"),
71            PersistenceBackend::Postgres { .. } => Some("persistence-postgres"),
72            PersistenceBackend::File { .. } => Some("persistence-file"),
73        }
74    }
75}
76
77/// Observability configuration for tracing and metrics.
78#[derive(Debug, Clone, Default)]
79pub struct ObservabilityConfig {
80    /// Backend type: "ndjson", "otlp", or "none".
81    pub backend: String,
82    /// OTLP endpoint URL (for otlp backend).
83    pub otlp_endpoint: Option<String>,
84    /// Service name for trace attribution.
85    pub service_name: String,
86}
87
88/// Target platform for code generation.
89#[derive(Debug, Clone, Default, PartialEq, Eq)]
90pub enum CodegenTarget {
91    /// Native binary (uses tokio, signal handlers, etc.)
92    #[default]
93    Native,
94    /// WebAssembly (uses wasm-bindgen, spawn_local, no signals)
95    Wasm,
96}
97
98/// Full configuration for code generation.
99#[derive(Debug, Clone, Default)]
100pub struct CodegenConfig {
101    /// How to specify the sage-runtime dependency.
102    pub runtime_dep: RuntimeDep,
103    /// Persistence backend configuration.
104    pub persistence: PersistenceBackend,
105    /// Supervision configuration (restart intensity limits).
106    pub supervision: SupervisionConfig,
107    /// Observability configuration for tracing.
108    pub observability: ObservabilityConfig,
109    /// Target platform (native or WASM).
110    pub target: CodegenTarget,
111}
112
113/// Generated Rust project files.
114pub struct GeneratedProject {
115    /// The main.rs content.
116    pub main_rs: String,
117    /// The Cargo.toml content.
118    pub cargo_toml: String,
119}
120
121/// Generate Rust code from a Sage program (single file).
122pub fn generate(program: &Program, project_name: &str) -> GeneratedProject {
123    generate_with_config(program, project_name, RuntimeDep::default())
124}
125
126/// Generate Rust code from a Sage program with custom runtime dependency.
127pub fn generate_with_config(
128    program: &Program,
129    project_name: &str,
130    runtime_dep: RuntimeDep,
131) -> GeneratedProject {
132    generate_with_full_config(
133        program,
134        project_name,
135        CodegenConfig {
136            runtime_dep,
137            persistence: PersistenceBackend::Memory,
138            supervision: SupervisionConfig::default(),
139            observability: ObservabilityConfig::default(),
140            ..Default::default()
141        },
142    )
143}
144
145/// Generate Rust code from a Sage program with full configuration.
146pub fn generate_with_full_config(
147    program: &Program,
148    project_name: &str,
149    config: CodegenConfig,
150) -> GeneratedProject {
151    let mut gen = Generator::new(config);
152    let main_rs = gen.generate_program(program);
153    let needs_persistence = Generator::has_persistent_fields(program);
154    let cargo_toml = gen.generate_cargo_toml_with_persistence(project_name, needs_persistence);
155    GeneratedProject {
156        main_rs,
157        cargo_toml,
158    }
159}
160
161/// Generate Rust code from a module tree (multi-file project).
162///
163/// This flattens all modules into a single Rust file, generating all agents
164/// and functions with appropriate visibility modifiers.
165pub fn generate_module_tree(tree: &ModuleTree, project_name: &str) -> GeneratedProject {
166    generate_module_tree_with_config(tree, project_name, RuntimeDep::default())
167}
168
169/// Generate Rust code from a module tree with custom runtime dependency.
170pub fn generate_module_tree_with_config(
171    tree: &ModuleTree,
172    project_name: &str,
173    runtime_dep: RuntimeDep,
174) -> GeneratedProject {
175    generate_module_tree_with_full_config(
176        tree,
177        project_name,
178        CodegenConfig {
179            runtime_dep,
180            persistence: PersistenceBackend::Memory,
181            supervision: SupervisionConfig::default(),
182            observability: ObservabilityConfig::default(),
183            ..Default::default()
184        },
185    )
186}
187
188/// Generate Rust code from a module tree with full configuration.
189pub fn generate_module_tree_with_full_config(
190    tree: &ModuleTree,
191    project_name: &str,
192    config: CodegenConfig,
193) -> GeneratedProject {
194    let mut gen = Generator::new(config);
195    let main_rs = gen.generate_module_tree(tree);
196    let needs_persistence = Generator::has_persistent_fields_in_tree(tree);
197    let cargo_toml = gen.generate_cargo_toml_with_persistence(project_name, needs_persistence);
198    GeneratedProject {
199        main_rs,
200        cargo_toml,
201    }
202}
203
204/// Generated test project files (RFC-0012).
205pub struct GeneratedTestProject {
206    /// The test main.rs content.
207    pub main_rs: String,
208    /// The Cargo.toml content.
209    pub cargo_toml: String,
210}
211
212/// Generate a test binary from a Sage test file (RFC-0012).
213pub fn generate_test_program(program: &Program, test_name: &str) -> GeneratedTestProject {
214    generate_test_program_with_config(program, test_name, RuntimeDep::default())
215}
216
217/// Generate a test binary with custom runtime dependency.
218pub fn generate_test_program_with_config(
219    program: &Program,
220    test_name: &str,
221    runtime_dep: RuntimeDep,
222) -> GeneratedTestProject {
223    let mut gen = Generator::with_runtime_dep(runtime_dep);
224    let main_rs = gen.generate_test_binary(program);
225    let cargo_toml = gen.generate_test_cargo_toml(test_name);
226    GeneratedTestProject {
227        main_rs,
228        cargo_toml,
229    }
230}
231
232/// Information about an agent's message handlers.
233#[derive(Clone)]
234struct AgentMessageHandlers {
235    /// List of (param_name, param_type) for each message handler.
236    handlers: Vec<(Ident, TypeExpr)>,
237}
238
239struct Generator {
240    emit: Emitter,
241    config: CodegenConfig,
242    /// Variables that are reassigned in the current scope
243    reassigned_vars: std::collections::HashSet<String>,
244    /// Agents that have on_error handlers
245    agents_with_error_handlers: std::collections::HashSet<String>,
246    /// Agents that have message handlers (agent_name -> handler info)
247    agents_with_message_handlers: std::collections::HashMap<String, AgentMessageHandlers>,
248    /// Phase 3: Current agent's protocol roles (protocol_name -> role_name)
249    current_protocol_roles: std::collections::HashMap<String, String>,
250    /// v2.0: Current agent's @persistent belief field names (for checkpoint())
251    current_agent_persistent_beliefs: Vec<String>,
252    /// Agent tool uses (agent_name -> list of tool names) for summon generation
253    agent_tool_uses: std::collections::HashMap<String, Vec<String>>,
254    /// Extern function names (for sage_extern:: dispatch)
255    extern_fn_names: std::collections::HashSet<String>,
256    /// Subset of extern fns that are fallible (marked with `fails`)
257    extern_fn_fallible: std::collections::HashSet<String>,
258    /// String constant names (need .to_string() when referenced)
259    string_consts: std::collections::HashSet<String>,
260}
261
262impl Generator {
263    fn new(config: CodegenConfig) -> Self {
264        Self {
265            emit: Emitter::new(),
266            config,
267            reassigned_vars: std::collections::HashSet::new(),
268            agents_with_error_handlers: std::collections::HashSet::new(),
269            agents_with_message_handlers: std::collections::HashMap::new(),
270            current_protocol_roles: std::collections::HashMap::new(),
271            current_agent_persistent_beliefs: Vec::new(),
272            agent_tool_uses: std::collections::HashMap::new(),
273            extern_fn_names: std::collections::HashSet::new(),
274            extern_fn_fallible: std::collections::HashSet::new(),
275            string_consts: std::collections::HashSet::new(),
276        }
277    }
278
279    /// Create a generator with only a runtime dependency (backwards compatible).
280    fn with_runtime_dep(runtime_dep: RuntimeDep) -> Self {
281        Self::new(CodegenConfig {
282            runtime_dep,
283            persistence: PersistenceBackend::Memory,
284            supervision: SupervisionConfig::default(),
285            observability: ObservabilityConfig::default(),
286            ..Default::default()
287        })
288    }
289
290    /// Collect extern function names and fallibility from declarations.
291    fn collect_extern_fns(&mut self, extern_fns: &[ExternFnDecl]) {
292        for ext_fn in extern_fns {
293            self.extern_fn_names.insert(ext_fn.name.name.clone());
294            if ext_fn.is_fallible {
295                self.extern_fn_fallible.insert(ext_fn.name.name.clone());
296            }
297        }
298    }
299
300    /// Emit the checkpoint store initialization based on configured backend.
301    fn emit_checkpoint_store_init(&mut self) {
302        match &self.config.persistence {
303            PersistenceBackend::Memory => {
304                self.emit
305                    .writeln("sage_runtime::persistence::MemoryCheckpointStore::new()");
306            }
307            PersistenceBackend::Sqlite { path } => {
308                self.emit
309                    .write("sage_runtime::persistence::SyncSqliteStore::open(\"");
310                self.emit.write(path);
311                self.emit
312                    .writeln("\").expect(\"Failed to open checkpoint database\")");
313            }
314            PersistenceBackend::Postgres { url } => {
315                self.emit
316                    .write("sage_runtime::persistence::SyncPostgresStore::connect(\"");
317                self.emit.write(url);
318                self.emit
319                    .writeln("\").expect(\"Failed to connect to checkpoint database\")");
320            }
321            PersistenceBackend::File { path } => {
322                self.emit
323                    .write("sage_runtime::persistence::SyncFileStore::open(\"");
324                self.emit.write(path);
325                self.emit
326                    .writeln("\").expect(\"Failed to open checkpoint directory\")");
327            }
328        }
329    }
330
331    /// Emit tracing initialization based on configured backend.
332    fn emit_tracing_init(&mut self) {
333        let obs = &self.config.observability;
334        if obs.backend.is_empty() || obs.backend == "ndjson" {
335            // Default: use environment-based init for NDJSON
336            self.emit.writeln("sage_runtime::trace::init();");
337        } else {
338            // Use config-based init
339            self.emit.writeln(
340                "sage_runtime::trace::init_with_config(sage_runtime::trace::TracingConfig {",
341            );
342            self.emit.indent();
343            self.emit.write("backend: \"");
344            self.emit.write(&obs.backend);
345            self.emit.writeln("\".to_string(),");
346            if let Some(endpoint) = &obs.otlp_endpoint {
347                self.emit.write("otlp_endpoint: Some(\"");
348                self.emit.write(endpoint);
349                self.emit.writeln("\".to_string()),");
350            } else {
351                self.emit.writeln("otlp_endpoint: None,");
352            }
353            self.emit.write("service_name: \"");
354            self.emit.write(if obs.service_name.is_empty() {
355                "sage-agent"
356            } else {
357                &obs.service_name
358            });
359            self.emit.writeln("\".to_string(),");
360            self.emit.dedent();
361            self.emit.writeln("});");
362        }
363    }
364
365    /// Scan a block to find all variables that are reassigned (Stmt::Assign)
366    fn collect_reassigned_vars(&mut self, block: &Block) {
367        for stmt in &block.stmts {
368            self.collect_reassigned_vars_stmt(stmt);
369        }
370    }
371
372    fn collect_reassigned_vars_stmt(&mut self, stmt: &Stmt) {
373        match stmt {
374            Stmt::Assign { name, .. } => {
375                self.reassigned_vars.insert(name.name.clone());
376            }
377            Stmt::If {
378                then_block,
379                else_block,
380                ..
381            } => {
382                self.collect_reassigned_vars(then_block);
383                if let Some(else_branch) = else_block {
384                    match else_branch {
385                        sage_parser::ElseBranch::Block(block) => {
386                            self.collect_reassigned_vars(block)
387                        }
388                        sage_parser::ElseBranch::ElseIf(stmt) => {
389                            self.collect_reassigned_vars_stmt(stmt)
390                        }
391                    }
392                }
393            }
394            Stmt::While { body, .. } | Stmt::Loop { body, .. } => {
395                self.collect_reassigned_vars(body);
396            }
397            Stmt::For { body, .. } => {
398                self.collect_reassigned_vars(body);
399            }
400            _ => {}
401        }
402    }
403
404    fn generate_program(&mut self, program: &Program) -> String {
405        // Collect extern function names before generating any code
406        self.collect_extern_fns(&program.extern_fns);
407
408        // Prelude
409        self.emit
410            .writeln("//! Generated by Sage compiler. Do not edit.");
411        self.emit.blank_line();
412        self.emit.writeln("use sage_runtime::prelude::*;");
413        if self.config.target == CodegenTarget::Wasm {
414            self.emit.writeln("use wasm_bindgen::prelude::*;");
415        }
416        self.emit.blank_line();
417
418        // Extern module declaration (implementation provided by user Rust code)
419        if !program.extern_fns.is_empty() {
420            self.emit.writeln("mod sage_extern;");
421            self.emit.blank_line();
422        }
423
424        // Constants
425        for const_decl in &program.consts {
426            self.generate_const(const_decl);
427            self.emit.blank_line();
428        }
429
430        // Enums
431        for enum_decl in &program.enums {
432            self.generate_enum(enum_decl);
433            self.emit.blank_line();
434        }
435
436        // Records
437        for record in &program.records {
438            self.generate_record(record);
439            self.emit.blank_line();
440        }
441
442        // Functions
443        for func in &program.functions {
444            self.generate_function(func);
445            self.emit.blank_line();
446        }
447
448        // Phase 3: Protocols (generate state machine modules)
449        for protocol in &program.protocols {
450            self.generate_protocol(protocol);
451            self.emit.blank_line();
452        }
453
454        // Phase 3: Effect handlers (generate InferConfig structs)
455        for handler in &program.effect_handlers {
456            self.generate_effect_handler(handler);
457            self.emit.blank_line();
458        }
459
460        // Pre-pass: Collect agent metadata for summon generation
461        for agent in &program.agents {
462            self.collect_agent_metadata(agent);
463        }
464
465        // Agents
466        for agent in &program.agents {
467            self.generate_agent(agent);
468            self.emit.blank_line();
469        }
470
471        // Supervisors
472        for supervisor in &program.supervisors {
473            self.generate_supervisor(supervisor, program);
474            self.emit.blank_line();
475        }
476
477        // Entry point (required for executables)
478        if let Some(run_entry) = &program.run_agent {
479            // First check if it's an agent
480            if let Some(agent) = program
481                .agents
482                .iter()
483                .find(|a| a.name.name == run_entry.name)
484            {
485                self.generate_main(agent);
486            } else if let Some(supervisor) = program
487                .supervisors
488                .iter()
489                .find(|s| s.name.name == run_entry.name)
490            {
491                // It's a supervisor entry point
492                self.generate_supervisor_main(supervisor, program);
493            }
494        }
495
496        std::mem::take(&mut self.emit).finish()
497    }
498
499    fn generate_module_tree(&mut self, tree: &ModuleTree) -> String {
500        // Pre-pass: Collect agent metadata and extern fns from all modules
501        let mut has_extern_fns = false;
502        for module in tree.modules.values() {
503            for agent in &module.program.agents {
504                self.collect_agent_metadata(agent);
505            }
506            if !module.program.extern_fns.is_empty() {
507                has_extern_fns = true;
508                self.collect_extern_fns(&module.program.extern_fns);
509            }
510        }
511
512        // Prelude
513        self.emit
514            .writeln("//! Generated by Sage compiler. Do not edit.");
515        self.emit.blank_line();
516        self.emit.writeln("use sage_runtime::prelude::*;");
517        if self.config.target == CodegenTarget::Wasm {
518            self.emit.writeln("use wasm_bindgen::prelude::*;");
519        }
520        self.emit.blank_line();
521
522        // Extern module declaration (implementation provided by user Rust code)
523        if has_extern_fns {
524            self.emit.writeln("mod sage_extern;");
525            self.emit.blank_line();
526        }
527
528        // Generate all modules, starting with the root
529        // We flatten everything into one file for simplicity
530        // (A more advanced implementation would generate mod.rs files)
531
532        // First, generate non-root modules
533        for (path, module) in &tree.modules {
534            if path != &tree.root {
535                self.emit.write("// Module: ");
536                if path.is_empty() {
537                    self.emit.writeln("(root)");
538                } else {
539                    self.emit.writeln(&path.join("::"));
540                }
541
542                for const_decl in &module.program.consts {
543                    self.generate_const(const_decl);
544                    self.emit.blank_line();
545                }
546
547                for enum_decl in &module.program.enums {
548                    self.generate_enum(enum_decl);
549                    self.emit.blank_line();
550                }
551
552                for record in &module.program.records {
553                    self.generate_record(record);
554                    self.emit.blank_line();
555                }
556
557                for func in &module.program.functions {
558                    self.generate_function(func);
559                    self.emit.blank_line();
560                }
561
562                // Phase 3: Protocols
563                for protocol in &module.program.protocols {
564                    self.generate_protocol(protocol);
565                    self.emit.blank_line();
566                }
567
568                // Phase 3: Effect handlers
569                for handler in &module.program.effect_handlers {
570                    self.generate_effect_handler(handler);
571                    self.emit.blank_line();
572                }
573
574                for agent in &module.program.agents {
575                    self.generate_agent(agent);
576                    self.emit.blank_line();
577                }
578
579                for supervisor in &module.program.supervisors {
580                    self.generate_supervisor(supervisor, &module.program);
581                    self.emit.blank_line();
582                }
583            }
584        }
585
586        // Then, generate the root module
587        if let Some(root_module) = tree.modules.get(&tree.root) {
588            self.emit.writeln("// Root module");
589
590            for const_decl in &root_module.program.consts {
591                self.generate_const(const_decl);
592                self.emit.blank_line();
593            }
594
595            for enum_decl in &root_module.program.enums {
596                self.generate_enum(enum_decl);
597                self.emit.blank_line();
598            }
599
600            for record in &root_module.program.records {
601                self.generate_record(record);
602                self.emit.blank_line();
603            }
604
605            for func in &root_module.program.functions {
606                self.generate_function(func);
607                self.emit.blank_line();
608            }
609
610            // Phase 3: Protocols
611            for protocol in &root_module.program.protocols {
612                self.generate_protocol(protocol);
613                self.emit.blank_line();
614            }
615
616            // Phase 3: Effect handlers
617            for handler in &root_module.program.effect_handlers {
618                self.generate_effect_handler(handler);
619                self.emit.blank_line();
620            }
621
622            for agent in &root_module.program.agents {
623                self.generate_agent(agent);
624                self.emit.blank_line();
625            }
626
627            for supervisor in &root_module.program.supervisors {
628                self.generate_supervisor(supervisor, &root_module.program);
629                self.emit.blank_line();
630            }
631
632            // Entry point (only in root module)
633            if let Some(run_entry) = &root_module.program.run_agent {
634                // First check if it's an agent
635                if let Some(agent) = root_module
636                    .program
637                    .agents
638                    .iter()
639                    .find(|a| a.name.name == run_entry.name)
640                {
641                    self.generate_main(agent);
642                } else if let Some(supervisor) = root_module
643                    .program
644                    .supervisors
645                    .iter()
646                    .find(|s| s.name.name == run_entry.name)
647                {
648                    // It's a supervisor entry point
649                    self.generate_supervisor_main(supervisor, &root_module.program);
650                }
651            }
652        }
653
654        std::mem::take(&mut self.emit).finish()
655    }
656
657    #[allow(dead_code)]
658    fn generate_cargo_toml(&self, name: &str) -> String {
659        self.generate_cargo_toml_impl(name, false)
660    }
661
662    fn generate_cargo_toml_with_persistence(&self, name: &str, needs_persistence: bool) -> String {
663        self.generate_cargo_toml_impl(name, needs_persistence)
664    }
665
666    fn generate_cargo_toml_impl(&self, name: &str, needs_persistence: bool) -> String {
667        if self.config.target == CodegenTarget::Wasm {
668            return self.generate_wasm_cargo_toml(name);
669        }
670
671        // Get the feature flag for the configured persistence backend
672        let feature_flag = if needs_persistence {
673            self.config.persistence.feature_flag()
674        } else {
675            None
676        };
677        let runtime_dep = self.config.runtime_dep.to_cargo_dep(feature_flag);
678
679        format!(
680            r#"[package]
681name = "{name}"
682version = "0.1.0"
683edition = "2021"
684
685[dependencies]
686{runtime_dep}
687tokio = {{ version = "1", features = ["full"] }}
688serde = {{ version = "1", features = ["derive"] }}
689serde_json = "1"
690
691# Standalone project, not part of parent workspace
692[workspace]
693"#
694        )
695    }
696
697    fn generate_wasm_cargo_toml(&self, name: &str) -> String {
698        let runtime_dep = self.config.runtime_dep.to_cargo_dep(None);
699
700        // Derive sage-runtime-web path from runtime dep
701        let web_dep = match &self.config.runtime_dep {
702            RuntimeDep::Path { path } => {
703                // Sibling crate: replace sage-runtime with sage-runtime-web
704                let web_path = path.replace("sage-runtime", "sage-runtime-web");
705                format!("sage-runtime-web = {{ path = \"{}\" }}", web_path)
706            }
707            RuntimeDep::CratesIo { version } => {
708                format!("sage-runtime-web = \"{version}\"")
709            }
710        };
711
712        format!(
713            r#"[package]
714name = "{name}"
715version = "0.1.0"
716edition = "2021"
717
718[lib]
719crate-type = ["cdylib", "rlib"]
720
721[dependencies]
722{runtime_dep}
723{web_dep}
724wasm-bindgen = "0.2"
725wasm-bindgen-futures = "0.4"
726serde = {{ version = "1", features = ["derive"] }}
727serde_json = "1"
728console_error_panic_hook = "0.1"
729
730# Standalone project, not part of parent workspace
731[workspace]
732
733[profile.wasm-release]
734inherits = "release"
735opt-level = "z"
736lto = true
737codegen-units = 1
738"#
739        )
740    }
741
742    /// Check if a program has any agents with @persistent fields.
743    fn has_persistent_fields(program: &Program) -> bool {
744        program
745            .agents
746            .iter()
747            .any(|agent| agent.beliefs.iter().any(|b| b.is_persistent))
748    }
749
750    /// Check if any module in a tree has agents with @persistent fields.
751    fn has_persistent_fields_in_tree(tree: &ModuleTree) -> bool {
752        tree.modules
753            .values()
754            .any(|module| Self::has_persistent_fields(&module.program))
755    }
756
757    // =========================================================================
758    // RFC-0012: Test generation
759    // =========================================================================
760
761    fn generate_test_binary(&mut self, program: &Program) -> String {
762        // Test prelude
763        self.emit
764            .writeln("//! Generated test file by Sage compiler. Do not edit.");
765        self.emit.blank_line();
766        self.emit.writeln("#![allow(unused_imports, dead_code)]");
767        self.emit.blank_line();
768        self.emit.writeln("use sage_runtime::prelude::*;");
769        self.emit.blank_line();
770
771        // Constants (test files may import types/constants from main code)
772        for const_decl in &program.consts {
773            self.generate_const(const_decl);
774            self.emit.blank_line();
775        }
776
777        // Enums
778        for enum_decl in &program.enums {
779            self.generate_enum(enum_decl);
780            self.emit.blank_line();
781        }
782
783        // Records
784        for record in &program.records {
785            self.generate_record(record);
786            self.emit.blank_line();
787        }
788
789        // Functions
790        for func in &program.functions {
791            self.generate_function(func);
792            self.emit.blank_line();
793        }
794
795        // Agents (test files may define helper agents)
796        for agent in &program.agents {
797            self.generate_agent(agent);
798            self.emit.blank_line();
799        }
800
801        // Separate serial and concurrent tests
802        let (serial_tests, concurrent_tests): (Vec<_>, Vec<_>) =
803            program.tests.iter().partition(|t| t.is_serial);
804
805        // Generate concurrent test functions
806        for test in &concurrent_tests {
807            self.generate_test_function(test);
808            self.emit.blank_line();
809        }
810
811        // Generate serial test functions (marked with #[serial])
812        for test in &serial_tests {
813            self.generate_test_function(test);
814            self.emit.blank_line();
815        }
816
817        // Generate an empty main function (required for bin crates)
818        self.emit.writeln("fn main() {}");
819
820        std::mem::take(&mut self.emit).finish()
821    }
822
823    fn generate_test_cargo_toml(&self, name: &str) -> String {
824        let runtime_dep = self.config.runtime_dep.to_cargo_dep(None);
825        format!(
826            r#"[package]
827name = "{name}"
828version = "0.1.0"
829edition = "2021"
830
831[dependencies]
832{runtime_dep}
833tokio = {{ version = "1", features = ["full"] }}
834serde = {{ version = "1", features = ["derive"] }}
835serde_json = "1"
836
837# Standalone project, not part of parent workspace
838[workspace]
839"#
840        )
841    }
842
843    fn generate_test_function(&mut self, test: &TestDecl) {
844        // Collect mock statements from the test body
845        let mock_infers = self.collect_mock_infers(&test.body);
846        let mock_tools = self.collect_mock_tools(&test.body);
847
848        // Generate test function
849        self.emit.writeln("#[tokio::test]");
850        // Convert test name to valid Rust identifier
851        let test_fn_name = self.sanitize_test_name(&test.name);
852        self.emit.write("async fn ");
853        self.emit.write(&test_fn_name);
854        self.emit.writeln("() {");
855        self.emit.indent();
856
857        // Generate mock LLM client if there are mock divines
858        if !mock_infers.is_empty() {
859            self.emit
860                .writeln("let _mock_client = MockLlmClient::with_responses(vec![");
861            self.emit.indent();
862            for mock in &mock_infers {
863                match mock {
864                    MockValue::Value(expr) => {
865                        self.emit.write("MockResponse::value(");
866                        self.generate_expr(expr);
867                        self.emit.writeln("),");
868                    }
869                    MockValue::Fail(expr) => {
870                        self.emit.write("MockResponse::fail(");
871                        self.generate_expr(expr);
872                        self.emit.writeln("),");
873                    }
874                }
875            }
876            self.emit.dedent();
877            self.emit.writeln("]);");
878            self.emit.blank_line();
879        }
880
881        // Generate mock tool registry if there are mock tools
882        if !mock_tools.is_empty() {
883            self.emit
884                .writeln("let _mock_tools = MockToolRegistry::new();");
885            for (tool_name, fn_name, value) in &mock_tools {
886                self.emit.write("_mock_tools.register(\"");
887                self.emit.write(tool_name);
888                self.emit.write("\", \"");
889                self.emit.write(fn_name);
890                self.emit.write("\", ");
891                match value {
892                    MockValue::Value(expr) => {
893                        self.emit.write("MockResponse::value(");
894                        self.generate_expr(expr);
895                        self.emit.write(")");
896                    }
897                    MockValue::Fail(expr) => {
898                        self.emit.write("MockResponse::fail(");
899                        self.generate_expr(expr);
900                        self.emit.write(")");
901                    }
902                }
903                self.emit.writeln(");");
904            }
905            self.emit.blank_line();
906
907            // Wrap test body in with_mock_tools context
908            self.emit.writeln("with_mock_tools(_mock_tools, async {");
909            self.emit.indent();
910            self.generate_test_block(&test.body);
911            self.emit.dedent();
912            self.emit.writeln("}).await;");
913        } else {
914            // No tool mocks, generate test body directly
915            self.generate_test_block(&test.body);
916        }
917
918        self.emit.dedent();
919        self.emit.writeln("}");
920    }
921
922    fn collect_mock_infers(&self, block: &Block) -> Vec<MockValue> {
923        let mut mocks = Vec::new();
924        for stmt in &block.stmts {
925            if let Stmt::MockDivine { value, .. } = stmt {
926                mocks.push(value.clone());
927            }
928        }
929        mocks
930    }
931
932    fn collect_mock_tools(&self, block: &Block) -> Vec<(String, String, MockValue)> {
933        let mut mocks = Vec::new();
934        for stmt in &block.stmts {
935            if let Stmt::MockTool {
936                tool_name,
937                fn_name,
938                value,
939                ..
940            } = stmt
941            {
942                mocks.push((tool_name.name.clone(), fn_name.name.clone(), value.clone()));
943            }
944        }
945        mocks
946    }
947
948    fn generate_test_block(&mut self, block: &Block) {
949        for stmt in &block.stmts {
950            // Skip mock divine and mock tool statements - they were collected separately
951            if matches!(stmt, Stmt::MockDivine { .. } | Stmt::MockTool { .. }) {
952                continue;
953            }
954            self.generate_test_stmt(stmt);
955        }
956    }
957
958    fn generate_test_stmt(&mut self, stmt: &Stmt) {
959        match stmt {
960            // Handle assertion builtins specially
961            Stmt::Expr { expr, .. } => {
962                if let Expr::Call { name, args, .. } = expr {
963                    if self.is_assertion_builtin(&name.name) {
964                        self.generate_assertion(&name.name, args);
965                        return;
966                    }
967                }
968                // Regular expression statement
969                self.generate_expr(expr);
970                self.emit.writeln(";");
971            }
972            // For other statements, use the normal generation
973            _ => self.generate_stmt(stmt),
974        }
975    }
976
977    fn is_assertion_builtin(&self, name: &str) -> bool {
978        matches!(
979            name,
980            "assert"
981                | "assert_eq"
982                | "assert_neq"
983                | "assert_gt"
984                | "assert_lt"
985                | "assert_gte"
986                | "assert_lte"
987                | "assert_true"
988                | "assert_false"
989                | "assert_contains"
990                | "assert_not_contains"
991                | "assert_empty"
992                | "assert_not_empty"
993                | "assert_starts_with"
994                | "assert_ends_with"
995                | "assert_len"
996                | "assert_empty_list"
997                | "assert_not_empty_list"
998                | "assert_fails"
999        )
1000    }
1001
1002    fn generate_assertion(&mut self, name: &str, args: &[Expr]) {
1003        match name {
1004            "assert" | "assert_true" => {
1005                self.emit.write("assert!(");
1006                if !args.is_empty() {
1007                    self.generate_expr(&args[0]);
1008                }
1009                self.emit.writeln(");");
1010            }
1011            "assert_false" => {
1012                self.emit.write("assert!(!");
1013                if !args.is_empty() {
1014                    self.generate_expr(&args[0]);
1015                }
1016                self.emit.writeln(");");
1017            }
1018            "assert_eq" => {
1019                self.emit.write("assert_eq!(");
1020                if args.len() >= 2 {
1021                    self.generate_expr(&args[0]);
1022                    self.emit.write(", ");
1023                    self.generate_expr(&args[1]);
1024                }
1025                self.emit.writeln(");");
1026            }
1027            "assert_neq" => {
1028                self.emit.write("assert_ne!(");
1029                if args.len() >= 2 {
1030                    self.generate_expr(&args[0]);
1031                    self.emit.write(", ");
1032                    self.generate_expr(&args[1]);
1033                }
1034                self.emit.writeln(");");
1035            }
1036            "assert_gt" => {
1037                self.emit.write("assert!(");
1038                if args.len() >= 2 {
1039                    self.generate_expr(&args[0]);
1040                    self.emit.write(" > ");
1041                    self.generate_expr(&args[1]);
1042                }
1043                self.emit.writeln(");");
1044            }
1045            "assert_lt" => {
1046                self.emit.write("assert!(");
1047                if args.len() >= 2 {
1048                    self.generate_expr(&args[0]);
1049                    self.emit.write(" < ");
1050                    self.generate_expr(&args[1]);
1051                }
1052                self.emit.writeln(");");
1053            }
1054            "assert_gte" => {
1055                self.emit.write("assert!(");
1056                if args.len() >= 2 {
1057                    self.generate_expr(&args[0]);
1058                    self.emit.write(" >= ");
1059                    self.generate_expr(&args[1]);
1060                }
1061                self.emit.writeln(");");
1062            }
1063            "assert_lte" => {
1064                self.emit.write("assert!(");
1065                if args.len() >= 2 {
1066                    self.generate_expr(&args[0]);
1067                    self.emit.write(" <= ");
1068                    self.generate_expr(&args[1]);
1069                }
1070                self.emit.writeln(");");
1071            }
1072            "assert_contains" => {
1073                self.emit.write("assert!(");
1074                if args.len() >= 2 {
1075                    self.generate_expr(&args[0]);
1076                    self.emit.write(".contains(&");
1077                    self.generate_expr(&args[1]);
1078                    self.emit.write(")");
1079                }
1080                self.emit.writeln(");");
1081            }
1082            "assert_not_contains" => {
1083                self.emit.write("assert!(!");
1084                if args.len() >= 2 {
1085                    self.generate_expr(&args[0]);
1086                    self.emit.write(".contains(&");
1087                    self.generate_expr(&args[1]);
1088                    self.emit.write(")");
1089                }
1090                self.emit.writeln(");");
1091            }
1092            "assert_empty" => {
1093                self.emit.write("assert!(");
1094                if !args.is_empty() {
1095                    self.generate_expr(&args[0]);
1096                }
1097                self.emit.writeln(".is_empty());");
1098            }
1099            "assert_not_empty" => {
1100                self.emit.write("assert!(!");
1101                if !args.is_empty() {
1102                    self.generate_expr(&args[0]);
1103                }
1104                self.emit.writeln(".is_empty());");
1105            }
1106            "assert_starts_with" => {
1107                self.emit.write("assert!(");
1108                if args.len() >= 2 {
1109                    self.generate_expr(&args[0]);
1110                    self.emit.write(".starts_with(&");
1111                    self.generate_expr(&args[1]);
1112                    self.emit.write(")");
1113                }
1114                self.emit.writeln(");");
1115            }
1116            "assert_ends_with" => {
1117                self.emit.write("assert!(");
1118                if args.len() >= 2 {
1119                    self.generate_expr(&args[0]);
1120                    self.emit.write(".ends_with(&");
1121                    self.generate_expr(&args[1]);
1122                    self.emit.write(")");
1123                }
1124                self.emit.writeln(");");
1125            }
1126            "assert_len" => {
1127                self.emit.write("assert_eq!(");
1128                if args.len() >= 2 {
1129                    self.generate_expr(&args[0]);
1130                    self.emit.write(".len() as i64, ");
1131                    self.generate_expr(&args[1]);
1132                }
1133                self.emit.writeln(");");
1134            }
1135            "assert_empty_list" => {
1136                self.emit.write("assert!(");
1137                if !args.is_empty() {
1138                    self.generate_expr(&args[0]);
1139                }
1140                self.emit.writeln(".is_empty());");
1141            }
1142            "assert_not_empty_list" => {
1143                self.emit.write("assert!(!");
1144                if !args.is_empty() {
1145                    self.generate_expr(&args[0]);
1146                }
1147                self.emit.writeln(".is_empty());");
1148            }
1149            "assert_fails" => {
1150                // assert_fails expects an expression that should fail
1151                self.emit.writeln("{");
1152                self.emit.indent();
1153                self.emit.write("let result = ");
1154                if !args.is_empty() {
1155                    self.generate_expr(&args[0]);
1156                }
1157                self.emit.writeln(";");
1158                self.emit.writeln(
1159                    "assert!(result.is_err(), \"Expected operation to fail but it succeeded\");",
1160                );
1161                self.emit.dedent();
1162                self.emit.writeln("}");
1163            }
1164            _ => {
1165                // Unknown assertion - just call it as a regular function
1166                self.emit.write(name);
1167                self.emit.write("(");
1168                for (i, arg) in args.iter().enumerate() {
1169                    if i > 0 {
1170                        self.emit.write(", ");
1171                    }
1172                    self.generate_expr(arg);
1173                }
1174                self.emit.writeln(");");
1175            }
1176        }
1177    }
1178
1179    fn sanitize_test_name(&self, name: &str) -> String {
1180        // Convert test name to valid Rust identifier
1181        name.chars()
1182            .map(|c| if c.is_alphanumeric() { c } else { '_' })
1183            .collect::<String>()
1184            .to_lowercase()
1185    }
1186
1187    fn generate_const(&mut self, const_decl: &ConstDecl) {
1188        if const_decl.is_pub {
1189            self.emit.write("pub ");
1190        }
1191        self.emit.write("const ");
1192        self.emit.write(&const_decl.name.name);
1193        self.emit.write(": ");
1194        // String constants must use &'static str since .to_string() isn't const
1195        let is_string = matches!(const_decl.ty, TypeExpr::String);
1196        if is_string {
1197            self.string_consts.insert(const_decl.name.name.clone());
1198            self.emit.write("&'static str");
1199        } else {
1200            self.emit_type(&const_decl.ty);
1201        }
1202        self.emit.write(" = ");
1203        // For string constants, emit raw string literal without .to_string()
1204        if is_string {
1205            if let Expr::Literal {
1206                value: Literal::String(s),
1207                ..
1208            } = &const_decl.value
1209            {
1210                self.emit.write("\"");
1211                self.emit.write(&Self::escape_string_for_rust(s));
1212                self.emit.write("\"");
1213            } else {
1214                self.generate_expr(&const_decl.value);
1215            }
1216        } else {
1217            self.generate_expr(&const_decl.value);
1218        }
1219        self.emit.writeln(";");
1220    }
1221
1222    fn generate_enum(&mut self, enum_decl: &EnumDecl) {
1223        if enum_decl.is_pub {
1224            self.emit.write("pub ");
1225        }
1226        // Generic enums can't be Copy since type params may not be Copy
1227        if enum_decl.type_params.is_empty() {
1228            self.emit
1229                .writeln("#[derive(Debug, Clone, Copy, PartialEq, Eq)]");
1230        } else {
1231            self.emit.writeln("#[derive(Debug, Clone, PartialEq, Eq)]");
1232        }
1233        self.emit.write("enum ");
1234        self.emit.write(&enum_decl.name.name);
1235        // RFC-0015: Emit type parameters
1236        if !enum_decl.type_params.is_empty() {
1237            self.emit.write("<");
1238            for (i, param) in enum_decl.type_params.iter().enumerate() {
1239                if i > 0 {
1240                    self.emit.write(", ");
1241                }
1242                self.emit.write(&param.name);
1243            }
1244            self.emit.write(">");
1245        }
1246        self.emit.writeln(" {");
1247        self.emit.indent();
1248        for variant in &enum_decl.variants {
1249            self.emit.write(&variant.name.name);
1250            if let Some(payload_ty) = &variant.payload {
1251                self.emit.write("(");
1252                self.emit_type(payload_ty);
1253                self.emit.write(")");
1254            }
1255            self.emit.writeln(",");
1256        }
1257        self.emit.dedent();
1258        self.emit.writeln("}");
1259    }
1260
1261    fn generate_record(&mut self, record: &RecordDecl) {
1262        if record.is_pub {
1263            self.emit.write("pub ");
1264        }
1265        // Derive serde traits for message serialization
1266        self.emit
1267            .writeln("#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]");
1268        self.emit.write("struct ");
1269        self.emit.write(&record.name.name);
1270        // RFC-0015: Emit type parameters
1271        if !record.type_params.is_empty() {
1272            self.emit.write("<");
1273            for (i, param) in record.type_params.iter().enumerate() {
1274                if i > 0 {
1275                    self.emit.write(", ");
1276                }
1277                self.emit.write(&param.name);
1278            }
1279            self.emit.write(">");
1280        }
1281        self.emit.writeln(" {");
1282        self.emit.indent();
1283        for field in &record.fields {
1284            self.emit.write(&field.name.name);
1285            self.emit.write(": ");
1286            self.emit_type(&field.ty);
1287            self.emit.writeln(",");
1288        }
1289        self.emit.dedent();
1290        self.emit.writeln("}");
1291    }
1292
1293    fn generate_function(&mut self, func: &FnDecl) {
1294        // Function signature with visibility
1295        if func.is_pub {
1296            self.emit.write("pub ");
1297        }
1298        self.emit.write("fn ");
1299        self.emit.write(&func.name.name);
1300        // RFC-0015: Emit type parameters
1301        if !func.type_params.is_empty() {
1302            self.emit.write("<");
1303            for (i, param) in func.type_params.iter().enumerate() {
1304                if i > 0 {
1305                    self.emit.write(", ");
1306                }
1307                self.emit.write(&param.name);
1308            }
1309            self.emit.write(">");
1310        }
1311        self.emit.write("(");
1312
1313        for (i, param) in func.params.iter().enumerate() {
1314            if i > 0 {
1315                self.emit.write(", ");
1316            }
1317            self.emit.write(&param.name.name);
1318            self.emit.write(": ");
1319            self.emit_type(&param.ty);
1320        }
1321
1322        self.emit.write(") -> ");
1323
1324        // RFC-0007: Wrap return type in SageResult if fallible
1325        if func.is_fallible {
1326            self.emit.write("SageResult<");
1327            self.emit_type(&func.return_ty);
1328            self.emit.write(">");
1329        } else {
1330            self.emit_type(&func.return_ty);
1331        }
1332
1333        self.emit.write(" ");
1334        self.generate_block(&func.body);
1335    }
1336
1337    /// Collect metadata about an agent (error handlers, message handlers) for use in summon generation.
1338    /// This is called in a pre-pass before any code generation.
1339    fn collect_agent_metadata(&mut self, agent: &AgentDecl) {
1340        let name = &agent.name.name;
1341
1342        // Track if this agent has an error handler
1343        let has_error_handler = agent
1344            .handlers
1345            .iter()
1346            .any(|h| matches!(h.event, EventKind::Error { .. }));
1347        if has_error_handler {
1348            self.agents_with_error_handlers.insert(name.clone());
1349        }
1350
1351        // Track tool uses for summon generation
1352        if !agent.tool_uses.is_empty() {
1353            let tool_names: Vec<String> = agent.tool_uses.iter().map(|t| t.name.clone()).collect();
1354            self.agent_tool_uses.insert(name.clone(), tool_names);
1355        }
1356
1357        // Track message handlers
1358        let message_handlers: Vec<_> = agent
1359            .handlers
1360            .iter()
1361            .filter_map(|h| {
1362                if let EventKind::Message {
1363                    param_name,
1364                    param_ty,
1365                } = &h.event
1366                {
1367                    Some((param_name.clone(), param_ty.clone()))
1368                } else {
1369                    None
1370                }
1371            })
1372            .collect();
1373        if !message_handlers.is_empty() {
1374            self.agents_with_message_handlers.insert(
1375                name.clone(),
1376                AgentMessageHandlers {
1377                    handlers: message_handlers,
1378                },
1379            );
1380        }
1381    }
1382
1383    fn generate_agent(&mut self, agent: &AgentDecl) {
1384        let name = &agent.name.name;
1385
1386        // Phase 3: Set current protocol roles for this agent (used by reply() generation)
1387        self.current_protocol_roles.clear();
1388        for pr in &agent.follows {
1389            self.current_protocol_roles
1390                .insert(pr.protocol.name.clone(), pr.role.name.clone());
1391        }
1392
1393        // v2.0: Set current agent's @persistent beliefs (used by checkpoint() generation)
1394        self.current_agent_persistent_beliefs = agent
1395            .beliefs
1396            .iter()
1397            .filter(|b| b.is_persistent)
1398            .map(|b| b.name.name.clone())
1399            .collect();
1400
1401        // RFC-0011: Check for tool usage
1402        let has_tools = !agent.tool_uses.is_empty();
1403        let needs_struct_body = !agent.beliefs.is_empty() || has_tools;
1404
1405        // Struct definition with visibility
1406        if agent.is_pub {
1407            self.emit.write("pub ");
1408        }
1409        self.emit.write("struct ");
1410        self.emit.write(name);
1411        if !needs_struct_body {
1412            self.emit.writeln(";");
1413        } else {
1414            self.emit.writeln(" {");
1415            self.emit.indent();
1416
1417            // RFC-0011: Generate tool fields
1418            for tool_use in &agent.tool_uses {
1419                // Generate field like: http: HttpClient
1420                self.emit.write(&tool_use.name.to_lowercase());
1421                self.emit.write(": ");
1422                self.emit.write(&tool_use.name);
1423                self.emit.writeln("Client,");
1424            }
1425
1426            // Check if this agent has any @persistent fields
1427            let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
1428
1429            // Add checkpoint store field if agent has persistent beliefs
1430            if has_persistent {
1431                self.emit
1432                    .writeln("_checkpoint: std::sync::Arc<dyn CheckpointStore>,");
1433                self.emit.writeln("_checkpoint_key: String,");
1434            }
1435
1436            // Regular belief fields - wrap @persistent ones in Persisted<T>
1437            for belief in &agent.beliefs {
1438                self.emit.write(&belief.name.name);
1439                self.emit.write(": ");
1440                if belief.is_persistent {
1441                    self.emit.write("Persisted<");
1442                    self.emit_type(&belief.ty);
1443                    self.emit.write(">");
1444                } else {
1445                    self.emit_type(&belief.ty);
1446                }
1447                self.emit.writeln(",");
1448            }
1449            self.emit.dedent();
1450            self.emit.writeln("}");
1451        }
1452        self.emit.blank_line();
1453
1454        // Find the output type from the start handler
1455        let output_type = self.infer_agent_output_type(agent);
1456
1457        // Impl block
1458        self.emit.write("impl ");
1459        self.emit.write(name);
1460        self.emit.writeln(" {");
1461        self.emit.indent();
1462
1463        // Generate handlers
1464        for handler in &agent.handlers {
1465            match &handler.event {
1466                // v2 lifecycle: on waking - runs before start, after persistent state loaded
1467                EventKind::Waking => {
1468                    self.emit.writeln("async fn on_waking(&self) {");
1469                    self.emit.indent();
1470                    self.generate_block_contents(&handler.body);
1471                    self.emit.dedent();
1472                    self.emit.writeln("}");
1473                }
1474
1475                EventKind::Start => {
1476                    self.emit
1477                        .write("async fn on_start(&self, ctx: &mut AgentContext<");
1478                    self.emit.write(&output_type);
1479                    self.emit.write(">) -> SageResult<");
1480                    self.emit.write(&output_type);
1481                    self.emit.writeln("> {");
1482                    self.emit.indent();
1483                    self.generate_block_contents(&handler.body);
1484                    self.emit.dedent();
1485                    self.emit.writeln("}");
1486                }
1487
1488                // RFC-0007: Generate on_error handler
1489                EventKind::Error { param_name } => {
1490                    self.emit.write("async fn on_error(&self, _");
1491                    self.emit.write(&param_name.name);
1492                    self.emit.write(": SageError, ctx: &mut AgentContext<");
1493                    self.emit.write(&output_type);
1494                    self.emit.write(">) -> SageResult<");
1495                    self.emit.write(&output_type);
1496                    self.emit.writeln("> {");
1497                    self.emit.indent();
1498                    self.generate_block_contents(&handler.body);
1499                    // Fallback: re-raise the error if handler doesn't explicitly return
1500                    self.emit.write("Err(_");
1501                    self.emit.write(&param_name.name);
1502                    self.emit.writeln(")");
1503                    self.emit.dedent();
1504                    self.emit.writeln("}");
1505                }
1506
1507                // v2 lifecycle: on pause - runs when supervisor signals graceful pause
1508                EventKind::Pause => {
1509                    self.emit.writeln("async fn on_pause(&self) {");
1510                    self.emit.indent();
1511                    self.generate_block_contents(&handler.body);
1512                    self.emit.dedent();
1513                    self.emit.writeln("}");
1514                }
1515
1516                // v2 lifecycle: on resume - runs when agent is unpaused
1517                EventKind::Resume => {
1518                    self.emit.writeln("async fn on_resume(&self) {");
1519                    self.emit.indent();
1520                    self.generate_block_contents(&handler.body);
1521                    self.emit.dedent();
1522                    self.emit.writeln("}");
1523                }
1524
1525                // on stop handler - cleanup before termination
1526                EventKind::Stop => {
1527                    self.emit.writeln("async fn on_stop(&self) {");
1528                    self.emit.indent();
1529                    self.generate_block_contents(&handler.body);
1530                    self.emit.dedent();
1531                    self.emit.writeln("}");
1532                }
1533
1534                // v2 lifecycle: on resting - alias for stop
1535                EventKind::Resting => {
1536                    self.emit.writeln("async fn on_stop(&self) {");
1537                    self.emit.indent();
1538                    self.generate_block_contents(&handler.body);
1539                    self.emit.dedent();
1540                    self.emit.writeln("}");
1541                }
1542
1543                // Message handler: on message(param: Type)
1544                EventKind::Message {
1545                    param_name,
1546                    param_ty,
1547                } => {
1548                    // Generate method name from type, e.g., on_message_ping for Ping
1549                    let type_name = self.type_expr_to_string(param_ty);
1550                    let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
1551
1552                    self.emit.write("async fn ");
1553                    self.emit.write(&method_name);
1554                    self.emit.write("(&self, ");
1555                    self.emit.write(&param_name.name);
1556                    self.emit.write(": ");
1557                    self.emit_type(param_ty);
1558                    self.emit.write(", ctx: &mut AgentContext<");
1559                    self.emit.write(&output_type);
1560                    self.emit.write(">) -> SageResult<()> ");
1561                    self.emit.writeln("{");
1562                    self.emit.indent();
1563                    self.generate_block_contents(&handler.body);
1564                    self.emit.writeln("Ok(())");
1565                    self.emit.dedent();
1566                    self.emit.writeln("}");
1567                }
1568            }
1569        }
1570
1571        self.emit.dedent();
1572        self.emit.writeln("}");
1573    }
1574
1575    fn generate_main(&mut self, agent: &AgentDecl) {
1576        if self.config.target == CodegenTarget::Wasm {
1577            return self.generate_wasm_main(agent);
1578        }
1579
1580        let entry_agent = &agent.name.name;
1581
1582        // Phase 3: Set current protocol roles for this agent (used by reply() generation)
1583        self.current_protocol_roles.clear();
1584        for pr in &agent.follows {
1585            self.current_protocol_roles
1586                .insert(pr.protocol.name.clone(), pr.role.name.clone());
1587        }
1588
1589        // v2.0: Set current agent's @persistent beliefs (used by checkpoint() generation)
1590        self.current_agent_persistent_beliefs = agent
1591            .beliefs
1592            .iter()
1593            .filter(|b| b.is_persistent)
1594            .map(|b| b.name.name.clone())
1595            .collect();
1596
1597        let has_error_handler = agent
1598            .handlers
1599            .iter()
1600            .any(|h| matches!(h.event, EventKind::Error { .. }));
1601
1602        let has_stop_handler = agent
1603            .handlers
1604            .iter()
1605            .any(|h| matches!(h.event, EventKind::Stop | EventKind::Resting));
1606
1607        // RFC-0011: Check if agent uses tools
1608        let has_tools = !agent.tool_uses.is_empty();
1609
1610        // v2.0: Check if agent has @persistent fields
1611        let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
1612
1613        // v2.0: Check if agent has on_waking handler
1614        let has_waking = agent
1615            .handlers
1616            .iter()
1617            .any(|h| matches!(h.event, EventKind::Waking));
1618
1619        // Collect message handlers for dispatch loop
1620        let message_handlers: Vec<_> = agent
1621            .handlers
1622            .iter()
1623            .filter_map(|h| {
1624                if let EventKind::Message {
1625                    param_name,
1626                    param_ty,
1627                } = &h.event
1628                {
1629                    Some((param_name.clone(), param_ty.clone()))
1630                } else {
1631                    None
1632                }
1633            })
1634            .collect();
1635        let has_message_handlers = !message_handlers.is_empty();
1636
1637        self.emit.writeln("#[tokio::main]");
1638        self.emit
1639            .writeln("async fn main() -> Result<(), Box<dyn std::error::Error>> {");
1640        self.emit.indent();
1641
1642        // Initialize tracing with config
1643        self.emit_tracing_init();
1644        self.emit.writeln("");
1645
1646        // v2.0: Initialize checkpoint store if agent has persistent fields
1647        if has_persistent {
1648            self.emit
1649                .writeln("// Initialize persistence checkpoint store");
1650            self.emit.writeln(
1651                "let _checkpoint: std::sync::Arc<dyn CheckpointStore> = std::sync::Arc::new(",
1652            );
1653            self.emit.indent();
1654            self.emit_checkpoint_store_init();
1655            self.emit.dedent();
1656            self.emit.writeln(");");
1657            self.emit.write("let _checkpoint_key = \"");
1658            self.emit.write(entry_agent);
1659            self.emit.writeln("_entry\".to_string();");
1660            self.emit.writeln("");
1661        }
1662
1663        // Helper to generate agent construction (with or without tool/persistent fields)
1664        let needs_struct = has_tools || !agent.beliefs.is_empty();
1665        let agent_construct = if needs_struct {
1666            let mut s = format!("{entry_agent} {{ ");
1667            let mut fields = Vec::new();
1668
1669            // Add checkpoint fields if persistent
1670            if has_persistent {
1671                fields.push("_checkpoint: std::sync::Arc::clone(&_checkpoint)".to_string());
1672                fields.push("_checkpoint_key: _checkpoint_key.clone()".to_string());
1673            }
1674
1675            // Add tool fields
1676            for tool_use in &agent.tool_uses {
1677                if tool_use.name == "Database" {
1678                    fields.push(format!("{}: _db_client", tool_use.name.to_lowercase()));
1679                } else {
1680                    fields.push(format!(
1681                        "{}: {}Client::from_env()",
1682                        tool_use.name.to_lowercase(),
1683                        tool_use.name
1684                    ));
1685                }
1686            }
1687
1688            // Add belief fields
1689            for belief in &agent.beliefs {
1690                if belief.is_persistent {
1691                    // Persisted fields load from checkpoint
1692                    fields.push(format!(
1693                        "{}: Persisted::new(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"{}\")",
1694                        belief.name.name,
1695                        belief.name.name
1696                    ));
1697                } else {
1698                    // Non-persistent beliefs use Default
1699                    fields.push(format!("{}: Default::default()", belief.name.name));
1700                }
1701            }
1702
1703            s.push_str(&fields.join(", "));
1704            s.push_str(" }");
1705            s
1706        } else {
1707            entry_agent.to_string()
1708        };
1709
1710        // Set up graceful shutdown signal handling
1711        self.emit
1712            .writeln("let ctrl_c = async { tokio::signal::ctrl_c().await.ok() };");
1713
1714        self.emit.writeln("#[cfg(unix)]");
1715        self.emit.writeln("let terminate = async {");
1716        self.emit.indent();
1717        self.emit.writeln(
1718            "if let Ok(mut s) = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {",
1719        );
1720        self.emit.indent();
1721        self.emit.writeln("s.recv().await;");
1722        self.emit.dedent();
1723        self.emit.writeln("} else {");
1724        self.emit.indent();
1725        self.emit.writeln("std::future::pending::<()>().await;");
1726        self.emit.dedent();
1727        self.emit.writeln("}");
1728        self.emit.dedent();
1729        self.emit.writeln("};");
1730        self.emit.writeln("#[cfg(not(unix))]");
1731        self.emit
1732            .writeln("let terminate = std::future::pending::<()>();");
1733        self.emit.writeln("");
1734
1735        self.emit
1736            .writeln("let handle = sage_runtime::spawn(move |mut ctx| async move {");
1737        self.emit.indent();
1738
1739        // RFC-0011: Initialize async tools (like Database) before agent construction
1740        for tool_use in &agent.tool_uses {
1741            if tool_use.name == "Database" {
1742                // Database requires async initialization
1743                self.emit
1744                    .write("let _db_client = DatabaseClient::from_env().await");
1745                self.emit
1746                    .writeln(".expect(\"Failed to connect to database\");");
1747            }
1748        }
1749
1750        self.emit.write("let agent = ");
1751        self.emit.write(&agent_construct);
1752        self.emit.writeln(";");
1753
1754        // v2.0: Call on_waking after persistent state is loaded
1755        if has_waking {
1756            self.emit.writeln("");
1757            self.emit
1758                .writeln("// on_waking: runs after persistent state loaded, before on_start");
1759            self.emit.writeln("agent.on_waking().await;");
1760        }
1761
1762        if has_error_handler {
1763            // RFC-0007: Generate error dispatch code
1764            self.emit
1765                .writeln("let result = match agent.on_start(&mut ctx).await {");
1766            self.emit.indent();
1767            self.emit.writeln("Ok(result) => Ok(result),");
1768            self.emit
1769                .writeln("Err(e) => agent.on_error(e, &mut ctx).await,");
1770            self.emit.dedent();
1771            self.emit.writeln("};");
1772        } else {
1773            // Simple case: no error handler
1774            self.emit
1775                .writeln("let result = agent.on_start(&mut ctx).await;");
1776        }
1777
1778        // Phase 3: Collect protocol roles for validation
1779        let protocol_roles: Vec<_> = agent
1780            .follows
1781            .iter()
1782            .map(|pr| (pr.protocol.name.clone(), pr.role.name.clone()))
1783            .collect();
1784        let has_protocols = !protocol_roles.is_empty();
1785
1786        // Message receive loop (if agent has message handlers)
1787        if has_message_handlers {
1788            self.emit.writeln("");
1789            self.emit.writeln("// Message receive loop");
1790            self.emit.writeln("if result.is_ok() {");
1791            self.emit.indent();
1792            self.emit.writeln("loop {");
1793            self.emit.indent();
1794            self.emit.writeln("match ctx.receive_raw().await {");
1795            self.emit.indent();
1796            self.emit.writeln("Ok(msg) => {");
1797            self.emit.indent();
1798            self.emit.writeln("// Dispatch based on message type");
1799            self.emit.writeln("match msg.type_name.as_deref() {");
1800            self.emit.indent();
1801
1802            // Generate match arms for each message type
1803            for (param_name, param_ty) in &message_handlers {
1804                let type_name = self.type_expr_to_string(param_ty);
1805                let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
1806
1807                self.emit.write("Some(\"");
1808                self.emit.write(&type_name);
1809                self.emit.writeln("\") => {");
1810                self.emit.indent();
1811
1812                // Phase 3: Validate protocol state for this message type
1813                if has_protocols {
1814                    // Find which protocol role handles this message type
1815                    // For simplicity, use the first role that might handle it
1816                    // (in practice, the checker ensures only one protocol/role combo is valid)
1817                    if let Some((_, role)) = protocol_roles.first() {
1818                        self.emit.writeln("// Phase 3: Validate protocol state");
1819                        self.emit.write("ctx.validate_protocol_receive(\"");
1820                        self.emit.write(&type_name);
1821                        self.emit.write("\", \"");
1822                        self.emit.write(role);
1823                        self.emit.writeln("\").await?;");
1824                    }
1825                }
1826
1827                self.emit.write("let ");
1828                self.emit.write(&param_name.name);
1829                self.emit.write(": ");
1830                self.emit_type(param_ty);
1831                self.emit.writeln(" = serde_json::from_value(msg.payload)");
1832                self.emit.indent();
1833                self.emit.writeln(".map_err(|e| SageError::Agent(format!(\"Failed to deserialize message: {e}\")))?;");
1834                self.emit.dedent();
1835                self.emit.write("agent.");
1836                self.emit.write(&method_name);
1837                self.emit.write("(");
1838                self.emit.write(&param_name.name);
1839                self.emit.writeln(", &mut ctx).await?;");
1840                self.emit.dedent();
1841                self.emit.writeln("}");
1842            }
1843
1844            // Default case for unknown message types
1845            self.emit.writeln("_ => {");
1846            self.emit.indent();
1847            self.emit.writeln("// Unknown message type, skip");
1848            self.emit.dedent();
1849            self.emit.writeln("}");
1850
1851            self.emit.dedent();
1852            self.emit.writeln("}"); // close match type_name
1853            self.emit.dedent();
1854            self.emit.writeln("}"); // close Ok(msg)
1855            self.emit.writeln("Err(_) => break, // Channel closed");
1856            self.emit.dedent();
1857            self.emit.writeln("}"); // close match receive_raw
1858            self.emit.dedent();
1859            self.emit.writeln("}"); // close loop
1860            self.emit.dedent();
1861            self.emit.writeln("}"); // close if result.is_ok()
1862        }
1863
1864        if has_stop_handler {
1865            // Call on_stop for cleanup (errors are ignored)
1866            self.emit.writeln("agent.on_stop().await;");
1867        }
1868
1869        self.emit.writeln("result");
1870        self.emit.dedent();
1871        self.emit.writeln("});");
1872
1873        // Use tokio::select! to race between agent completion and shutdown signals
1874        self.emit.writeln("");
1875        self.emit.writeln("let _result = tokio::select! {");
1876        self.emit.indent();
1877        self.emit.writeln("result = handle.result() => result?,");
1878        self.emit.writeln("_ = ctrl_c => {");
1879        self.emit.indent();
1880        self.emit
1881            .writeln("eprintln!(\"\\nReceived interrupt signal, shutting down...\");");
1882        self.emit.writeln("std::process::exit(0);");
1883        self.emit.dedent();
1884        self.emit.writeln("}");
1885        self.emit.writeln("_ = terminate => {");
1886        self.emit.indent();
1887        self.emit
1888            .writeln("eprintln!(\"Received terminate signal, shutting down...\");");
1889        self.emit.writeln("std::process::exit(0);");
1890        self.emit.dedent();
1891        self.emit.writeln("}");
1892        self.emit.dedent();
1893        self.emit.writeln("};");
1894        self.emit.writeln("Ok(())");
1895
1896        self.emit.dedent();
1897        self.emit.writeln("}");
1898    }
1899
1900    /// Generate WASM entry point using wasm_bindgen(start).
1901    ///
1902    /// Instead of `#[tokio::main] async fn main()`, generates a `#[wasm_bindgen(start)]`
1903    /// function that sets up panic hook, tracing, and spawns the agent via spawn_local.
1904    fn generate_wasm_main(&mut self, agent: &AgentDecl) {
1905        let entry_agent = &agent.name.name;
1906
1907        // Set current protocol roles (same as native)
1908        self.current_protocol_roles.clear();
1909        for pr in &agent.follows {
1910            self.current_protocol_roles
1911                .insert(pr.protocol.name.clone(), pr.role.name.clone());
1912        }
1913
1914        // Set current agent's @persistent beliefs
1915        self.current_agent_persistent_beliefs = agent
1916            .beliefs
1917            .iter()
1918            .filter(|b| b.is_persistent)
1919            .map(|b| b.name.name.clone())
1920            .collect();
1921
1922        let has_error_handler = agent
1923            .handlers
1924            .iter()
1925            .any(|h| matches!(h.event, EventKind::Error { .. }));
1926
1927        let has_stop_handler = agent
1928            .handlers
1929            .iter()
1930            .any(|h| matches!(h.event, EventKind::Stop | EventKind::Resting));
1931
1932        let has_tools = !agent.tool_uses.is_empty();
1933        let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
1934
1935        let has_waking = agent
1936            .handlers
1937            .iter()
1938            .any(|h| matches!(h.event, EventKind::Waking));
1939
1940        // Collect message handlers
1941        let message_handlers: Vec<_> = agent
1942            .handlers
1943            .iter()
1944            .filter_map(|h| {
1945                if let EventKind::Message {
1946                    param_name,
1947                    param_ty,
1948                } = &h.event
1949                {
1950                    Some((param_name.clone(), param_ty.clone()))
1951                } else {
1952                    None
1953                }
1954            })
1955            .collect();
1956        let has_message_handlers = !message_handlers.is_empty();
1957
1958        // WASM entry point
1959        self.emit.writeln("#[wasm_bindgen(start)]");
1960        self.emit.writeln("pub fn wasm_main() {");
1961        self.emit.indent();
1962
1963        // Panic hook for browser console
1964        self.emit.writeln("console_error_panic_hook::set_once();");
1965        self.emit.writeln("");
1966
1967        // Tracing init (console backend on WASM)
1968        self.emit.writeln("sage_runtime::trace::init();");
1969        self.emit.writeln("");
1970
1971        // Initialize checkpoint store if needed
1972        if has_persistent {
1973            self.emit
1974                .writeln("// Initialize persistence checkpoint store (memory-only on WASM)");
1975            self.emit.writeln(
1976                "let _checkpoint: std::sync::Arc<dyn CheckpointStore> = std::sync::Arc::new(",
1977            );
1978            self.emit.indent();
1979            self.emit
1980                .writeln("sage_runtime::persistence::MemoryCheckpointStore::new()");
1981            self.emit.dedent();
1982            self.emit.writeln(");");
1983            self.emit.write("let _checkpoint_key = \"");
1984            self.emit.write(entry_agent);
1985            self.emit.writeln("_entry\".to_string();");
1986            self.emit.writeln("");
1987        }
1988
1989        // Build the agent construction expression (same logic as native)
1990        let needs_struct = has_tools || !agent.beliefs.is_empty();
1991        let agent_construct = if needs_struct {
1992            let mut s = format!("{entry_agent} {{ ");
1993            let mut fields = Vec::new();
1994
1995            if has_persistent {
1996                fields.push("_checkpoint: std::sync::Arc::clone(&_checkpoint)".to_string());
1997                fields.push("_checkpoint_key: _checkpoint_key.clone()".to_string());
1998            }
1999
2000            for tool_use in &agent.tool_uses {
2001                if tool_use.name == "Database" {
2002                    // Database is stubbed on WASM, but still needs the field
2003                    fields.push(format!("{}: _db_client", tool_use.name.to_lowercase()));
2004                } else {
2005                    fields.push(format!(
2006                        "{}: {}Client::from_env()",
2007                        tool_use.name.to_lowercase(),
2008                        tool_use.name
2009                    ));
2010                }
2011            }
2012
2013            for belief in &agent.beliefs {
2014                if belief.is_persistent {
2015                    fields.push(format!(
2016                        "{}: Persisted::new(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"{}\")",
2017                        belief.name.name,
2018                        belief.name.name
2019                    ));
2020                } else {
2021                    fields.push(format!("{}: Default::default()", belief.name.name));
2022                }
2023            }
2024
2025            s.push_str(&fields.join(", "));
2026            s.push_str(" }");
2027            s
2028        } else {
2029            entry_agent.to_string()
2030        };
2031
2032        // Spawn the agent via spawn_local (no Send bounds on WASM)
2033        self.emit
2034            .writeln("let handle = sage_runtime::spawn(move |mut ctx| async move {");
2035        self.emit.indent();
2036
2037        // Async tool initialization inside spawn closure
2038        for tool_use in &agent.tool_uses {
2039            if tool_use.name == "Database" {
2040                self.emit
2041                    .write("let _db_client = DatabaseClient::from_env().await");
2042                self.emit
2043                    .writeln(".expect(\"Failed to connect to database\");");
2044            }
2045        }
2046
2047        self.emit.write("let agent = ");
2048        self.emit.write(&agent_construct);
2049        self.emit.writeln(";");
2050
2051        if has_waking {
2052            self.emit.writeln("");
2053            self.emit
2054                .writeln("// on_waking: runs after persistent state loaded, before on_start");
2055            self.emit.writeln("agent.on_waking().await;");
2056        }
2057
2058        if has_error_handler {
2059            self.emit
2060                .writeln("let result = match agent.on_start(&mut ctx).await {");
2061            self.emit.indent();
2062            self.emit.writeln("Ok(result) => Ok(result),");
2063            self.emit
2064                .writeln("Err(e) => agent.on_error(e, &mut ctx).await,");
2065            self.emit.dedent();
2066            self.emit.writeln("};");
2067        } else {
2068            self.emit
2069                .writeln("let result = agent.on_start(&mut ctx).await;");
2070        }
2071
2072        // Protocol roles for message dispatch
2073        let protocol_roles: Vec<_> = agent
2074            .follows
2075            .iter()
2076            .map(|pr| (pr.protocol.name.clone(), pr.role.name.clone()))
2077            .collect();
2078        let has_protocols = !protocol_roles.is_empty();
2079
2080        // Message receive loop (same as native)
2081        if has_message_handlers {
2082            self.emit.writeln("");
2083            self.emit.writeln("// Message receive loop");
2084            self.emit.writeln("if result.is_ok() {");
2085            self.emit.indent();
2086            self.emit.writeln("loop {");
2087            self.emit.indent();
2088            self.emit.writeln("match ctx.receive_raw().await {");
2089            self.emit.indent();
2090            self.emit.writeln("Ok(msg) => {");
2091            self.emit.indent();
2092            self.emit.writeln("match msg.type_name.as_deref() {");
2093            self.emit.indent();
2094
2095            for (param_name, param_ty) in &message_handlers {
2096                let type_name = self.type_expr_to_string(param_ty);
2097                let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
2098
2099                self.emit.write("Some(\"");
2100                self.emit.write(&type_name);
2101                self.emit.writeln("\") => {");
2102                self.emit.indent();
2103
2104                if has_protocols {
2105                    if let Some((_, role)) = protocol_roles.first() {
2106                        self.emit.writeln("// Validate protocol state");
2107                        self.emit.write("ctx.validate_protocol_receive(\"");
2108                        self.emit.write(&type_name);
2109                        self.emit.write("\", \"");
2110                        self.emit.write(role);
2111                        self.emit.writeln("\").await?;");
2112                    }
2113                }
2114
2115                self.emit.write("let ");
2116                self.emit.write(&param_name.name);
2117                self.emit.write(": ");
2118                self.emit_type(param_ty);
2119                self.emit.writeln(" = serde_json::from_value(msg.payload)");
2120                self.emit.indent();
2121                self.emit.writeln(".map_err(|e| SageError::Agent(format!(\"Failed to deserialize message: {e}\")))?;");
2122                self.emit.dedent();
2123                self.emit.write("agent.");
2124                self.emit.write(&method_name);
2125                self.emit.write("(");
2126                self.emit.write(&param_name.name);
2127                self.emit.writeln(", &mut ctx).await?;");
2128                self.emit.dedent();
2129                self.emit.writeln("}");
2130            }
2131
2132            self.emit.writeln("_ => {}");
2133            self.emit.dedent();
2134            self.emit.writeln("}"); // close match type_name
2135            self.emit.dedent();
2136            self.emit.writeln("}"); // close Ok(msg)
2137            self.emit.writeln("Err(_) => break,");
2138            self.emit.dedent();
2139            self.emit.writeln("}"); // close match receive_raw
2140            self.emit.dedent();
2141            self.emit.writeln("}"); // close loop
2142            self.emit.dedent();
2143            self.emit.writeln("}"); // close if result.is_ok()
2144        }
2145
2146        if has_stop_handler {
2147            self.emit.writeln("agent.on_stop().await;");
2148        }
2149
2150        self.emit.writeln("result");
2151        self.emit.dedent();
2152        self.emit.writeln("});");
2153
2154        self.emit.writeln("");
2155
2156        // On WASM, we spawn_local to await the handle result and log errors
2157        self.emit.writeln("wasm_bindgen_futures::spawn_local(async move {");
2158        self.emit.indent();
2159        self.emit.writeln("match handle.result().await {");
2160        self.emit.indent();
2161        self.emit.writeln("Ok(_) => sage_runtime_web::console_log(\"Agent completed successfully\"),");
2162        self.emit.writeln("Err(e) => sage_runtime_web::console_error(&format!(\"Agent failed: {e}\")),");
2163        self.emit.dedent();
2164        self.emit.writeln("}");
2165        self.emit.dedent();
2166        self.emit.writeln("});");
2167
2168        self.emit.dedent();
2169        self.emit.writeln("}");
2170    }
2171
2172    /// Generate a supervisor declaration.
2173    ///
2174    /// This generates a struct for the supervisor and doesn't generate handlers
2175    /// since the supervisor itself doesn't have handlers - it manages child agents.
2176    fn generate_supervisor(&mut self, supervisor: &SupervisorDecl, _program: &Program) {
2177        let name = &supervisor.name.name;
2178
2179        // Comment indicating this is a supervisor
2180        self.emit.write("// Supervisor: ");
2181        self.emit.writeln(name);
2182
2183        // Struct definition with visibility (just a marker struct)
2184        if supervisor.is_pub {
2185            self.emit.write("pub ");
2186        }
2187        self.emit.write("struct ");
2188        self.emit.write(name);
2189        self.emit.writeln(";");
2190    }
2191
2192    /// Generate the main function for a supervisor entry point.
2193    ///
2194    /// This creates a Supervisor instance, adds all child agents with their
2195    /// spawn functions and restart policies, then runs the supervisor.
2196    fn generate_supervisor_main(&mut self, supervisor: &SupervisorDecl, program: &Program) {
2197        let name = &supervisor.name.name;
2198
2199        self.emit.writeln("#[tokio::main]");
2200        self.emit
2201            .writeln("async fn main() -> Result<(), Box<dyn std::error::Error>> {");
2202        self.emit.indent();
2203
2204        // Initialize tracing with config
2205        self.emit_tracing_init();
2206        self.emit.writeln("");
2207
2208        // Create the supervisor with the configured strategy
2209        self.emit
2210            .write("let mut supervisor = Supervisor::new(Strategy::");
2211        match supervisor.strategy {
2212            SupervisionStrategy::OneForOne => self.emit.write("OneForOne"),
2213            SupervisionStrategy::OneForAll => self.emit.write("OneForAll"),
2214            SupervisionStrategy::RestForOne => self.emit.write("RestForOne"),
2215        }
2216        self.emit.writeln(&format!(
2217            ", RestartConfig {{ max_restarts: {}, within: std::time::Duration::from_secs({}) }});",
2218            self.config.supervision.max_restarts, self.config.supervision.within_seconds
2219        ));
2220        self.emit.writeln("");
2221
2222        // Add each child with its spawn function
2223        for child in &supervisor.children {
2224            let child_agent_name = &child.agent_name.name;
2225
2226            // Find the agent declaration to understand its structure
2227            let agent = program
2228                .agents
2229                .iter()
2230                .find(|a| a.name.name == *child_agent_name);
2231
2232            // Phase 3: Set current protocol roles for this child agent
2233            self.current_protocol_roles.clear();
2234            if let Some(agent) = agent {
2235                for pr in &agent.follows {
2236                    self.current_protocol_roles
2237                        .insert(pr.protocol.name.clone(), pr.role.name.clone());
2238                }
2239            }
2240
2241            // v2.0: Set current agent's @persistent beliefs (used by checkpoint() generation)
2242            self.current_agent_persistent_beliefs = if let Some(agent) = agent {
2243                agent
2244                    .beliefs
2245                    .iter()
2246                    .filter(|b| b.is_persistent)
2247                    .map(|b| b.name.name.clone())
2248                    .collect()
2249            } else {
2250                Vec::new()
2251            };
2252
2253            // Determine restart policy
2254            let restart_policy = match child.restart {
2255                RestartPolicy::Permanent => "Permanent",
2256                RestartPolicy::Transient => "Transient",
2257                RestartPolicy::Temporary => "Temporary",
2258            };
2259
2260            self.emit.write("supervisor.add_child(\"");
2261            self.emit.write(child_agent_name);
2262            self.emit.write("\", RestartPolicy::");
2263            self.emit.write(restart_policy);
2264            self.emit.writeln(", || {");
2265            self.emit.indent();
2266
2267            // Generate the spawn closure body
2268            self.emit.writeln("async {");
2269            self.emit.indent();
2270
2271            // Check if agent has tools, beliefs, or persistence
2272            let (has_tools, has_beliefs, has_persistent) = if let Some(agent) = agent {
2273                let has_tools = !agent.tool_uses.is_empty();
2274                let has_beliefs = !agent.beliefs.is_empty();
2275                let has_persistent = agent.beliefs.iter().any(|b| b.is_persistent);
2276                (has_tools, has_beliefs, has_persistent)
2277            } else {
2278                (false, false, false)
2279            };
2280
2281            // Initialize checkpoint store if needed
2282            if has_persistent {
2283                self.emit.writeln(
2284                    "let _checkpoint: std::sync::Arc<dyn CheckpointStore> = std::sync::Arc::new(",
2285                );
2286                self.emit.indent();
2287                self.emit_checkpoint_store_init();
2288                self.emit.dedent();
2289                self.emit.writeln(");");
2290                self.emit.write("let _checkpoint_key = \"");
2291                self.emit.write(child_agent_name);
2292                self.emit.writeln("\".to_string();");
2293            }
2294
2295            // Initialize async tools (like Database)
2296            if let Some(agent) = agent {
2297                for tool_use in &agent.tool_uses {
2298                    if tool_use.name == "Database" {
2299                        self.emit
2300                            .write("let _db_client = DatabaseClient::from_env().await");
2301                        self.emit
2302                            .writeln(".expect(\"Failed to connect to database\");");
2303                    }
2304                }
2305            }
2306
2307            // Construct the agent
2308            if has_tools || has_beliefs {
2309                self.emit.write("let agent = ");
2310                self.emit.write(child_agent_name);
2311                self.emit.writeln(" {");
2312                self.emit.indent();
2313
2314                // Add checkpoint fields if persistent
2315                if has_persistent {
2316                    self.emit
2317                        .writeln("_checkpoint: std::sync::Arc::clone(&_checkpoint),");
2318                    self.emit
2319                        .writeln("_checkpoint_key: _checkpoint_key.clone(),");
2320                }
2321
2322                // Add tool fields
2323                if let Some(agent) = agent {
2324                    for tool_use in &agent.tool_uses {
2325                        if tool_use.name == "Database" {
2326                            self.emit.write(&tool_use.name.to_lowercase());
2327                            self.emit.writeln(": _db_client,");
2328                        } else {
2329                            self.emit.write(&tool_use.name.to_lowercase());
2330                            self.emit.write(": ");
2331                            self.emit.write(&tool_use.name);
2332                            self.emit.writeln("Client::from_env(),");
2333                        }
2334                    }
2335                }
2336
2337                // Add belief fields from child spec's initial values
2338                if let Some(agent) = agent {
2339                    for belief in &agent.beliefs {
2340                        // Check if there's an initial value in child spec
2341                        let init_value = child
2342                            .beliefs
2343                            .iter()
2344                            .find(|f| f.name.name == belief.name.name);
2345
2346                        self.emit.write(&belief.name.name);
2347                        self.emit.write(": ");
2348
2349                        if belief.is_persistent {
2350                            if let Some(init) = init_value {
2351                                // Persisted with initial value from child spec
2352                                self.emit.write("Persisted::with_initial(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"");
2353                                self.emit.write(&belief.name.name);
2354                                self.emit.write("\", ");
2355                                self.generate_expr(&init.value);
2356                                self.emit.write(")");
2357                            } else {
2358                                // Persisted with default
2359                                self.emit.write("Persisted::new(std::sync::Arc::clone(&_checkpoint), &_checkpoint_key, \"");
2360                                self.emit.write(&belief.name.name);
2361                                self.emit.write("\")");
2362                            }
2363                        } else if let Some(init) = init_value {
2364                            // Non-persistent belief with initial value from child spec
2365                            self.generate_expr(&init.value);
2366                        } else {
2367                            // Non-persistent belief with default
2368                            self.emit.write("Default::default()");
2369                        }
2370                        self.emit.writeln(",");
2371                    }
2372                }
2373
2374                self.emit.dedent();
2375                self.emit.writeln("};");
2376            } else {
2377                self.emit.write("let agent = ");
2378                self.emit.write(child_agent_name);
2379                self.emit.writeln(";");
2380            }
2381
2382            // Check for waking handler
2383            let has_waking = agent
2384                .map(|a| {
2385                    a.handlers
2386                        .iter()
2387                        .any(|h| matches!(h.event, EventKind::Waking))
2388                })
2389                .unwrap_or(false);
2390
2391            // Check for error handler
2392            let has_error_handler = agent
2393                .map(|a| {
2394                    a.handlers
2395                        .iter()
2396                        .any(|h| matches!(h.event, EventKind::Error { .. }))
2397                })
2398                .unwrap_or(false);
2399
2400            // Check for stop handler
2401            let has_stop_handler = agent
2402                .map(|a| {
2403                    a.handlers
2404                        .iter()
2405                        .any(|h| matches!(h.event, EventKind::Stop | EventKind::Resting))
2406                })
2407                .unwrap_or(false);
2408
2409            // Phase 3: Check for Infer effect handler assignment
2410            let infer_handler = child
2411                .handler_assignments
2412                .iter()
2413                .find(|ha| ha.effect.name == "Infer")
2414                .map(|ha| ha.handler.name.clone());
2415
2416            // Create the agent handle and run it
2417            if let Some(handler_name) = &infer_handler {
2418                // Generate LlmConfig from handler
2419                self.emit
2420                    .writeln("// Phase 3: Use effect handler configuration");
2421                self.emit
2422                    .write("let llm_config = sage_runtime::LlmConfig::with_model(handler_");
2423                self.emit.write(&Self::to_snake_case(handler_name));
2424                self.emit.writeln("::CONFIG.model)");
2425                self.emit.indent();
2426                // Add temperature if configured
2427                self.emit.write(".with_temperature(handler_");
2428                self.emit.write(&Self::to_snake_case(handler_name));
2429                self.emit.writeln("::CONFIG.temperature)");
2430                // Add max_tokens if configured
2431                self.emit.write(".with_max_tokens(handler_");
2432                self.emit.write(&Self::to_snake_case(handler_name));
2433                self.emit.writeln("::CONFIG.max_tokens);");
2434                self.emit.dedent();
2435                self.emit.writeln(
2436                    "let handle = sage_runtime::spawn_with_llm_config(move |mut ctx| async move {",
2437                );
2438            } else {
2439                self.emit
2440                    .writeln("let handle = sage_runtime::spawn(move |mut ctx| async move {");
2441            }
2442            self.emit.indent();
2443
2444            // Call on_waking if present
2445            if has_waking {
2446                self.emit.writeln("agent.on_waking().await;");
2447            }
2448
2449            // Generate the main execution with error handling
2450            if has_error_handler {
2451                self.emit
2452                    .writeln("let result = match agent.on_start(&mut ctx).await {");
2453                self.emit.indent();
2454                self.emit.writeln("Ok(result) => Ok(result),");
2455                self.emit
2456                    .writeln("Err(e) => agent.on_error(e, &mut ctx).await,");
2457                self.emit.dedent();
2458                self.emit.writeln("};");
2459            } else {
2460                self.emit
2461                    .writeln("let result = agent.on_start(&mut ctx).await;");
2462            }
2463
2464            // Call on_stop if present
2465            if has_stop_handler {
2466                self.emit.writeln("agent.on_stop().await;");
2467            }
2468
2469            self.emit.writeln("result");
2470            self.emit.dedent();
2471            self.emit.writeln("});");
2472
2473            // Wait for the handle result, discarding the value and returning Ok(())
2474            self.emit
2475                .writeln("handle.result().await.map_err(|e| SageError::Agent(e.to_string()))?;");
2476            self.emit.writeln("Ok(())");
2477
2478            self.emit.dedent();
2479            self.emit.writeln("}");
2480            self.emit.dedent();
2481            self.emit.writeln("});");
2482            self.emit.writeln("");
2483        }
2484
2485        // Set up graceful shutdown signal handling
2486        self.emit
2487            .writeln("let ctrl_c = async { tokio::signal::ctrl_c().await.ok() };");
2488        self.emit.writeln("");
2489        self.emit.writeln("#[cfg(unix)]");
2490        self.emit.writeln("let terminate = async {");
2491        self.emit.indent();
2492        self.emit.writeln("if let Ok(mut s) = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {");
2493        self.emit.indent();
2494        self.emit.writeln("s.recv().await;");
2495        self.emit.dedent();
2496        self.emit.writeln("} else {");
2497        self.emit.indent();
2498        self.emit.writeln("std::future::pending::<()>().await;");
2499        self.emit.dedent();
2500        self.emit.writeln("}");
2501        self.emit.dedent();
2502        self.emit.writeln("};");
2503        self.emit.writeln("#[cfg(not(unix))]");
2504        self.emit
2505            .writeln("let terminate = std::future::pending::<()>();");
2506        self.emit.writeln("");
2507
2508        // Run the supervisor with signal handling
2509        let child_count = supervisor.children.len();
2510        self.emit.write("eprintln!(\"Starting supervisor '");
2511        self.emit.write(name);
2512        self.emit.write("' with ");
2513        self.emit.write(&child_count.to_string());
2514        self.emit.writeln(" children...\");");
2515        self.emit.writeln("");
2516        self.emit.writeln("let result = tokio::select! {");
2517        self.emit.indent();
2518        self.emit.writeln("result = supervisor.run() => result,");
2519        self.emit.writeln("_ = ctrl_c => {");
2520        self.emit.indent();
2521        self.emit
2522            .writeln("eprintln!(\"\\nReceived interrupt signal, shutting down supervisor...\");");
2523        self.emit.writeln("return Ok(());");
2524        self.emit.dedent();
2525        self.emit.writeln("}");
2526        self.emit.writeln("_ = terminate => {");
2527        self.emit.indent();
2528        self.emit
2529            .writeln("eprintln!(\"Received terminate signal, shutting down supervisor...\");");
2530        self.emit.writeln("return Ok(());");
2531        self.emit.dedent();
2532        self.emit.writeln("}");
2533        self.emit.dedent();
2534        self.emit.writeln("};");
2535        self.emit.writeln("");
2536        self.emit.writeln("if let Err(e) = result {");
2537        self.emit.indent();
2538        self.emit.writeln("eprintln!(\"Supervisor error: {}\", e);");
2539        self.emit.writeln("return Err(e.into());");
2540        self.emit.dedent();
2541        self.emit.writeln("}");
2542        self.emit.writeln("");
2543        self.emit.writeln("Ok(())");
2544
2545        self.emit.dedent();
2546        self.emit.writeln("}");
2547    }
2548
2549    // =========================================================================
2550    // Phase 3: Protocol generation (Session Types)
2551    // =========================================================================
2552
2553    /// Generate a protocol state machine module.
2554    ///
2555    /// For a protocol like:
2556    /// ```sage
2557    /// protocol PingPong {
2558    ///     Pinger -> Ponger: Ping
2559    ///     Ponger -> Pinger: Pong
2560    /// }
2561    /// ```
2562    ///
2563    /// We generate a module with:
2564    /// - An enum for the protocol states
2565    /// - An implementation of `ProtocolStateMachine` trait
2566    fn generate_protocol(&mut self, protocol: &ProtocolDecl) {
2567        let name = &protocol.name.name;
2568        let mod_name = Self::to_snake_case(name);
2569
2570        // Comment
2571        self.emit.write("// Protocol: ");
2572        self.emit.writeln(name);
2573
2574        // Open module
2575        if protocol.is_pub {
2576            self.emit.write("pub ");
2577        }
2578        self.emit.write("mod protocol_");
2579        self.emit.write(&mod_name);
2580        self.emit.writeln(" {");
2581        self.emit.indent();
2582
2583        self.emit.writeln("use super::*;");
2584        self.emit.blank_line();
2585
2586        // Generate state enum
2587        // States are: Initial, then one state after each step, plus Done
2588        self.emit
2589            .writeln("#[derive(Debug, Clone, Copy, PartialEq, Eq)]");
2590        self.emit.writeln("pub enum State {");
2591        self.emit.indent();
2592        self.emit.writeln("Initial,");
2593        for (i, step) in protocol.steps.iter().enumerate() {
2594            self.emit.write("After");
2595            self.emit.write(&Self::capitalize(&step.sender.name));
2596            self.emit.write("Sends");
2597            self.generate_type_name_for_state(&step.message_type);
2598            self.emit.write("_");
2599            self.emit.write(&i.to_string());
2600            self.emit.writeln(",");
2601        }
2602        self.emit.writeln("Done,");
2603        self.emit.dedent();
2604        self.emit.writeln("}");
2605        self.emit.blank_line();
2606
2607        // Generate Default impl
2608        self.emit.writeln("impl Default for State {");
2609        self.emit.indent();
2610        self.emit.writeln("fn default() -> Self {");
2611        self.emit.indent();
2612        self.emit.writeln("Self::Initial");
2613        self.emit.dedent();
2614        self.emit.writeln("}");
2615        self.emit.dedent();
2616        self.emit.writeln("}");
2617        self.emit.blank_line();
2618
2619        // Generate ProtocolStateMachine impl
2620        self.emit.writeln("impl ProtocolStateMachine for State {");
2621        self.emit.indent();
2622
2623        // state_name()
2624        self.emit.writeln("fn state_name(&self) -> &str {");
2625        self.emit.indent();
2626        self.emit.writeln("match self {");
2627        self.emit.indent();
2628        self.emit.writeln("State::Initial => \"Initial\",");
2629        for (i, step) in protocol.steps.iter().enumerate() {
2630            self.emit.write("State::After");
2631            self.emit.write(&Self::capitalize(&step.sender.name));
2632            self.emit.write("Sends");
2633            self.generate_type_name_for_state(&step.message_type);
2634            self.emit.write("_");
2635            self.emit.write(&i.to_string());
2636            self.emit.write(" => \"After");
2637            self.emit.write(&Self::capitalize(&step.sender.name));
2638            self.emit.write("Sends");
2639            self.generate_type_name_for_state(&step.message_type);
2640            self.emit.writeln("\",");
2641        }
2642        self.emit.writeln("State::Done => \"Done\",");
2643        self.emit.dedent();
2644        self.emit.writeln("}");
2645        self.emit.dedent();
2646        self.emit.writeln("}");
2647        self.emit.blank_line();
2648
2649        // can_send()
2650        self.emit
2651            .writeln("fn can_send(&self, msg_type: &str, from_role: &str) -> bool {");
2652        self.emit.indent();
2653        self.emit.writeln("match (self, msg_type, from_role) {");
2654        self.emit.indent();
2655        for (i, step) in protocol.steps.iter().enumerate() {
2656            let state_name = if i == 0 {
2657                "State::Initial".to_string()
2658            } else {
2659                let prev = &protocol.steps[i - 1];
2660                format!(
2661                    "State::After{}Sends{}_{}",
2662                    Self::capitalize(&prev.sender.name),
2663                    Self::type_name_for_state(&prev.message_type),
2664                    i - 1
2665                )
2666            };
2667            self.emit.write("(");
2668            self.emit.write(&state_name);
2669            self.emit.write(", \"");
2670            self.generate_type_name_for_state(&step.message_type);
2671            self.emit.write("\", \"");
2672            self.emit.write(&step.sender.name);
2673            self.emit.writeln("\") => true,");
2674        }
2675        self.emit.writeln("_ => false,");
2676        self.emit.dedent();
2677        self.emit.writeln("}");
2678        self.emit.dedent();
2679        self.emit.writeln("}");
2680        self.emit.blank_line();
2681
2682        // can_receive()
2683        self.emit
2684            .writeln("fn can_receive(&self, msg_type: &str, to_role: &str) -> bool {");
2685        self.emit.indent();
2686        self.emit.writeln("match (self, msg_type, to_role) {");
2687        self.emit.indent();
2688        for (i, step) in protocol.steps.iter().enumerate() {
2689            let state_name = if i == 0 {
2690                "State::Initial".to_string()
2691            } else {
2692                let prev = &protocol.steps[i - 1];
2693                format!(
2694                    "State::After{}Sends{}_{}",
2695                    Self::capitalize(&prev.sender.name),
2696                    Self::type_name_for_state(&prev.message_type),
2697                    i - 1
2698                )
2699            };
2700            self.emit.write("(");
2701            self.emit.write(&state_name);
2702            self.emit.write(", \"");
2703            self.generate_type_name_for_state(&step.message_type);
2704            self.emit.write("\", \"");
2705            self.emit.write(&step.receiver.name);
2706            self.emit.writeln("\") => true,");
2707        }
2708        self.emit.writeln("_ => false,");
2709        self.emit.dedent();
2710        self.emit.writeln("}");
2711        self.emit.dedent();
2712        self.emit.writeln("}");
2713        self.emit.blank_line();
2714
2715        // transition()
2716        self.emit
2717            .writeln("fn transition(&mut self, msg_type: &str) -> Result<(), ProtocolViolation> {");
2718        self.emit.indent();
2719        self.emit.writeln("let next = match (&self, msg_type) {");
2720        self.emit.indent();
2721        for (i, step) in protocol.steps.iter().enumerate() {
2722            let state_name = if i == 0 {
2723                "State::Initial".to_string()
2724            } else {
2725                let prev = &protocol.steps[i - 1];
2726                format!(
2727                    "State::After{}Sends{}_{}",
2728                    Self::capitalize(&prev.sender.name),
2729                    Self::type_name_for_state(&prev.message_type),
2730                    i - 1
2731                )
2732            };
2733            let next_state = if i + 1 >= protocol.steps.len() {
2734                "State::Done".to_string()
2735            } else {
2736                format!(
2737                    "State::After{}Sends{}_{}",
2738                    Self::capitalize(&step.sender.name),
2739                    Self::type_name_for_state(&step.message_type),
2740                    i
2741                )
2742            };
2743            self.emit.write("(");
2744            self.emit.write(&state_name);
2745            self.emit.write(", \"");
2746            self.generate_type_name_for_state(&step.message_type);
2747            self.emit.write("\") => ");
2748            self.emit.write(&next_state);
2749            self.emit.writeln(",");
2750        }
2751        self.emit
2752            .writeln("_ => return Err(ProtocolViolation::UnexpectedMessage {");
2753        self.emit.indent();
2754        self.emit.write("protocol: \"");
2755        self.emit.write(name);
2756        self.emit.writeln("\".to_string(),");
2757        self.emit.writeln("expected: \"unknown\".to_string(),");
2758        self.emit.writeln("received: msg_type.to_string(),");
2759        self.emit.writeln("state: self.state_name().to_string(),");
2760        self.emit.dedent();
2761        self.emit.writeln("}),");
2762        self.emit.dedent();
2763        self.emit.writeln("};");
2764        self.emit.writeln("*self = next;");
2765        self.emit.writeln("Ok(())");
2766        self.emit.dedent();
2767        self.emit.writeln("}");
2768        self.emit.blank_line();
2769
2770        // is_terminal()
2771        self.emit.writeln("fn is_terminal(&self) -> bool {");
2772        self.emit.indent();
2773        self.emit.writeln("matches!(self, State::Done)");
2774        self.emit.dedent();
2775        self.emit.writeln("}");
2776        self.emit.blank_line();
2777
2778        // protocol_name()
2779        self.emit.writeln("fn protocol_name(&self) -> &str {");
2780        self.emit.indent();
2781        self.emit.write("\"");
2782        self.emit.write(name);
2783        self.emit.writeln("\"");
2784        self.emit.dedent();
2785        self.emit.writeln("}");
2786        self.emit.blank_line();
2787
2788        // clone_box()
2789        self.emit
2790            .writeln("fn clone_box(&self) -> Box<dyn ProtocolStateMachine> {");
2791        self.emit.indent();
2792        self.emit.writeln("Box::new(*self)");
2793        self.emit.dedent();
2794        self.emit.writeln("}");
2795
2796        self.emit.dedent();
2797        self.emit.writeln("}"); // impl ProtocolStateMachine
2798
2799        // Close module
2800        self.emit.dedent();
2801        self.emit.writeln("}"); // mod
2802    }
2803
2804    /// Convert a name to snake_case.
2805    fn to_snake_case(name: &str) -> String {
2806        let mut result = String::new();
2807        for (i, c) in name.chars().enumerate() {
2808            if c.is_uppercase() {
2809                if i > 0 {
2810                    result.push('_');
2811                }
2812                result.push(c.to_ascii_lowercase());
2813            } else {
2814                result.push(c);
2815            }
2816        }
2817        result
2818    }
2819
2820    /// Capitalize the first letter of a name.
2821    fn capitalize(name: &str) -> String {
2822        let mut chars = name.chars();
2823        match chars.next() {
2824            None => String::new(),
2825            Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
2826        }
2827    }
2828
2829    /// Generate a type name suitable for state enum variant names.
2830    fn generate_type_name_for_state(&mut self, ty: &TypeExpr) {
2831        match ty {
2832            TypeExpr::Int => self.emit.write("Int"),
2833            TypeExpr::Float => self.emit.write("Float"),
2834            TypeExpr::String => self.emit.write("String"),
2835            TypeExpr::Bool => self.emit.write("Bool"),
2836            TypeExpr::Unit => self.emit.write("Unit"),
2837            TypeExpr::Named(ident, _) => self.emit.write(&ident.name),
2838            TypeExpr::List(_) => self.emit.write("List"),
2839            TypeExpr::Map(_, _) => self.emit.write("Map"),
2840            TypeExpr::Option(_) => self.emit.write("Option"),
2841            TypeExpr::Result(_, _) => self.emit.write("Result"),
2842            TypeExpr::Oracle(_) => self.emit.write("Oracle"),
2843            TypeExpr::Agent(_) => self.emit.write("Agent"),
2844            TypeExpr::Tuple(_) => self.emit.write("Tuple"),
2845            TypeExpr::Fn(_, _) => self.emit.write("Fn"),
2846            TypeExpr::Error => self.emit.write("Error"),
2847        }
2848    }
2849
2850    /// Get the type name as a string (for state generation).
2851    fn type_name_for_state(ty: &TypeExpr) -> String {
2852        match ty {
2853            TypeExpr::Int => "Int".to_string(),
2854            TypeExpr::Float => "Float".to_string(),
2855            TypeExpr::String => "String".to_string(),
2856            TypeExpr::Bool => "Bool".to_string(),
2857            TypeExpr::Unit => "Unit".to_string(),
2858            TypeExpr::Named(ident, _) => ident.name.clone(),
2859            TypeExpr::List(_) => "List".to_string(),
2860            TypeExpr::Map(_, _) => "Map".to_string(),
2861            TypeExpr::Option(_) => "Option".to_string(),
2862            TypeExpr::Result(_, _) => "Result".to_string(),
2863            TypeExpr::Oracle(_) => "Oracle".to_string(),
2864            TypeExpr::Agent(_) => "Agent".to_string(),
2865            TypeExpr::Tuple(_) => "Tuple".to_string(),
2866            TypeExpr::Fn(_, _) => "Fn".to_string(),
2867            TypeExpr::Error => "Error".to_string(),
2868        }
2869    }
2870
2871    // =========================================================================
2872    // Phase 3: Effect handler generation (Algebraic Effects)
2873    // =========================================================================
2874
2875    /// Generate an effect handler struct with configuration.
2876    ///
2877    /// For a handler like:
2878    /// ```sage
2879    /// handler DefaultLLM handles Infer {
2880    ///     model: "gpt-4o"
2881    ///     temperature: 0.7
2882    /// }
2883    /// ```
2884    ///
2885    /// We generate a struct with the configuration values.
2886    fn generate_effect_handler(&mut self, handler: &EffectHandlerDecl) {
2887        let name = &handler.name.name;
2888        let effect = &handler.effect.name;
2889
2890        // Comment
2891        self.emit.write("// Effect handler: ");
2892        self.emit.write(name);
2893        self.emit.write(" handles ");
2894        self.emit.writeln(effect);
2895
2896        // For Infer effect, generate an InferConfig struct
2897        if effect == "Infer" {
2898            // Generate a struct with the config
2899            if handler.is_pub {
2900                self.emit.write("pub ");
2901            }
2902            self.emit.write("mod handler_");
2903            self.emit.write(&Self::to_snake_case(name));
2904            self.emit.writeln(" {");
2905            self.emit.indent();
2906
2907            self.emit.writeln("#[derive(Debug, Clone)]");
2908            self.emit.writeln("pub struct Config {");
2909            self.emit.indent();
2910
2911            // Generate fields for each config entry
2912            for config in &handler.config {
2913                self.emit.write("pub ");
2914                self.emit.write(&config.key.name);
2915                self.emit.write(": ");
2916                // Infer type from literal
2917                match &config.value {
2918                    Literal::Int(_) => self.emit.write("i64"),
2919                    Literal::Float(_) => self.emit.write("f64"),
2920                    Literal::String(_) => self.emit.write("&'static str"),
2921                    Literal::Bool(_) => self.emit.write("bool"),
2922                }
2923                self.emit.writeln(",");
2924            }
2925
2926            self.emit.dedent();
2927            self.emit.writeln("}");
2928            self.emit.blank_line();
2929
2930            // Generate a const instance with the values
2931            self.emit.write("pub const CONFIG: Config = Config ");
2932            self.emit.writeln("{");
2933            self.emit.indent();
2934
2935            for config in &handler.config {
2936                self.emit.write(&config.key.name);
2937                self.emit.write(": ");
2938                match &config.value {
2939                    Literal::Int(n) => self.emit.write(&n.to_string()),
2940                    Literal::Float(f) => {
2941                        let s = f.to_string();
2942                        self.emit.write(&s);
2943                        if !s.contains('.') {
2944                            self.emit.write(".0");
2945                        }
2946                    }
2947                    Literal::String(s) => {
2948                        self.emit.write("\"");
2949                        self.emit.write(s);
2950                        self.emit.write("\"");
2951                    }
2952                    Literal::Bool(b) => self.emit.write(&b.to_string()),
2953                }
2954                self.emit.writeln(",");
2955            }
2956
2957            self.emit.dedent();
2958            self.emit.writeln("};");
2959
2960            self.emit.dedent();
2961            self.emit.writeln("}");
2962        } else {
2963            // For other effects, generate a placeholder
2964            self.emit.write("// TODO: Handler for effect '");
2965            self.emit.write(effect);
2966            self.emit.writeln("' not yet implemented");
2967        }
2968    }
2969
2970    fn generate_block(&mut self, block: &Block) {
2971        self.emit.open_brace();
2972        self.generate_block_contents(block);
2973        self.emit.close_brace();
2974    }
2975
2976    fn generate_block_inline(&mut self, block: &Block) {
2977        self.emit.open_brace();
2978        self.generate_block_contents(block);
2979        self.emit.close_brace_inline();
2980    }
2981
2982    fn generate_block_contents(&mut self, block: &Block) {
2983        // Collect variables that are reassigned in this block
2984        self.collect_reassigned_vars(block);
2985        for stmt in &block.stmts {
2986            self.generate_stmt(stmt);
2987        }
2988    }
2989
2990    fn generate_stmt(&mut self, stmt: &Stmt) {
2991        match stmt {
2992            Stmt::Let {
2993                name, ty, value, ..
2994            } => {
2995                // Only add mut if the variable is reassigned later
2996                if self.reassigned_vars.contains(&name.name) {
2997                    self.emit.write("let mut ");
2998                } else {
2999                    self.emit.write("let ");
3000                }
3001                if ty.is_some() {
3002                    self.emit.write(&name.name);
3003                    self.emit.write(": ");
3004                    self.emit_type(ty.as_ref().unwrap());
3005                } else {
3006                    self.emit.write(&name.name);
3007                }
3008                self.emit.write(" = ");
3009                self.generate_expr(value);
3010                self.emit.writeln(";");
3011            }
3012
3013            Stmt::Assign { name, value, .. } => {
3014                self.emit.write(&name.name);
3015                self.emit.write(" = ");
3016                self.generate_expr(value);
3017                self.emit.writeln(";");
3018            }
3019
3020            Stmt::Return { value, .. } => {
3021                self.emit.write("return ");
3022                if let Some(expr) = value {
3023                    self.generate_expr(expr);
3024                }
3025                self.emit.writeln(";");
3026            }
3027
3028            Stmt::If {
3029                condition,
3030                then_block,
3031                else_block,
3032                ..
3033            } => {
3034                self.emit.write("if ");
3035                self.generate_expr(condition);
3036                self.emit.write(" ");
3037                if else_block.is_some() {
3038                    self.generate_block_inline(then_block);
3039                    self.emit.write(" else ");
3040                    match else_block.as_ref().unwrap() {
3041                        sage_parser::ElseBranch::Block(block) => {
3042                            self.generate_block(block);
3043                        }
3044                        sage_parser::ElseBranch::ElseIf(stmt) => {
3045                            self.generate_stmt(stmt);
3046                        }
3047                    }
3048                } else {
3049                    self.generate_block(then_block);
3050                }
3051            }
3052
3053            Stmt::For {
3054                pattern,
3055                iter,
3056                body,
3057                ..
3058            } => {
3059                self.emit.write("for ");
3060                self.emit_pattern(pattern);
3061                self.emit.write(" in ");
3062                self.generate_expr(iter);
3063                self.emit.write(" ");
3064                self.generate_block(body);
3065            }
3066
3067            Stmt::While {
3068                condition, body, ..
3069            } => {
3070                self.emit.write("while ");
3071                self.generate_expr(condition);
3072                self.emit.write(" ");
3073                self.generate_block(body);
3074            }
3075
3076            Stmt::Loop { body, .. } => {
3077                self.emit.write("loop ");
3078                self.generate_block(body);
3079            }
3080
3081            Stmt::Break { .. } => {
3082                self.emit.writeln("break;");
3083            }
3084
3085            Stmt::SpanBlock { name, body, .. } => {
3086                // Generate a block that:
3087                // 1. Records start time
3088                // 2. Emits span_start event
3089                // 3. Runs body
3090                // 4. Emits span_end event with duration
3091                self.emit.writeln("{");
3092                self.emit.indent();
3093                self.emit.write("let __span_name = ");
3094                self.generate_expr(name);
3095                self.emit.writeln(";");
3096                self.emit
3097                    .writeln("let __span_start = std::time::Instant::now();");
3098                self.emit
3099                    .writeln("sage_runtime::trace::span_start(&__span_name);");
3100                // Generate body statements
3101                self.generate_block(body);
3102                self.emit.writeln(
3103                    "sage_runtime::trace::span_end(&__span_name, __span_start.elapsed().as_millis() as u64);",
3104                );
3105                self.emit.dedent();
3106                self.emit.writeln("}");
3107            }
3108
3109            Stmt::Checkpoint { .. } => {
3110                // Generate explicit checkpoint calls for each @persistent belief
3111                // This forces an immediate save of all @persistent beliefs
3112                if self.current_agent_persistent_beliefs.is_empty() {
3113                    // No persistent fields - checkpoint is a no-op
3114                    self.emit.writeln("// checkpoint() - no @persistent fields");
3115                } else {
3116                    self.emit
3117                        .writeln("// checkpoint() - save all @persistent beliefs");
3118                    for field_name in &self.current_agent_persistent_beliefs.clone() {
3119                        self.emit.write("self_");
3120                        self.emit.write(field_name);
3121                        self.emit.writeln(".checkpoint();");
3122                    }
3123                }
3124            }
3125
3126            Stmt::Expr { expr, .. } => {
3127                // Handle emit specially
3128                if let Expr::Yield { value, .. } = expr {
3129                    self.emit.write("return ctx.emit(");
3130                    self.generate_expr(value);
3131                    self.emit.writeln(");");
3132                } else if let Expr::Call { name, args, .. } = expr {
3133                    // Handle assertion builtins (needed for assertions inside span blocks)
3134                    if self.is_assertion_builtin(&name.name) {
3135                        self.generate_assertion(&name.name, args);
3136                    } else {
3137                        self.generate_expr(expr);
3138                        self.emit.writeln(";");
3139                    }
3140                } else {
3141                    self.generate_expr(expr);
3142                    self.emit.writeln(";");
3143                }
3144            }
3145
3146            Stmt::LetTuple { names, value, .. } => {
3147                self.emit.write("let (");
3148                for (i, name) in names.iter().enumerate() {
3149                    if i > 0 {
3150                        self.emit.write(", ");
3151                    }
3152                    self.emit.write(&name.name);
3153                }
3154                self.emit.write(") = ");
3155                self.generate_expr(value);
3156                self.emit.writeln(";");
3157            }
3158
3159            // RFC-0012: mock divine - codegen will be handled in test harness generation
3160            Stmt::MockDivine { value, .. } => {
3161                // Mock statements are collected during test codegen, not emitted inline
3162                // This placeholder ensures the match is exhaustive
3163                self.emit.write("// mock divine: ");
3164                match value {
3165                    sage_parser::MockValue::Value(expr) => {
3166                        self.generate_expr(expr);
3167                    }
3168                    sage_parser::MockValue::Fail(expr) => {
3169                        self.emit.write("fail(");
3170                        self.generate_expr(expr);
3171                        self.emit.write(")");
3172                    }
3173                }
3174                self.emit.writeln(";");
3175            }
3176
3177            // RFC-0012: mock tool - codegen will be handled in test harness generation
3178            Stmt::MockTool {
3179                tool_name,
3180                fn_name,
3181                value,
3182                ..
3183            } => {
3184                // Mock statements are collected during test codegen, not emitted inline
3185                // This placeholder ensures the match is exhaustive
3186                self.emit.write("// mock tool ");
3187                self.emit.write(&tool_name.name);
3188                self.emit.write(".");
3189                self.emit.write(&fn_name.name);
3190                self.emit.write(": ");
3191                match value {
3192                    sage_parser::MockValue::Value(expr) => {
3193                        self.generate_expr(expr);
3194                    }
3195                    sage_parser::MockValue::Fail(expr) => {
3196                        self.emit.write("fail(");
3197                        self.generate_expr(expr);
3198                        self.emit.write(")");
3199                    }
3200                }
3201                self.emit.writeln(";");
3202            }
3203        }
3204    }
3205
3206    fn generate_expr(&mut self, expr: &Expr) {
3207        match expr {
3208            Expr::Literal { value, .. } => {
3209                self.emit_literal(value);
3210            }
3211
3212            Expr::Var { name, .. } => {
3213                // Handle builtin constants (RFC-0013)
3214                match name.name.as_str() {
3215                    "PI" => self.emit.write("std::f64::consts::PI"),
3216                    "E" => self.emit.write("std::f64::consts::E"),
3217                    // Time constants
3218                    "MS_PER_SECOND" => self.emit.write("1000_i64"),
3219                    "MS_PER_MINUTE" => self.emit.write("60000_i64"),
3220                    "MS_PER_HOUR" => self.emit.write("3600000_i64"),
3221                    "MS_PER_DAY" => self.emit.write("86400000_i64"),
3222                    _ => {
3223                        self.emit.write(&name.name);
3224                        if self.string_consts.contains(&name.name) {
3225                            self.emit.write(".to_string()");
3226                        }
3227                    }
3228                }
3229            }
3230
3231            Expr::Binary {
3232                op, left, right, ..
3233            } => {
3234                // Handle string concatenation specially
3235                if matches!(op, BinOp::Concat) {
3236                    self.emit.write("format!(\"{}{}\", ");
3237                    self.generate_expr(left);
3238                    self.emit.write(", ");
3239                    self.generate_expr(right);
3240                    self.emit.write(")");
3241                } else {
3242                    self.generate_expr(left);
3243                    self.emit.write(" ");
3244                    self.emit_binop(op);
3245                    self.emit.write(" ");
3246                    self.generate_expr(right);
3247                }
3248            }
3249
3250            Expr::Unary { op, operand, .. } => {
3251                self.emit_unaryop(op);
3252                self.generate_expr(operand);
3253            }
3254
3255            Expr::Call {
3256                name,
3257                type_args,
3258                args,
3259                ..
3260            } => {
3261                let fn_name = &name.name;
3262
3263                // Handle builtins
3264                match fn_name.as_str() {
3265                    "print" => {
3266                        self.emit.write("println!(\"{}\", ");
3267                        self.generate_expr(&args[0]);
3268                        self.emit.write(")");
3269                    }
3270                    "str" => {
3271                        self.generate_expr(&args[0]);
3272                        self.emit.write(".to_string()");
3273                    }
3274                    "len" => {
3275                        self.generate_expr(&args[0]);
3276                        self.emit.write(".len() as i64");
3277                    }
3278
3279                    // RFC-0013: String functions
3280                    "split" => {
3281                        self.generate_expr(&args[0]);
3282                        self.emit.write(".split(&*");
3283                        self.generate_expr(&args[1]);
3284                        self.emit.write(").map(str::to_string).collect::<Vec<_>>()");
3285                    }
3286                    "lines" => {
3287                        self.generate_expr(&args[0]);
3288                        self.emit
3289                            .write(".lines().map(str::to_string).collect::<Vec<_>>()");
3290                    }
3291                    "chars" => {
3292                        self.generate_expr(&args[0]);
3293                        self.emit
3294                            .write(".chars().map(|c| c.to_string()).collect::<Vec<_>>()");
3295                    }
3296                    "join" => {
3297                        self.generate_expr(&args[0]);
3298                        self.emit.write(".join(&*");
3299                        self.generate_expr(&args[1]);
3300                        self.emit.write(")");
3301                    }
3302                    "trim" => {
3303                        self.generate_expr(&args[0]);
3304                        self.emit.write(".trim().to_string()");
3305                    }
3306                    "trim_start" => {
3307                        self.generate_expr(&args[0]);
3308                        self.emit.write(".trim_start().to_string()");
3309                    }
3310                    "trim_end" => {
3311                        self.generate_expr(&args[0]);
3312                        self.emit.write(".trim_end().to_string()");
3313                    }
3314                    "starts_with" => {
3315                        self.generate_expr(&args[0]);
3316                        self.emit.write(".starts_with(&*");
3317                        self.generate_expr(&args[1]);
3318                        self.emit.write(")");
3319                    }
3320                    "ends_with" => {
3321                        self.generate_expr(&args[0]);
3322                        self.emit.write(".ends_with(&*");
3323                        self.generate_expr(&args[1]);
3324                        self.emit.write(")");
3325                    }
3326                    "str_contains" => {
3327                        self.generate_expr(&args[0]);
3328                        self.emit.write(".contains(&*");
3329                        self.generate_expr(&args[1]);
3330                        self.emit.write(")");
3331                    }
3332                    "replace" => {
3333                        self.generate_expr(&args[0]);
3334                        self.emit.write(".replace(&*");
3335                        self.generate_expr(&args[1]);
3336                        self.emit.write(", &*");
3337                        self.generate_expr(&args[2]);
3338                        self.emit.write(")");
3339                    }
3340                    "replace_first" => {
3341                        self.generate_expr(&args[0]);
3342                        self.emit.write(".replacen(&*");
3343                        self.generate_expr(&args[1]);
3344                        self.emit.write(", &*");
3345                        self.generate_expr(&args[2]);
3346                        self.emit.write(", 1)");
3347                    }
3348                    "to_upper" => {
3349                        self.generate_expr(&args[0]);
3350                        self.emit.write(".to_uppercase()");
3351                    }
3352                    "to_lower" => {
3353                        self.generate_expr(&args[0]);
3354                        self.emit.write(".to_lowercase()");
3355                    }
3356                    "str_len" => {
3357                        self.generate_expr(&args[0]);
3358                        self.emit.write(".chars().count() as i64");
3359                    }
3360                    "str_slice" => {
3361                        self.emit.write("sage_runtime::stdlib::str_slice(&");
3362                        self.generate_expr(&args[0]);
3363                        self.emit.write(", ");
3364                        self.generate_expr(&args[1]);
3365                        self.emit.write(", ");
3366                        self.generate_expr(&args[2]);
3367                        self.emit.write(")");
3368                    }
3369                    "str_index_of" => {
3370                        self.emit.write("sage_runtime::stdlib::str_index_of(&");
3371                        self.generate_expr(&args[0]);
3372                        self.emit.write(", &");
3373                        self.generate_expr(&args[1]);
3374                        self.emit.write(")");
3375                    }
3376                    "str_repeat" => {
3377                        self.generate_expr(&args[0]);
3378                        self.emit.write(".repeat(");
3379                        self.generate_expr(&args[1]);
3380                        self.emit.write(" as usize)");
3381                    }
3382                    "str_pad_start" => {
3383                        self.emit.write("sage_runtime::stdlib::str_pad_start(&");
3384                        self.generate_expr(&args[0]);
3385                        self.emit.write(", ");
3386                        self.generate_expr(&args[1]);
3387                        self.emit.write(", &");
3388                        self.generate_expr(&args[2]);
3389                        self.emit.write(")");
3390                    }
3391                    "str_pad_end" => {
3392                        self.emit.write("sage_runtime::stdlib::str_pad_end(&");
3393                        self.generate_expr(&args[0]);
3394                        self.emit.write(", ");
3395                        self.generate_expr(&args[1]);
3396                        self.emit.write(", &");
3397                        self.generate_expr(&args[2]);
3398                        self.emit.write(")");
3399                    }
3400
3401                    "chr" => {
3402                        self.emit.write("sage_runtime::stdlib::chr(");
3403                        self.generate_expr(&args[0]);
3404                        self.emit.write(")");
3405                    }
3406
3407                    // RFC-0013: Math functions
3408                    "abs" => {
3409                        self.emit.write("(");
3410                        self.generate_expr(&args[0]);
3411                        self.emit.write(").abs()");
3412                    }
3413                    "abs_float" => {
3414                        self.emit.write("(");
3415                        self.generate_expr(&args[0]);
3416                        self.emit.write(").abs()");
3417                    }
3418                    "min" => {
3419                        self.generate_expr(&args[0]);
3420                        self.emit.write(".min(");
3421                        self.generate_expr(&args[1]);
3422                        self.emit.write(")");
3423                    }
3424                    "max" => {
3425                        self.generate_expr(&args[0]);
3426                        self.emit.write(".max(");
3427                        self.generate_expr(&args[1]);
3428                        self.emit.write(")");
3429                    }
3430                    "min_float" => {
3431                        self.generate_expr(&args[0]);
3432                        self.emit.write(".min(");
3433                        self.generate_expr(&args[1]);
3434                        self.emit.write(")");
3435                    }
3436                    "max_float" => {
3437                        self.generate_expr(&args[0]);
3438                        self.emit.write(".max(");
3439                        self.generate_expr(&args[1]);
3440                        self.emit.write(")");
3441                    }
3442                    "clamp" => {
3443                        self.emit.write("(");
3444                        self.generate_expr(&args[0]);
3445                        self.emit.write(").clamp(");
3446                        self.generate_expr(&args[1]);
3447                        self.emit.write(", ");
3448                        self.generate_expr(&args[2]);
3449                        self.emit.write(")");
3450                    }
3451                    "clamp_float" => {
3452                        self.emit.write("(");
3453                        self.generate_expr(&args[0]);
3454                        self.emit.write(").clamp(");
3455                        self.generate_expr(&args[1]);
3456                        self.emit.write(", ");
3457                        self.generate_expr(&args[2]);
3458                        self.emit.write(")");
3459                    }
3460                    "floor" => {
3461                        self.generate_expr(&args[0]);
3462                        self.emit.write(".floor() as i64");
3463                    }
3464                    "ceil" => {
3465                        self.generate_expr(&args[0]);
3466                        self.emit.write(".ceil() as i64");
3467                    }
3468                    "round" => {
3469                        self.generate_expr(&args[0]);
3470                        self.emit.write(".round() as i64");
3471                    }
3472                    "floor_float" => {
3473                        self.generate_expr(&args[0]);
3474                        self.emit.write(".floor()");
3475                    }
3476                    "ceil_float" => {
3477                        self.generate_expr(&args[0]);
3478                        self.emit.write(".ceil()");
3479                    }
3480                    "pow" => {
3481                        // Safe power: handle negative exponents by returning 0
3482                        self.emit.write("{ let __base = ");
3483                        self.generate_expr(&args[0]);
3484                        self.emit.write("; let __exp = ");
3485                        self.generate_expr(&args[1]);
3486                        self.emit
3487                            .write("; if __exp < 0 { 0 } else { __base.pow(__exp as u32) } }");
3488                    }
3489                    "pow_float" => {
3490                        self.generate_expr(&args[0]);
3491                        self.emit.write(".powf(");
3492                        self.generate_expr(&args[1]);
3493                        self.emit.write(")");
3494                    }
3495                    "sqrt" => {
3496                        self.generate_expr(&args[0]);
3497                        self.emit.write(".sqrt()");
3498                    }
3499                    "int_to_float" => {
3500                        self.generate_expr(&args[0]);
3501                        self.emit.write(" as f64");
3502                    }
3503                    "float_to_int" => {
3504                        self.generate_expr(&args[0]);
3505                        self.emit.write(" as i64");
3506                    }
3507
3508                    // RFC-0013: Parsing functions
3509                    "parse_int" => {
3510                        self.generate_expr(&args[0]);
3511                        self.emit
3512                            .write(".trim().parse::<i64>().map_err(|e| e.to_string())");
3513                    }
3514                    "parse_float" => {
3515                        self.generate_expr(&args[0]);
3516                        self.emit
3517                            .write(".trim().parse::<f64>().map_err(|e| e.to_string())");
3518                    }
3519                    "parse_bool" => {
3520                        self.emit.write("sage_runtime::stdlib::parse_bool(&");
3521                        self.generate_expr(&args[0]);
3522                        self.emit.write(")");
3523                    }
3524                    "float_to_str" => {
3525                        self.generate_expr(&args[0]);
3526                        self.emit.write(".to_string()");
3527                    }
3528                    "bool_to_str" => {
3529                        self.emit.write("if ");
3530                        self.generate_expr(&args[0]);
3531                        self.emit
3532                            .write(" { \"true\".to_string() } else { \"false\".to_string() }");
3533                    }
3534                    "int_to_str" => {
3535                        self.emit.write("(");
3536                        self.generate_expr(&args[0]);
3537                        self.emit.write(").to_string()");
3538                    }
3539
3540                    // RFC-0013: List Higher-Order Functions
3541                    "map" => {
3542                        self.generate_expr(&args[0]);
3543                        self.emit.write(".into_iter().map(");
3544                        self.generate_expr(&args[1]);
3545                        self.emit.write(").collect::<Vec<_>>()");
3546                    }
3547                    "filter" => {
3548                        self.generate_expr(&args[0]);
3549                        self.emit.write(".into_iter().filter(|__x| (");
3550                        self.generate_expr(&args[1]);
3551                        self.emit.write(")((__x).clone())).collect::<Vec<_>>()");
3552                    }
3553                    "reduce" => {
3554                        self.generate_expr(&args[0]);
3555                        self.emit.write(".into_iter().fold(");
3556                        self.generate_expr(&args[1]);
3557                        self.emit.write(", ");
3558                        self.generate_expr(&args[2]);
3559                        self.emit.write(")");
3560                    }
3561                    "any" => {
3562                        self.generate_expr(&args[0]);
3563                        self.emit.write(".into_iter().any(|__x| (");
3564                        self.generate_expr(&args[1]);
3565                        self.emit.write(")((__x).clone()))");
3566                    }
3567                    "all" => {
3568                        self.generate_expr(&args[0]);
3569                        self.emit.write(".into_iter().all(|__x| (");
3570                        self.generate_expr(&args[1]);
3571                        self.emit.write(")((__x).clone()))");
3572                    }
3573                    "find" => {
3574                        self.generate_expr(&args[0]);
3575                        self.emit.write(".into_iter().find(|__x| (");
3576                        self.generate_expr(&args[1]);
3577                        self.emit.write(")((__x).clone()))");
3578                    }
3579                    "flat_map" => {
3580                        self.generate_expr(&args[0]);
3581                        self.emit.write(".into_iter().flat_map(");
3582                        self.generate_expr(&args[1]);
3583                        self.emit.write(").collect::<Vec<_>>()");
3584                    }
3585                    "zip" => {
3586                        self.generate_expr(&args[0]);
3587                        self.emit.write(".into_iter().zip(");
3588                        self.generate_expr(&args[1]);
3589                        self.emit.write(".into_iter()).collect::<Vec<_>>()");
3590                    }
3591                    "sort_by" => {
3592                        self.emit.write("{ let mut __v = ");
3593                        self.generate_expr(&args[0]);
3594                        self.emit.write("; __v.sort_by(|__a, __b| { let __cmp = (");
3595                        self.generate_expr(&args[1]);
3596                        self.emit.write(")((__a).clone(), (__b).clone()); if __cmp < 0 { std::cmp::Ordering::Less } else if __cmp > 0 { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Equal } }); __v }");
3597                    }
3598                    "enumerate" => {
3599                        self.generate_expr(&args[0]);
3600                        self.emit
3601                            .write(".into_iter().enumerate().map(|(__i, __x)| (__i as i64, __x)).collect::<Vec<_>>()");
3602                    }
3603                    "take" => {
3604                        self.generate_expr(&args[0]);
3605                        self.emit.write(".into_iter().take(");
3606                        self.generate_expr(&args[1]);
3607                        self.emit.write(" as usize).collect::<Vec<_>>()");
3608                    }
3609                    "drop" => {
3610                        self.generate_expr(&args[0]);
3611                        self.emit.write(".into_iter().skip(");
3612                        self.generate_expr(&args[1]);
3613                        self.emit.write(" as usize).collect::<Vec<_>>()");
3614                    }
3615                    "flatten" => {
3616                        self.generate_expr(&args[0]);
3617                        self.emit
3618                            .write(".into_iter().flatten().collect::<Vec<_>>()");
3619                    }
3620                    "reverse" => {
3621                        self.emit.write("{ let mut __v = ");
3622                        self.generate_expr(&args[0]);
3623                        self.emit.write("; __v.reverse(); __v }");
3624                    }
3625                    "unique" => {
3626                        self.emit
3627                            .write("{ let mut __seen = std::collections::HashSet::new(); ");
3628                        self.generate_expr(&args[0]);
3629                        self.emit.write(".into_iter().filter(|__x| __seen.insert(format!(\"{:?}\", __x))).collect::<Vec<_>>() }");
3630                    }
3631                    "count_where" => {
3632                        self.generate_expr(&args[0]);
3633                        self.emit.write(".into_iter().filter(|__x| (");
3634                        self.generate_expr(&args[1]);
3635                        self.emit.write(")((__x).clone())).count() as i64");
3636                    }
3637                    "sum" => {
3638                        self.generate_expr(&args[0]);
3639                        self.emit.write(".iter().sum::<i64>()");
3640                    }
3641                    "sum_floats" => {
3642                        self.generate_expr(&args[0]);
3643                        self.emit.write(".iter().sum::<f64>()");
3644                    }
3645
3646                    // =========================================================================
3647                    // RFC-0010: List Utilities
3648                    // =========================================================================
3649                    "range" => {
3650                        self.emit.write("(");
3651                        self.generate_expr(&args[0]);
3652                        self.emit.write("..");
3653                        self.generate_expr(&args[1]);
3654                        self.emit.write(").collect::<Vec<_>>()");
3655                    }
3656                    "range_step" => {
3657                        self.emit.write("(");
3658                        self.generate_expr(&args[0]);
3659                        self.emit.write("..");
3660                        self.generate_expr(&args[1]);
3661                        self.emit.write(").step_by(");
3662                        self.generate_expr(&args[2]);
3663                        self.emit.write(" as usize).collect::<Vec<_>>()");
3664                    }
3665                    "first" => {
3666                        self.generate_expr(&args[0]);
3667                        self.emit.write(".first().cloned()");
3668                    }
3669                    "last" => {
3670                        self.generate_expr(&args[0]);
3671                        self.emit.write(".last().cloned()");
3672                    }
3673                    "get" => {
3674                        self.generate_expr(&args[0]);
3675                        self.emit.write(".get(");
3676                        self.generate_expr(&args[1]);
3677                        self.emit.write(" as usize).cloned()");
3678                    }
3679                    "list_contains" => {
3680                        self.generate_expr(&args[0]);
3681                        self.emit.write(".contains(&");
3682                        self.generate_expr(&args[1]);
3683                        self.emit.write(")");
3684                    }
3685                    "sort" => {
3686                        self.emit.write("{ let mut __v = ");
3687                        self.generate_expr(&args[0]);
3688                        self.emit.write("; __v.sort(); __v }");
3689                    }
3690                    "list_slice" => {
3691                        self.emit.write("sage_runtime::stdlib::list_slice(");
3692                        self.generate_expr(&args[0]);
3693                        self.emit.write(", ");
3694                        self.generate_expr(&args[1]);
3695                        self.emit.write(", ");
3696                        self.generate_expr(&args[2]);
3697                        self.emit.write(")");
3698                    }
3699                    "chunk" => {
3700                        self.generate_expr(&args[0]);
3701                        self.emit.write(".chunks(");
3702                        self.generate_expr(&args[1]);
3703                        self.emit
3704                            .write(" as usize).map(|c| c.to_vec()).collect::<Vec<_>>()");
3705                    }
3706                    "pop" => {
3707                        self.emit.write("{ let mut __v = ");
3708                        self.generate_expr(&args[0]);
3709                        self.emit.write("; __v.pop() }");
3710                    }
3711                    "push" => {
3712                        self.emit.write("{ let mut __v = ");
3713                        self.generate_expr(&args[0]);
3714                        self.emit.write("; __v.push(");
3715                        self.generate_expr(&args[1]);
3716                        self.emit.write("); __v }");
3717                    }
3718                    "concat" => {
3719                        self.emit.write("{ let mut __v = ");
3720                        self.generate_expr(&args[0]);
3721                        self.emit.write("; __v.extend(");
3722                        self.generate_expr(&args[1]);
3723                        self.emit.write("); __v }");
3724                    }
3725                    "take_while" => {
3726                        self.generate_expr(&args[0]);
3727                        self.emit.write(".into_iter().take_while(|__x| (");
3728                        self.generate_expr(&args[1]);
3729                        self.emit.write(")((__x).clone())).collect::<Vec<_>>()");
3730                    }
3731                    "drop_while" => {
3732                        self.generate_expr(&args[0]);
3733                        self.emit.write(".into_iter().skip_while(|__x| (");
3734                        self.generate_expr(&args[1]);
3735                        self.emit.write(")((__x).clone())).collect::<Vec<_>>()");
3736                    }
3737
3738                    // =========================================================================
3739                    // RFC-0010: Option Utilities
3740                    // =========================================================================
3741                    "is_some" => {
3742                        self.generate_expr(&args[0]);
3743                        self.emit.write(".is_some()");
3744                    }
3745                    "is_none" => {
3746                        self.generate_expr(&args[0]);
3747                        self.emit.write(".is_none()");
3748                    }
3749                    "unwrap" => {
3750                        self.generate_expr(&args[0]);
3751                        self.emit.write(".expect(\"unwrap called on None\")");
3752                    }
3753                    "unwrap_or" => {
3754                        self.generate_expr(&args[0]);
3755                        self.emit.write(".unwrap_or(");
3756                        self.generate_expr(&args[1]);
3757                        self.emit.write(")");
3758                    }
3759                    "unwrap_or_else" => {
3760                        self.generate_expr(&args[0]);
3761                        self.emit.write(".unwrap_or_else(");
3762                        self.generate_expr(&args[1]);
3763                        self.emit.write(")");
3764                    }
3765                    "map_option" => {
3766                        self.generate_expr(&args[0]);
3767                        self.emit.write(".map(");
3768                        self.generate_expr(&args[1]);
3769                        self.emit.write(")");
3770                    }
3771                    "or_option" => {
3772                        self.generate_expr(&args[0]);
3773                        self.emit.write(".or(");
3774                        self.generate_expr(&args[1]);
3775                        self.emit.write(")");
3776                    }
3777
3778                    // =========================================================================
3779                    // RFC-0010: Result Utilities
3780                    // =========================================================================
3781                    "is_ok" => {
3782                        self.generate_expr(&args[0]);
3783                        self.emit.write(".is_ok()");
3784                    }
3785                    "is_err" => {
3786                        self.generate_expr(&args[0]);
3787                        self.emit.write(".is_err()");
3788                    }
3789                    "unwrap_result" => {
3790                        self.generate_expr(&args[0]);
3791                        self.emit.write(".expect(\"unwrap_result called on Err\")");
3792                    }
3793                    "unwrap_err" => {
3794                        self.generate_expr(&args[0]);
3795                        self.emit.write(".expect_err(\"unwrap_err called on Ok\")");
3796                    }
3797                    "unwrap_or_result" => {
3798                        self.generate_expr(&args[0]);
3799                        self.emit.write(".unwrap_or(");
3800                        self.generate_expr(&args[1]);
3801                        self.emit.write(")");
3802                    }
3803                    "map_result" => {
3804                        self.generate_expr(&args[0]);
3805                        self.emit.write(".map(");
3806                        self.generate_expr(&args[1]);
3807                        self.emit.write(")");
3808                    }
3809                    "map_err" => {
3810                        self.generate_expr(&args[0]);
3811                        self.emit.write(".map_err(");
3812                        self.generate_expr(&args[1]);
3813                        self.emit.write(")");
3814                    }
3815                    "ok" => {
3816                        self.generate_expr(&args[0]);
3817                        self.emit.write(".ok()");
3818                    }
3819                    "err_value" => {
3820                        self.generate_expr(&args[0]);
3821                        self.emit.write(".err()");
3822                    }
3823
3824                    // =========================================================================
3825                    // RFC-0010: I/O Functions
3826                    // =========================================================================
3827                    "read_file" => {
3828                        self.emit.write("sage_runtime::stdlib::read_file(&");
3829                        self.generate_expr(&args[0]);
3830                        self.emit
3831                            .write(").map_err(sage_runtime::SageError::agent)?");
3832                    }
3833                    "write_file" => {
3834                        self.emit.write("sage_runtime::stdlib::write_file(&");
3835                        self.generate_expr(&args[0]);
3836                        self.emit.write(", &");
3837                        self.generate_expr(&args[1]);
3838                        self.emit
3839                            .write(").map_err(sage_runtime::SageError::agent)?");
3840                    }
3841                    "append_file" => {
3842                        self.emit.write("sage_runtime::stdlib::append_file(&");
3843                        self.generate_expr(&args[0]);
3844                        self.emit.write(", &");
3845                        self.generate_expr(&args[1]);
3846                        self.emit
3847                            .write(").map_err(sage_runtime::SageError::agent)?");
3848                    }
3849                    "file_exists" => {
3850                        self.emit.write("sage_runtime::stdlib::file_exists(&");
3851                        self.generate_expr(&args[0]);
3852                        self.emit.write(")");
3853                    }
3854                    "delete_file" => {
3855                        self.emit.write("sage_runtime::stdlib::delete_file(&");
3856                        self.generate_expr(&args[0]);
3857                        self.emit
3858                            .write(").map_err(sage_runtime::SageError::agent)?");
3859                    }
3860                    "list_dir" => {
3861                        self.emit.write("sage_runtime::stdlib::list_dir(&");
3862                        self.generate_expr(&args[0]);
3863                        self.emit
3864                            .write(").map_err(sage_runtime::SageError::agent)?");
3865                    }
3866                    "make_dir" => {
3867                        self.emit.write("sage_runtime::stdlib::make_dir(&");
3868                        self.generate_expr(&args[0]);
3869                        self.emit
3870                            .write(").map_err(sage_runtime::SageError::agent)?");
3871                    }
3872                    "read_line" => {
3873                        self.emit.write("sage_runtime::stdlib::read_line().map_err(sage_runtime::SageError::agent)");
3874                    }
3875                    "read_all" => {
3876                        self.emit.write("sage_runtime::stdlib::read_all().map_err(sage_runtime::SageError::agent)");
3877                    }
3878                    "print_err" => {
3879                        self.emit.write("eprintln!(\"{}\", ");
3880                        self.generate_expr(&args[0]);
3881                        self.emit.write(")");
3882                    }
3883
3884                    // =========================================================================
3885                    // RFC-0010: Time Functions
3886                    // =========================================================================
3887                    "now_ms" => {
3888                        self.emit.write("sage_runtime::stdlib::now_ms()");
3889                    }
3890                    "now_s" => {
3891                        self.emit.write("sage_runtime::stdlib::now_s()");
3892                    }
3893                    "sleep_ms" => {
3894                        self.emit
3895                            .write("tokio::time::sleep(std::time::Duration::from_millis(");
3896                        self.generate_expr(&args[0]);
3897                        self.emit.write(" as u64)).await");
3898                    }
3899                    "format_timestamp" => {
3900                        self.emit.write("sage_runtime::stdlib::format_timestamp(");
3901                        self.generate_expr(&args[0]);
3902                        self.emit.write(", &");
3903                        self.generate_expr(&args[1]);
3904                        self.emit.write(")");
3905                    }
3906                    "parse_timestamp" => {
3907                        self.emit.write("sage_runtime::stdlib::parse_timestamp(&");
3908                        self.generate_expr(&args[0]);
3909                        self.emit.write(", &");
3910                        self.generate_expr(&args[1]);
3911                        self.emit
3912                            .write(").map_err(sage_runtime::SageError::agent)?");
3913                    }
3914
3915                    // =========================================================================
3916                    // RFC-0010: JSON Utilities
3917                    // =========================================================================
3918                    "json_parse" => {
3919                        self.emit.write("sage_runtime::stdlib::json_parse(&");
3920                        self.generate_expr(&args[0]);
3921                        self.emit
3922                            .write(").map_err(sage_runtime::SageError::agent)?");
3923                    }
3924                    "json_get" => {
3925                        self.emit.write("sage_runtime::stdlib::json_get(&");
3926                        self.generate_expr(&args[0]);
3927                        self.emit.write(", &");
3928                        self.generate_expr(&args[1]);
3929                        self.emit.write(")");
3930                    }
3931                    "json_get_int" => {
3932                        self.emit.write("sage_runtime::stdlib::json_get_int(&");
3933                        self.generate_expr(&args[0]);
3934                        self.emit.write(", &");
3935                        self.generate_expr(&args[1]);
3936                        self.emit.write(")");
3937                    }
3938                    "json_get_float" => {
3939                        self.emit.write("sage_runtime::stdlib::json_get_float(&");
3940                        self.generate_expr(&args[0]);
3941                        self.emit.write(", &");
3942                        self.generate_expr(&args[1]);
3943                        self.emit.write(")");
3944                    }
3945                    "json_get_bool" => {
3946                        self.emit.write("sage_runtime::stdlib::json_get_bool(&");
3947                        self.generate_expr(&args[0]);
3948                        self.emit.write(", &");
3949                        self.generate_expr(&args[1]);
3950                        self.emit.write(")");
3951                    }
3952                    "json_get_list" => {
3953                        self.emit.write("sage_runtime::stdlib::json_get_list(&");
3954                        self.generate_expr(&args[0]);
3955                        self.emit.write(", &");
3956                        self.generate_expr(&args[1]);
3957                        self.emit.write(")");
3958                    }
3959                    "json_stringify" => {
3960                        self.emit
3961                            .write("sage_runtime::stdlib::json_stringify_string(&");
3962                        self.generate_expr(&args[0]);
3963                        self.emit.write(".to_string())");
3964                    }
3965
3966                    name if self.extern_fn_names.contains(name) => {
3967                        // Extern function — call into sage_extern module
3968                        self.emit.write("sage_extern::");
3969                        self.emit.write(name);
3970                        self.emit.write("(");
3971                        for (i, arg) in args.iter().enumerate() {
3972                            if i > 0 {
3973                                self.emit.write(", ");
3974                            }
3975                            // Clone arguments to match user-defined fn convention
3976                            self.emit.write("(");
3977                            self.generate_expr(arg);
3978                            self.emit.write(").clone()");
3979                        }
3980                        self.emit.write(")");
3981                        // Fallible extern fns need error conversion
3982                        if self.extern_fn_fallible.contains(name) {
3983                            self.emit.write(".map_err(sage_runtime::SageError::agent)?");
3984                        }
3985                    }
3986
3987                    _ => {
3988                        // User-defined function call
3989                        self.emit.write(fn_name);
3990                        // RFC-0015: Emit type arguments if provided (turbofish syntax)
3991                        if !type_args.is_empty() {
3992                            self.emit.write("::<");
3993                            for (i, arg) in type_args.iter().enumerate() {
3994                                if i > 0 {
3995                                    self.emit.write(", ");
3996                                }
3997                                self.emit_type(arg);
3998                            }
3999                            self.emit.write(">");
4000                        }
4001                        self.emit.write("(");
4002                        for (i, arg) in args.iter().enumerate() {
4003                            if i > 0 {
4004                                self.emit.write(", ");
4005                            }
4006                            // Clone arguments to avoid move issues with Strings
4007                            // (compiler optimizes away unnecessary clones for Copy types)
4008                            self.emit.write("(");
4009                            self.generate_expr(arg);
4010                            self.emit.write(").clone()");
4011                        }
4012                        self.emit.write(")");
4013                    }
4014                }
4015            }
4016
4017            Expr::SelfField { field, .. } => {
4018                self.emit.write("self.");
4019                self.emit.write(&field.name);
4020            }
4021
4022            Expr::Apply { callee, args, .. } => {
4023                // Generate callee expression (usually a FieldAccess for method calls)
4024                self.generate_expr(callee);
4025                self.emit.write("(");
4026                for (i, arg) in args.iter().enumerate() {
4027                    if i > 0 {
4028                        self.emit.write(", ");
4029                    }
4030                    self.generate_expr(arg);
4031                }
4032                self.emit.write(")");
4033            }
4034
4035            Expr::SelfMethodCall { method, args, .. } => {
4036                self.emit.write("self.");
4037                self.emit.write(&method.name);
4038                self.emit.write("(");
4039                for (i, arg) in args.iter().enumerate() {
4040                    if i > 0 {
4041                        self.emit.write(", ");
4042                    }
4043                    self.generate_expr(arg);
4044                }
4045                self.emit.write(")");
4046            }
4047
4048            Expr::List { elements, .. } => {
4049                self.emit.write("vec![");
4050                for (i, elem) in elements.iter().enumerate() {
4051                    if i > 0 {
4052                        self.emit.write(", ");
4053                    }
4054                    self.generate_expr(elem);
4055                }
4056                self.emit.write("]");
4057            }
4058
4059            Expr::Paren { inner, .. } => {
4060                self.emit.write("(");
4061                self.generate_expr(inner);
4062                self.emit.write(")");
4063            }
4064
4065            Expr::Divine { template, .. } => {
4066                // Note: No ? here - the try wrapper is responsible for error propagation
4067                self.emit.write("ctx.infer_string(&");
4068                self.emit_string_template(template);
4069                self.emit.write(").await");
4070            }
4071
4072            Expr::Summon { agent, fields, .. } => {
4073                let has_error_handler = self.agents_with_error_handlers.contains(&agent.name);
4074                let message_handlers = self.agents_with_message_handlers.get(&agent.name).cloned();
4075                let tool_uses = self
4076                    .agent_tool_uses
4077                    .get(&agent.name)
4078                    .cloned()
4079                    .unwrap_or_default();
4080
4081                // Wrap in a block so we can emit clone bindings before the spawn
4082                self.emit.write("{ ");
4083                // Pre-clone field values to avoid capturing self/moving loop vars
4084                for (i, field) in fields.iter().enumerate() {
4085                    self.emit.write(&format!("let __sf{} = (", i));
4086                    self.generate_expr(&field.value);
4087                    self.emit.write(").clone(); ");
4088                }
4089
4090                self.emit
4091                    .write("sage_runtime::spawn(move |mut ctx| async move { ");
4092                self.emit.write("let agent = ");
4093                self.emit.write(&agent.name);
4094                // Always use struct literal syntax when there are fields or tool uses
4095                if fields.is_empty() && tool_uses.is_empty() {
4096                    self.emit.write("; ");
4097                } else {
4098                    self.emit.write(" { ");
4099                    for (i, field) in fields.iter().enumerate() {
4100                        if i > 0 {
4101                            self.emit.write(", ");
4102                        }
4103                        self.emit.write(&field.name.name);
4104                        self.emit.write(&format!(": __sf{}", i));
4105                    }
4106                    // Add tool fields automatically
4107                    for tool_name in &tool_uses {
4108                        if !fields.is_empty()
4109                            || tool_uses.iter().position(|t| t == tool_name) != Some(0)
4110                        {
4111                            self.emit.write(", ");
4112                        }
4113                        self.emit.write(&tool_name.to_lowercase());
4114                        self.emit.write(": ");
4115                        if tool_name == "Database" {
4116                            self.emit.write("DatabaseClient::from_env().await.expect(\"Failed to connect to database\")");
4117                        } else {
4118                            self.emit.write(tool_name);
4119                            self.emit.write("Client::from_env()");
4120                        }
4121                    }
4122                    self.emit.write(" }; ");
4123                }
4124
4125                // on_start with optional error handling
4126                if has_error_handler {
4127                    self.emit
4128                        .write("let result = match agent.on_start(&mut ctx).await { ");
4129                    self.emit.write("Ok(result) => Ok(result), ");
4130                    self.emit
4131                        .write("Err(e) => agent.on_error(e, &mut ctx).await }; ");
4132                } else {
4133                    self.emit
4134                        .write("let result = agent.on_start(&mut ctx).await; ");
4135                }
4136
4137                // Message receive loop (if agent has message handlers)
4138                if let Some(handlers) = message_handlers {
4139                    self.emit.write("if result.is_ok() { loop { ");
4140                    self.emit.write("match ctx.receive_raw().await { ");
4141                    self.emit.write("Ok(msg) => { ");
4142                    self.emit.write("match msg.type_name.as_deref() { ");
4143
4144                    // Generate match arms for each message type
4145                    for (param_name, param_ty) in &handlers.handlers {
4146                        let type_name = self.type_expr_to_string(param_ty);
4147                        let method_name = format!("on_message_{}", Self::to_snake_case(&type_name));
4148
4149                        self.emit.write("Some(\"");
4150                        self.emit.write(&type_name);
4151                        self.emit.write("\") => { ");
4152                        self.emit.write("if let Ok(");
4153                        self.emit.write(&param_name.name);
4154                        self.emit
4155                            .write(") = serde_json::from_value(msg.payload) { ");
4156                        self.emit.write("let _ = agent.");
4157                        self.emit.write(&method_name);
4158                        self.emit.write("(");
4159                        self.emit.write(&param_name.name);
4160                        self.emit.write(", &mut ctx).await; } } ");
4161                    }
4162
4163                    self.emit.write("_ => {} } } "); // close match type_name, Ok(msg)
4164                    self.emit.write("Err(_) => break } } } "); // close match receive, loop, if
4165                }
4166
4167                self.emit.write("result }) }");
4168            }
4169
4170            Expr::Await {
4171                handle, timeout, ..
4172            } => {
4173                // Note: No ? here - the try wrapper is responsible for error propagation
4174                if let Some(timeout_expr) = timeout {
4175                    // With timeout: wrap in tokio::time::timeout
4176                    self.emit.write("tokio::time::timeout(");
4177                    self.emit.write("std::time::Duration::from_millis(");
4178                    self.generate_expr(timeout_expr);
4179                    self.emit.write(" as u64), ");
4180                    self.generate_expr(handle);
4181                    self.emit
4182                        .write(".result()).await.map_err(|_| sage_runtime::SageError::agent(");
4183                    self.emit.write("\"await timed out\"))?");
4184                } else {
4185                    // Without timeout: simple await
4186                    self.generate_expr(handle);
4187                    self.emit.write(".result().await");
4188                }
4189            }
4190
4191            Expr::Send {
4192                handle, message, ..
4193            } => {
4194                self.generate_expr(handle);
4195
4196                // Phase 3: Include type name if we can determine it (for protocol tracking)
4197                // Note: Don't add ? here - the Try wrapper handles error propagation
4198                if let Some(type_name) = Self::extract_message_type_name(message) {
4199                    // Use send_message() for pre-built Message with metadata
4200                    self.emit.write(".send_message(Message::new(");
4201                    self.generate_expr(message);
4202                    self.emit.write(")?.with_type_name(\"");
4203                    self.emit.write(&type_name);
4204                    self.emit.write("\")).await");
4205                } else {
4206                    // Use simple send() for raw message
4207                    self.emit.write(".send(");
4208                    self.generate_expr(message);
4209                    self.emit.write(").await");
4210                }
4211            }
4212
4213            Expr::Yield { value, .. } => {
4214                self.emit.write("ctx.emit(");
4215                self.generate_expr(value);
4216                self.emit.write(")");
4217            }
4218
4219            Expr::StringInterp { template, .. } => {
4220                self.emit_string_template(template);
4221            }
4222
4223            Expr::Match {
4224                scrutinee, arms, ..
4225            } => {
4226                self.emit.write("match ");
4227                self.generate_expr(scrutinee);
4228                self.emit.writeln(" {");
4229                self.emit.indent();
4230                for arm in arms {
4231                    self.emit_pattern(&arm.pattern);
4232                    self.emit.write(" => ");
4233                    self.generate_expr(&arm.body);
4234                    self.emit.writeln(",");
4235                }
4236                self.emit.dedent();
4237                self.emit.write("}");
4238            }
4239
4240            Expr::RecordConstruct {
4241                name,
4242                type_args,
4243                fields,
4244                ..
4245            } => {
4246                self.emit.write(&name.name);
4247                // RFC-0015: Emit type arguments if provided (turbofish syntax)
4248                if !type_args.is_empty() {
4249                    self.emit.write("::<");
4250                    for (i, arg) in type_args.iter().enumerate() {
4251                        if i > 0 {
4252                            self.emit.write(", ");
4253                        }
4254                        self.emit_type(arg);
4255                    }
4256                    self.emit.write(">");
4257                }
4258                self.emit.write(" { ");
4259                for (i, field) in fields.iter().enumerate() {
4260                    if i > 0 {
4261                        self.emit.write(", ");
4262                    }
4263                    self.emit.write(&field.name.name);
4264                    self.emit.write(": ");
4265                    self.generate_expr(&field.value);
4266                }
4267                self.emit.write(" }");
4268            }
4269
4270            Expr::FieldAccess { object, field, .. } => {
4271                self.generate_expr(object);
4272                self.emit.write(".");
4273                self.emit.write(&field.name);
4274            }
4275
4276            Expr::Receive { .. } => {
4277                self.emit.write("ctx.receive().await?");
4278            }
4279
4280            // RFC-0007: Error handling
4281            Expr::Try { expr, .. } => {
4282                // Generate the inner expression with ? for error propagation
4283                self.generate_expr(expr);
4284                self.emit.write("?");
4285            }
4286
4287            Expr::Catch {
4288                expr,
4289                error_bind,
4290                recovery,
4291                ..
4292            } => {
4293                // Generate a match expression to handle the Result
4294                // If expr is Try { inner }, skip the Try wrapper since we handle the error here
4295                self.emit.write("match ");
4296                if let Expr::Try { expr: inner, .. } = expr.as_ref() {
4297                    self.generate_expr(inner);
4298                } else {
4299                    self.generate_expr(expr);
4300                }
4301                self.emit.writeln(" {");
4302                self.emit.indent();
4303
4304                // Ok arm - unwrap the value
4305                self.emit.writeln("Ok(__val) => __val,");
4306
4307                // Err arm - run recovery
4308                if let Some(err_name) = error_bind {
4309                    self.emit.write("Err(");
4310                    self.emit.write(&err_name.name);
4311                    self.emit.write(") => ");
4312                } else {
4313                    self.emit.write("Err(_) => ");
4314                }
4315                self.generate_expr(recovery);
4316                self.emit.writeln(",");
4317
4318                self.emit.dedent();
4319                self.emit.write("}");
4320            }
4321
4322            // fail expression - explicit error raising
4323            Expr::Fail { error, .. } => {
4324                // Generate: return Err(SageError::user(msg))
4325                self.emit.write("return Err(sage_runtime::SageError::user(");
4326                self.generate_expr(error);
4327                self.emit.write("))");
4328            }
4329
4330            // retry expression - retry a fallible operation
4331            Expr::Retry {
4332                count,
4333                delay,
4334                on_errors: _,
4335                body,
4336                ..
4337            } => {
4338                // Generate a retry loop with async block
4339                self.emit.writeln("'_retry: {");
4340                self.emit.indent();
4341
4342                self.emit.write("let _retry_max: i64 = ");
4343                self.generate_expr(count);
4344                self.emit.writeln(";");
4345
4346                if let Some(delay_expr) = delay {
4347                    self.emit.write("let _retry_delay: u64 = ");
4348                    self.generate_expr(delay_expr);
4349                    self.emit.writeln(" as u64;");
4350                }
4351
4352                self.emit
4353                    .writeln("let mut _last_error: Option<sage_runtime::SageError> = None;");
4354                self.emit.writeln("for _attempt in 0.._retry_max {");
4355                self.emit.indent();
4356
4357                // Wrap body in async block that returns Result
4358                self.emit.writeln("let _result = (async {");
4359                self.emit.indent();
4360                self.emit.write("Ok::<_, sage_runtime::SageError>(");
4361                self.generate_expr(body);
4362                self.emit.writeln(")");
4363                self.emit.dedent();
4364                self.emit.writeln("}).await;");
4365
4366                self.emit.writeln("match _result {");
4367                self.emit.indent();
4368                self.emit.writeln("Ok(v) => break '_retry v,");
4369                self.emit.writeln("Err(e) => {");
4370                self.emit.indent();
4371                self.emit.writeln("_last_error = Some(e);");
4372
4373                // Add delay between retries if specified
4374                if delay.is_some() {
4375                    self.emit.writeln("if _attempt < _retry_max - 1 {");
4376                    self.emit.indent();
4377                    self.emit.writeln(
4378                        "tokio::time::sleep(std::time::Duration::from_millis(_retry_delay)).await;",
4379                    );
4380                    self.emit.dedent();
4381                    self.emit.writeln("}");
4382                }
4383
4384                self.emit.dedent();
4385                self.emit.writeln("}");
4386                self.emit.dedent();
4387                self.emit.writeln("}");
4388
4389                self.emit.dedent();
4390                self.emit.writeln("}");
4391
4392                // After loop exhausted, return the last error
4393                self.emit.writeln("return Err(_last_error.unwrap());");
4394
4395                self.emit.dedent();
4396                self.emit.write("}");
4397            }
4398
4399            // trace(message) - emit a trace event
4400            Expr::Trace { message, .. } => {
4401                self.emit.write("sage_runtime::trace::user(&");
4402                self.generate_expr(message);
4403                self.emit.write(")");
4404            }
4405
4406            // RFC-0009: Closures
4407            Expr::Closure { params, body, .. } => {
4408                // Generate: Box::new(move |param1: Type1, param2: Type2| { body })
4409                self.emit.write("Box::new(move |");
4410                for (i, param) in params.iter().enumerate() {
4411                    if i > 0 {
4412                        self.emit.write(", ");
4413                    }
4414                    self.emit.write(&param.name.name);
4415                    if let Some(ty) = &param.ty {
4416                        self.emit.write(": ");
4417                        self.emit_type(ty);
4418                    }
4419                }
4420                self.emit.write("| ");
4421                self.generate_expr(body);
4422                self.emit.write(")");
4423            }
4424
4425            // RFC-0010: Tuples and Maps
4426            Expr::Tuple { elements, .. } => {
4427                self.emit.write("(");
4428                for (i, elem) in elements.iter().enumerate() {
4429                    if i > 0 {
4430                        self.emit.write(", ");
4431                    }
4432                    self.generate_expr(elem);
4433                }
4434                self.emit.write(")");
4435            }
4436
4437            Expr::TupleIndex { tuple, index, .. } => {
4438                self.generate_expr(tuple);
4439                self.emit.write(&format!(".{index}"));
4440            }
4441
4442            Expr::Map { entries, .. } => {
4443                if entries.is_empty() {
4444                    self.emit.write("std::collections::HashMap::new()");
4445                } else {
4446                    self.emit.write("std::collections::HashMap::from([");
4447                    for (i, entry) in entries.iter().enumerate() {
4448                        if i > 0 {
4449                            self.emit.write(", ");
4450                        }
4451                        self.emit.write("(");
4452                        self.generate_expr(&entry.key);
4453                        self.emit.write(", ");
4454                        self.generate_expr(&entry.value);
4455                        self.emit.write(")");
4456                    }
4457                    self.emit.write("])");
4458                }
4459            }
4460
4461            Expr::VariantConstruct {
4462                enum_name,
4463                type_args,
4464                variant,
4465                payload,
4466                ..
4467            } => {
4468                self.emit.write(&enum_name.name);
4469                // RFC-0015: Emit type arguments if provided (turbofish syntax)
4470                if !type_args.is_empty() {
4471                    self.emit.write("::<");
4472                    for (i, arg) in type_args.iter().enumerate() {
4473                        if i > 0 {
4474                            self.emit.write(", ");
4475                        }
4476                        self.emit_type(arg);
4477                    }
4478                    self.emit.write(">");
4479                }
4480                self.emit.write("::");
4481                self.emit.write(&variant.name);
4482                if let Some(payload_expr) = payload {
4483                    self.emit.write("(");
4484                    self.generate_expr(payload_expr);
4485                    self.emit.write(")");
4486                }
4487            }
4488
4489            // RFC-0011: Tool calls
4490            Expr::ToolCall {
4491                tool,
4492                function,
4493                args,
4494                ..
4495            } => {
4496                // Generate: self.tool_name.function(args).await
4497                // Returns SageResult<T> - must be handled with try/catch
4498                self.emit.write("self.");
4499                self.emit.write(&tool.name.to_lowercase());
4500                self.emit.write(".");
4501                self.emit.write(&function.name);
4502                self.emit.write("(");
4503                for (i, arg) in args.iter().enumerate() {
4504                    if i > 0 {
4505                        self.emit.write(", ");
4506                    }
4507                    // Clone arguments to avoid move-out-of-self issues
4508                    self.emit.write("(");
4509                    self.generate_expr(arg);
4510                    self.emit.write(").clone()");
4511                }
4512                self.emit.write(").await");
4513            }
4514
4515            // Phase 3: Reply expression for session types
4516            Expr::Reply { message, .. } => {
4517                // Clone protocol info to avoid borrow issues
4518                let protocol_role = self
4519                    .current_protocol_roles
4520                    .iter()
4521                    .next()
4522                    .map(|(_, r)| r.clone());
4523
4524                if let Some(role) = protocol_role {
4525                    // Agent follows protocols - use reply_with_protocol for validation
4526                    // We need to infer the message type from the expression
4527                    let msg_type = self.infer_expr_type_name(message);
4528                    self.emit.write("ctx.reply_with_protocol(");
4529                    self.generate_expr(message);
4530                    self.emit.write(", \"");
4531                    self.emit.write(&msg_type);
4532                    self.emit.write("\", \"");
4533                    self.emit.write(&role);
4534                    self.emit.write("\").await?");
4535                } else {
4536                    // No protocols - use simple reply
4537                    self.emit.write("ctx.reply(");
4538                    self.generate_expr(message);
4539                    self.emit.write(").await?");
4540                }
4541            }
4542        }
4543    }
4544
4545    fn emit_pattern(&mut self, pattern: &sage_parser::Pattern) {
4546        use sage_parser::Pattern;
4547        match pattern {
4548            Pattern::Wildcard { .. } => {
4549                self.emit.write("_");
4550            }
4551            Pattern::Variant {
4552                enum_name,
4553                variant,
4554                payload,
4555                ..
4556            } => {
4557                if let Some(enum_name) = enum_name {
4558                    self.emit.write(&enum_name.name);
4559                    self.emit.write("::");
4560                }
4561                self.emit.write(&variant.name);
4562                if let Some(inner_pattern) = payload {
4563                    self.emit.write("(");
4564                    self.emit_pattern(inner_pattern);
4565                    self.emit.write(")");
4566                }
4567            }
4568            Pattern::Literal { value, .. } => {
4569                self.emit_literal(value);
4570            }
4571            Pattern::Binding { name, .. } => {
4572                self.emit.write(&name.name);
4573            }
4574            Pattern::Tuple { elements, .. } => {
4575                self.emit.write("(");
4576                for (i, elem) in elements.iter().enumerate() {
4577                    if i > 0 {
4578                        self.emit.write(", ");
4579                    }
4580                    self.emit_pattern(elem);
4581                }
4582                self.emit.write(")");
4583            }
4584        }
4585    }
4586
4587    fn emit_literal(&mut self, lit: &Literal) {
4588        match lit {
4589            Literal::Int(n) => {
4590                self.emit.write(&format!("{n}_i64"));
4591            }
4592            Literal::Float(f) => {
4593                self.emit.write(&format!("{f}_f64"));
4594            }
4595            Literal::Bool(b) => {
4596                self.emit.write(if *b { "true" } else { "false" });
4597            }
4598            Literal::String(s) => {
4599                // Escape the string for Rust
4600                self.emit.write("\"");
4601                for c in s.chars() {
4602                    match c {
4603                        '"' => self.emit.write_raw("\\\""),
4604                        '\\' => self.emit.write_raw("\\\\"),
4605                        '\n' => self.emit.write_raw("\\n"),
4606                        '\r' => self.emit.write_raw("\\r"),
4607                        '\t' => self.emit.write_raw("\\t"),
4608                        c if c.is_control() => {
4609                            self.emit.write_raw(&format!("\\x{:02x}", c as u32));
4610                        }
4611                        _ => self.emit.write_raw(&c.to_string()),
4612                    }
4613                }
4614                self.emit.write("\".to_string()");
4615            }
4616        }
4617    }
4618
4619    fn escape_string_for_rust(s: &str) -> String {
4620        let mut out = String::new();
4621        for c in s.chars() {
4622            match c {
4623                '"' => out.push_str("\\\""),
4624                '\\' => out.push_str("\\\\"),
4625                '\n' => out.push_str("\\n"),
4626                '\r' => out.push_str("\\r"),
4627                '\t' => out.push_str("\\t"),
4628                c if c.is_control() => out.push_str(&format!("\\x{:02x}", c as u32)),
4629                _ => out.push(c),
4630            }
4631        }
4632        out
4633    }
4634
4635    fn emit_string_template(&mut self, template: &sage_parser::StringTemplate) {
4636        if !template.has_interpolations() {
4637            // Simple string literal
4638            if let Some(StringPart::Literal(s)) = template.parts.first() {
4639                self.emit.write("\"");
4640                self.emit.write_raw(&Self::escape_string_for_rust(s));
4641                self.emit.write("\".to_string()");
4642            }
4643            return;
4644        }
4645
4646        // Build format string and args
4647        self.emit.write("format!(\"");
4648        for part in &template.parts {
4649            match part {
4650                StringPart::Literal(s) => {
4651                    // Escape for Rust string, then escape braces for format string
4652                    let escaped = Self::escape_string_for_rust(s)
4653                        .replace('{', "{{")
4654                        .replace('}', "}}");
4655                    self.emit.write_raw(&escaped);
4656                }
4657                StringPart::Interpolation(_) => {
4658                    self.emit.write_raw("{}");
4659                }
4660            }
4661        }
4662        self.emit.write("\"");
4663
4664        // Add the interpolation args
4665        for part in &template.parts {
4666            if let StringPart::Interpolation(expr) = part {
4667                self.emit.write(", ");
4668                self.generate_expr(expr);
4669            }
4670        }
4671        self.emit.write(")");
4672    }
4673
4674    fn emit_type(&mut self, ty: &TypeExpr) {
4675        match ty {
4676            TypeExpr::Int => self.emit.write("i64"),
4677            TypeExpr::Float => self.emit.write("f64"),
4678            TypeExpr::Bool => self.emit.write("bool"),
4679            TypeExpr::String => self.emit.write("String"),
4680            TypeExpr::Unit => self.emit.write("()"),
4681            TypeExpr::List(inner) => {
4682                self.emit.write("Vec<");
4683                self.emit_type(inner);
4684                self.emit.write(">");
4685            }
4686            TypeExpr::Option(inner) => {
4687                self.emit.write("Option<");
4688                self.emit_type(inner);
4689                self.emit.write(">");
4690            }
4691            TypeExpr::Oracle(inner) => {
4692                // Inferred<T> just becomes T at runtime
4693                self.emit_type(inner);
4694            }
4695            TypeExpr::Agent(agent_name) => {
4696                // Agent handles use the agent's output type, but we don't know it here
4697                // For now, just use a generic output type
4698                self.emit.write("AgentHandle<");
4699                self.emit.write(&agent_name.name);
4700                self.emit.write("Output>");
4701            }
4702            TypeExpr::Named(name, type_args) => {
4703                self.emit.write(&name.name);
4704                if !type_args.is_empty() {
4705                    self.emit.write("<");
4706                    for (i, arg) in type_args.iter().enumerate() {
4707                        if i > 0 {
4708                            self.emit.write(", ");
4709                        }
4710                        self.emit_type(arg);
4711                    }
4712                    self.emit.write(">");
4713                }
4714            }
4715
4716            // RFC-0007: Error handling
4717            TypeExpr::Error => {
4718                self.emit.write("sage_runtime::SageError");
4719            }
4720
4721            // RFC-0009: Function types
4722            TypeExpr::Fn(params, ret) => {
4723                self.emit.write("Box<dyn Fn(");
4724                for (i, param) in params.iter().enumerate() {
4725                    if i > 0 {
4726                        self.emit.write(", ");
4727                    }
4728                    self.emit_type(param);
4729                }
4730                self.emit.write(") -> ");
4731                self.emit_type(ret);
4732                self.emit.write(" + Send + 'static>");
4733            }
4734
4735            // RFC-0010: Maps, tuples, Result
4736            TypeExpr::Map(key, value) => {
4737                self.emit.write("std::collections::HashMap<");
4738                self.emit_type(key);
4739                self.emit.write(", ");
4740                self.emit_type(value);
4741                self.emit.write(">");
4742            }
4743            TypeExpr::Tuple(elems) => {
4744                self.emit.write("(");
4745                for (i, elem) in elems.iter().enumerate() {
4746                    if i > 0 {
4747                        self.emit.write(", ");
4748                    }
4749                    self.emit_type(elem);
4750                }
4751                self.emit.write(")");
4752            }
4753            TypeExpr::Result(ok, err) => {
4754                self.emit.write("Result<");
4755                self.emit_type(ok);
4756                self.emit.write(", ");
4757                self.emit_type(err);
4758                self.emit.write(">");
4759            }
4760        }
4761    }
4762
4763    fn emit_binop(&mut self, op: &BinOp) {
4764        let s = match op {
4765            BinOp::Add => "+",
4766            BinOp::Sub => "-",
4767            BinOp::Mul => "*",
4768            BinOp::Div => "/",
4769            BinOp::Rem => "%",
4770            BinOp::Eq => "==",
4771            BinOp::Ne => "!=",
4772            BinOp::Lt => "<",
4773            BinOp::Gt => ">",
4774            BinOp::Le => "<=",
4775            BinOp::Ge => ">=",
4776            BinOp::And => "&&",
4777            BinOp::Or => "||",
4778            BinOp::Concat => "++", // Handled specially above
4779        };
4780        self.emit.write(s);
4781    }
4782
4783    fn emit_unaryop(&mut self, op: &UnaryOp) {
4784        let s = match op {
4785            UnaryOp::Neg => "-",
4786            UnaryOp::Not => "!",
4787        };
4788        self.emit.write(s);
4789    }
4790
4791    fn infer_agent_output_type(&self, agent: &AgentDecl) -> String {
4792        // Look for emit expression in start handler to infer return type
4793        // For now, default to i64
4794        for handler in &agent.handlers {
4795            if let EventKind::Start = &handler.event {
4796                if let Some(ty) = self.find_emit_type(&handler.body) {
4797                    return ty;
4798                }
4799            }
4800        }
4801        "i64".to_string()
4802    }
4803
4804    fn find_emit_type(&self, block: &Block) -> Option<String> {
4805        // Track variable assignments to resolve yield(var) types
4806        let mut var_types: std::collections::HashMap<String, String> =
4807            std::collections::HashMap::new();
4808
4809        for stmt in &block.stmts {
4810            // Track let bindings
4811            if let Stmt::Let { name, value, .. } = stmt {
4812                let ty = self.infer_expr_type_with_vars(value, &var_types);
4813                var_types.insert(name.name.clone(), ty);
4814            }
4815
4816            if let Stmt::Expr { expr, .. } = stmt {
4817                if let Expr::Yield { value, .. } = expr {
4818                    return Some(self.infer_expr_type_with_vars(value, &var_types));
4819                }
4820            }
4821            // Check nested blocks
4822            if let Stmt::If {
4823                then_block,
4824                else_block,
4825                ..
4826            } = stmt
4827            {
4828                if let Some(ty) = self.find_emit_type(then_block) {
4829                    return Some(ty);
4830                }
4831                if let Some(else_branch) = else_block {
4832                    if let sage_parser::ElseBranch::Block(block) = else_branch {
4833                        if let Some(ty) = self.find_emit_type(block) {
4834                            return Some(ty);
4835                        }
4836                    }
4837                }
4838            }
4839        }
4840        None
4841    }
4842
4843    fn infer_expr_type_with_vars(
4844        &self,
4845        expr: &Expr,
4846        var_types: &std::collections::HashMap<String, String>,
4847    ) -> String {
4848        match expr {
4849            Expr::Var { name, .. } => {
4850                // Look up variable type from tracked assignments
4851                if let Some(ty) = var_types.get(&name.name) {
4852                    return ty.clone();
4853                }
4854                "i64".to_string() // Conservative default
4855            }
4856            // Try expression unwraps to inner type
4857            Expr::Try { expr, .. } => self.infer_expr_type_with_vars(expr, var_types),
4858            // Catch expression returns the Ok type
4859            Expr::Catch { expr, .. } => self.infer_expr_type_with_vars(expr, var_types),
4860            // Delegate to basic type inference for other expressions
4861            _ => self.infer_expr_type(expr),
4862        }
4863    }
4864
4865    fn infer_expr_type(&self, expr: &Expr) -> String {
4866        match expr {
4867            Expr::Literal { value, .. } => match value {
4868                Literal::Int(_) => "i64".to_string(),
4869                Literal::Float(_) => "f64".to_string(),
4870                Literal::Bool(_) => "bool".to_string(),
4871                Literal::String(_) => "String".to_string(),
4872            },
4873            Expr::Var { .. } => "i64".to_string(), // Conservative default
4874            Expr::Binary { op, .. } => {
4875                if matches!(
4876                    op,
4877                    BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge
4878                ) {
4879                    "bool".to_string()
4880                } else if matches!(op, BinOp::Concat) {
4881                    "String".to_string()
4882                } else {
4883                    "i64".to_string()
4884                }
4885            }
4886            Expr::Divine { .. } | Expr::StringInterp { .. } => "String".to_string(),
4887            Expr::Call { name, .. } if name.name == "str" => "String".to_string(),
4888            Expr::Call { name, .. } if name.name == "len" => "i64".to_string(),
4889            _ => "i64".to_string(),
4890        }
4891    }
4892
4893    /// Phase 3: Extract the type name from a message expression (for protocol tracking).
4894    ///
4895    /// Returns Some(type_name) if the type can be determined, None otherwise.
4896    fn extract_message_type_name(expr: &Expr) -> Option<String> {
4897        match expr {
4898            // Record construction: `Ping {}` → "Ping"
4899            Expr::RecordConstruct { name, .. } => Some(name.name.clone()),
4900
4901            // Enum variant: `Status::Active` → "Status"
4902            Expr::VariantConstruct { enum_name, .. } => Some(enum_name.name.clone()),
4903
4904            // Parenthesized expression: unwrap
4905            Expr::Paren { inner, .. } => Self::extract_message_type_name(inner),
4906
4907            // For other expressions, we can't determine the type at codegen time
4908            _ => None,
4909        }
4910    }
4911
4912    /// Phase 3: Infer the type name from an expression.
4913    ///
4914    /// Used for protocol validation in reply() - we need to know the message type
4915    /// being sent. For struct constructions this is straightforward; for other
4916    /// expressions we use a fallback.
4917    fn infer_expr_type_name(&self, expr: &Expr) -> String {
4918        match expr {
4919            // Record construction: RecordName { ... }
4920            Expr::RecordConstruct { name, .. } => name.name.clone(),
4921            // Enum variant: EnumName.Variant or EnumName.Variant(payload)
4922            Expr::VariantConstruct { enum_name, .. } => enum_name.name.clone(),
4923            // Variable reference - we'd need type info to resolve this
4924            Expr::Var { name, .. } => name.name.clone(),
4925            // For other expressions, use a generic name
4926            _ => "Message".to_string(),
4927        }
4928    }
4929
4930    /// Convert a TypeExpr to its base type name string.
4931    ///
4932    /// Used for generating method names from message types.
4933    #[allow(clippy::only_used_in_recursion)]
4934    fn type_expr_to_string(&self, ty: &TypeExpr) -> String {
4935        match ty {
4936            TypeExpr::Int => "Int".to_string(),
4937            TypeExpr::Float => "Float".to_string(),
4938            TypeExpr::Bool => "Bool".to_string(),
4939            TypeExpr::String => "String".to_string(),
4940            TypeExpr::Unit => "Unit".to_string(),
4941            TypeExpr::Named(name, _) => name.name.clone(),
4942            TypeExpr::List(inner) => format!("List{}", self.type_expr_to_string(inner)),
4943            TypeExpr::Option(inner) => format!("Option{}", self.type_expr_to_string(inner)),
4944            TypeExpr::Oracle(inner) => self.type_expr_to_string(inner),
4945            TypeExpr::Agent(name) => name.name.clone(),
4946            TypeExpr::Error => "Error".to_string(),
4947            TypeExpr::Map(k, v) => {
4948                format!(
4949                    "Map{}To{}",
4950                    self.type_expr_to_string(k),
4951                    self.type_expr_to_string(v)
4952                )
4953            }
4954            TypeExpr::Fn(_, _) => "Fn".to_string(),
4955            TypeExpr::Tuple(elems) => {
4956                let parts: Vec<_> = elems.iter().map(|e| self.type_expr_to_string(e)).collect();
4957                format!("Tuple{}", parts.join(""))
4958            }
4959            TypeExpr::Result(ok, err) => {
4960                format!(
4961                    "Result{}Or{}",
4962                    self.type_expr_to_string(ok),
4963                    self.type_expr_to_string(err)
4964                )
4965            }
4966        }
4967    }
4968}
4969
4970#[cfg(test)]
4971mod tests {
4972    use super::*;
4973    use sage_parser::{lex, parse};
4974    use std::sync::Arc;
4975
4976    fn generate_source(source: &str) -> String {
4977        let lex_result = lex(source).expect("lexing failed");
4978        let source_arc: Arc<str> = Arc::from(source);
4979        let (program, errors) = parse(lex_result.tokens(), source_arc);
4980        assert!(errors.is_empty(), "parse errors: {errors:?}");
4981        let program = program.expect("should parse");
4982        generate(&program, "test").main_rs
4983    }
4984
4985    #[test]
4986    fn generate_minimal_program() {
4987        let source = r#"
4988            agent Main {
4989                on start {
4990                    yield(42);
4991                }
4992            }
4993            run Main;
4994        "#;
4995
4996        let output = generate_source(source);
4997        assert!(output.contains("struct Main;"));
4998        assert!(output.contains("async fn on_start"));
4999        assert!(output.contains("ctx.emit(42_i64)"));
5000        assert!(output.contains("#[tokio::main]"));
5001    }
5002
5003    #[test]
5004    fn generate_function() {
5005        let source = r#"
5006            fn add(a: Int, b: Int) -> Int {
5007                return a + b;
5008            }
5009            agent Main {
5010                on start {
5011                    yield(add(1, 2));
5012                }
5013            }
5014            run Main;
5015        "#;
5016
5017        let output = generate_source(source);
5018        assert!(output.contains("fn add(a: i64, b: i64) -> i64"));
5019        assert!(output.contains("return a + b;"));
5020    }
5021
5022    #[test]
5023    fn generate_agent_with_beliefs() {
5024        let source = r#"
5025            agent Worker {
5026                value: Int
5027
5028                on start {
5029                    yield(self.value * 2);
5030                }
5031            }
5032            agent Main {
5033                on start {
5034                    yield(0);
5035                }
5036            }
5037            run Main;
5038        "#;
5039
5040        let output = generate_source(source);
5041        assert!(output.contains("struct Worker {"));
5042        assert!(output.contains("value: i64,"));
5043        assert!(output.contains("self.value"));
5044    }
5045
5046    #[test]
5047    fn generate_persistent_beliefs() {
5048        let source = r#"
5049            agent Counter {
5050                @persistent count: Int
5051
5052                on waking {
5053                    print("woke up");
5054                }
5055
5056                on start {
5057                    yield(self.count.get());
5058                }
5059            }
5060            run Counter;
5061        "#;
5062
5063        let output = generate_source(source);
5064        // Agent struct should have checkpoint fields and Persisted wrapper
5065        assert!(output.contains("_checkpoint:"), "missing checkpoint field");
5066        assert!(
5067            output.contains("_checkpoint_key:"),
5068            "missing checkpoint key field"
5069        );
5070        assert!(
5071            output.contains("Persisted<i64>"),
5072            "count should be wrapped in Persisted"
5073        );
5074        // Main should initialize checkpoint store
5075        assert!(
5076            output.contains("MemoryCheckpointStore"),
5077            "missing checkpoint store init"
5078        );
5079        assert!(
5080            output.contains("Persisted::new"),
5081            "missing Persisted::new in construction"
5082        );
5083        // on_waking handler should be generated and called
5084        assert!(
5085            output.contains("async fn on_waking"),
5086            "missing on_waking handler"
5087        );
5088        assert!(
5089            output.contains("agent.on_waking().await"),
5090            "missing on_waking call"
5091        );
5092    }
5093
5094    #[test]
5095    fn generate_string_interpolation() {
5096        let source = r#"
5097            agent Main {
5098                on start {
5099                    let name = "World";
5100                    let msg = "Hello, {name}!";
5101                    print(msg);
5102                    yield(0);
5103                }
5104            }
5105            run Main;
5106        "#;
5107
5108        let output = generate_source(source);
5109        assert!(output.contains("format!(\"Hello, {}!\", name)"));
5110    }
5111
5112    #[test]
5113    fn generate_control_flow() {
5114        let source = r#"
5115            agent Main {
5116                on start {
5117                    let x = 10;
5118                    if x > 5 {
5119                        yield(1);
5120                    } else {
5121                        yield(0);
5122                    }
5123                }
5124            }
5125            run Main;
5126        "#;
5127
5128        let output = generate_source(source);
5129        assert!(output.contains("if x > 5_i64"), "output:\n{output}");
5130        // else is on the same line after close brace
5131        assert!(output.contains("else"), "output:\n{output}");
5132    }
5133
5134    #[test]
5135    fn generate_loops() {
5136        let source = r#"
5137            agent Main {
5138                on start {
5139                    for x in [1, 2, 3] {
5140                        print(str(x));
5141                    }
5142                    let n = 0;
5143                    while n < 5 {
5144                        n = n + 1;
5145                    }
5146                    yield(n);
5147                }
5148            }
5149            run Main;
5150        "#;
5151
5152        let output = generate_source(source);
5153        assert!(output.contains("for x in vec![1_i64, 2_i64, 3_i64]"));
5154        assert!(output.contains("while n < 5_i64"));
5155    }
5156
5157    #[test]
5158    fn generate_pub_function() {
5159        let source = r#"
5160            pub fn helper(x: Int) -> Int {
5161                return x * 2;
5162            }
5163            agent Main {
5164                on start {
5165                    yield(helper(21));
5166                }
5167            }
5168            run Main;
5169        "#;
5170
5171        let output = generate_source(source);
5172        assert!(output.contains("pub fn helper(x: i64) -> i64"));
5173    }
5174
5175    #[test]
5176    fn generate_pub_agent() {
5177        let source = r#"
5178            pub agent Worker {
5179                on start {
5180                    yield(42);
5181                }
5182            }
5183            agent Main {
5184                on start {
5185                    yield(0);
5186                }
5187            }
5188            run Main;
5189        "#;
5190
5191        let output = generate_source(source);
5192        assert!(output.contains("pub struct Worker;"));
5193    }
5194
5195    #[test]
5196    fn generate_module_tree_simple() {
5197        use sage_loader::load_single_file;
5198        use std::fs;
5199        use tempfile::TempDir;
5200
5201        let dir = TempDir::new().unwrap();
5202        let file = dir.path().join("test.sg");
5203        fs::write(
5204            &file,
5205            r#"
5206agent Main {
5207    on start {
5208        yield(42);
5209    }
5210}
5211run Main;
5212"#,
5213        )
5214        .unwrap();
5215
5216        let tree = load_single_file(&file).unwrap();
5217        let project = generate_module_tree(&tree, "test");
5218
5219        assert!(project.main_rs.contains("struct Main;"));
5220        assert!(project.main_rs.contains("async fn on_start"));
5221        assert!(project.main_rs.contains("#[tokio::main]"));
5222    }
5223
5224    #[test]
5225    fn generate_module_tree_with_supervisor() {
5226        use sage_loader::load_single_file;
5227        use std::fs;
5228        use tempfile::TempDir;
5229
5230        let dir = TempDir::new().unwrap();
5231        let file = dir.path().join("test.sg");
5232        fs::write(
5233            &file,
5234            r#"
5235agent Worker {
5236    on start {
5237        yield(42);
5238    }
5239}
5240supervisor AppSupervisor {
5241    strategy: OneForOne
5242    children {
5243        Worker { restart: Transient }
5244    }
5245}
5246run AppSupervisor;
5247"#,
5248        )
5249        .unwrap();
5250
5251        let tree = load_single_file(&file).unwrap();
5252        let project = generate_module_tree(&tree, "test");
5253
5254        // Verify supervisor struct is generated
5255        assert!(
5256            project.main_rs.contains("struct AppSupervisor;"),
5257            "Missing supervisor struct. Output:\n{}",
5258            project.main_rs
5259        );
5260        // Verify supervisor main is generated
5261        assert!(
5262            project
5263                .main_rs
5264                .contains("Supervisor::new(Strategy::OneForOne"),
5265            "Missing supervisor main. Output:\n{}",
5266            project.main_rs
5267        );
5268    }
5269
5270    #[test]
5271    fn generate_supervisor_with_belief_inits() {
5272        // Tests that supervisor children with belief initializers parse and generate correctly.
5273        // Belief inits can be written without commas (multiline style).
5274        let source = r#"
5275agent QueryMonitor {
5276    @persistent check_count: Int
5277    @persistent last_check: String
5278
5279    on start {
5280        yield(1);
5281    }
5282}
5283
5284supervisor DbGuardian {
5285    strategy: OneForOne
5286    children {
5287        QueryMonitor {
5288            restart: Permanent
5289            check_count: 0
5290            last_check: "never"
5291        }
5292    }
5293}
5294
5295run DbGuardian;
5296"#;
5297
5298        let output = generate_source(source);
5299
5300        // Verify supervisor main is generated
5301        assert!(
5302            output.contains("#[tokio::main]"),
5303            "Missing tokio::main. Output:\n{}",
5304            output
5305        );
5306        assert!(
5307            output.contains("Supervisor::new(Strategy::OneForOne"),
5308            "Missing supervisor creation. Output:\n{}",
5309            output
5310        );
5311        // Verify belief initializers are set with Persisted::with_initial
5312        assert!(
5313            output.contains("check_count: Persisted::with_initial("),
5314            "Missing check_count with_initial. Output:\n{}",
5315            output
5316        );
5317        assert!(
5318            output.contains("last_check: Persisted::with_initial("),
5319            "Missing last_check with_initial. Output:\n{}",
5320            output
5321        );
5322        // Check that values are passed
5323        assert!(
5324            output.contains("0_i64"),
5325            "Missing check_count initial value. Output:\n{}",
5326            output
5327        );
5328        assert!(
5329            output.contains("\"never\""),
5330            "Missing last_check initial value. Output:\n{}",
5331            output
5332        );
5333    }
5334
5335    #[test]
5336    fn generate_record_declaration() {
5337        let source = r#"
5338            record Point {
5339                x: Int,
5340                y: Int,
5341            }
5342            agent Main {
5343                on start {
5344                    let p = Point { x: 10, y: 20 };
5345                    yield(p.x);
5346                }
5347            }
5348            run Main;
5349        "#;
5350
5351        let output = generate_source(source);
5352        assert!(output.contains("#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]"));
5353        assert!(output.contains("struct Point {"));
5354        assert!(output.contains("x: i64,"));
5355        assert!(output.contains("y: i64,"));
5356        assert!(output.contains("Point { x: 10_i64, y: 20_i64 }"));
5357        assert!(output.contains("p.x"));
5358    }
5359
5360    #[test]
5361    fn generate_enum_declaration() {
5362        let source = r#"
5363            enum Status {
5364                Active,
5365                Inactive,
5366                Pending,
5367            }
5368            agent Main {
5369                on start {
5370                    yield(0);
5371                }
5372            }
5373            run Main;
5374        "#;
5375
5376        let output = generate_source(source);
5377        assert!(output.contains("#[derive(Debug, Clone, Copy, PartialEq, Eq)]"));
5378        assert!(output.contains("enum Status {"));
5379        assert!(output.contains("Active,"));
5380        assert!(output.contains("Inactive,"));
5381        assert!(output.contains("Pending,"));
5382    }
5383
5384    #[test]
5385    fn generate_const_declaration() {
5386        let source = r#"
5387            const MAX_SIZE: Int = 100;
5388            const GREETING: String = "Hello";
5389            agent Main {
5390                on start {
5391                    yield(MAX_SIZE);
5392                }
5393            }
5394            run Main;
5395        "#;
5396
5397        let output = generate_source(source);
5398        assert!(output.contains("const MAX_SIZE: i64 = 100_i64;"));
5399        // String constants use &'static str since .to_string() isn't const in Rust
5400        assert!(output.contains("const GREETING: &'static str = \"Hello\";"));
5401    }
5402
5403    #[test]
5404    fn generate_match_expression() {
5405        let source = r#"
5406            enum Status {
5407                Active,
5408                Inactive,
5409            }
5410            fn check_status(s: Status) -> Int {
5411                return match s {
5412                    Active => 1,
5413                    Inactive => 0,
5414                };
5415            }
5416            agent Main {
5417                on start {
5418                    yield(0);
5419                }
5420            }
5421            run Main;
5422        "#;
5423
5424        let output = generate_source(source);
5425        assert!(output.contains("match s {"));
5426        assert!(output.contains("Active => 1_i64,"));
5427        assert!(output.contains("Inactive => 0_i64,"));
5428    }
5429
5430    // =========================================================================
5431    // RFC-0007: Error handling codegen tests
5432    // =========================================================================
5433
5434    #[test]
5435    fn generate_fallible_function() {
5436        let source = r#"
5437            fn get_data(url: String) -> String fails {
5438                return url;
5439            }
5440            agent Main {
5441                on start { yield(0); }
5442            }
5443            run Main;
5444        "#;
5445
5446        let output = generate_source(source);
5447        // Fallible function should return SageResult<T>
5448        assert!(output.contains("fn get_data(url: String) -> SageResult<String>"));
5449    }
5450
5451    #[test]
5452    fn generate_try_expression() {
5453        let source = r#"
5454            fn fallible() -> Int fails { return 42; }
5455            fn caller() -> Int fails {
5456                let x = try fallible();
5457                return x;
5458            }
5459            agent Main {
5460                on start { yield(0); }
5461            }
5462            run Main;
5463        "#;
5464
5465        let output = generate_source(source);
5466        // try should generate ? operator
5467        assert!(output.contains("fallible()?"));
5468    }
5469
5470    #[test]
5471    fn generate_catch_expression() {
5472        let source = r#"
5473            fn fallible() -> Int fails { return 42; }
5474            agent Main {
5475                on start {
5476                    let x = fallible() catch { 0 };
5477                    yield(x);
5478                }
5479            }
5480            run Main;
5481        "#;
5482
5483        let output = generate_source(source);
5484        // catch should generate match expression
5485        assert!(output.contains("match fallible()"));
5486        assert!(output.contains("Ok(__val) => __val"));
5487        assert!(output.contains("Err(_) => 0_i64"));
5488    }
5489
5490    #[test]
5491    fn generate_catch_with_binding() {
5492        let source = r#"
5493            fn fallible() -> Int fails { return 42; }
5494            agent Main {
5495                on start {
5496                    let x = fallible() catch(e) { 0 };
5497                    yield(x);
5498                }
5499            }
5500            run Main;
5501        "#;
5502
5503        let output = generate_source(source);
5504        // catch with binding should capture the error
5505        assert!(output.contains("Err(e) => 0_i64"));
5506    }
5507
5508    #[test]
5509    fn generate_on_error_handler() {
5510        let source = r#"
5511            agent Main {
5512                on start {
5513                    yield(0);
5514                }
5515                on error(e) {
5516                    yield(1);
5517                }
5518            }
5519            run Main;
5520        "#;
5521
5522        let output = generate_source(source);
5523        // Should generate on_error method with &self and &mut ctx
5524        assert!(output.contains("async fn on_error(&self, _e: SageError, ctx: &mut AgentContext"));
5525        // Main should dispatch to on_error on failure with &mut ctx
5526        assert!(output.contains(".on_error(e, &mut ctx)"));
5527    }
5528
5529    // =========================================================================
5530    // RFC-0011: Tool support codegen tests
5531    // =========================================================================
5532
5533    #[test]
5534    fn generate_agent_with_tool_use() {
5535        let source = r#"
5536            agent Fetcher {
5537                use Http
5538
5539                on start {
5540                    let r = Http.get("https://example.com");
5541                    yield(0);
5542                }
5543            }
5544            run Fetcher;
5545        "#;
5546
5547        let output = generate_source(source);
5548        // Should generate struct with http field
5549        assert!(output.contains("struct Fetcher {"));
5550        assert!(output.contains("http: HttpClient,"));
5551        // Should initialize HttpClient in main
5552        assert!(output.contains("http: HttpClient::from_env()"));
5553        // Should generate tool call
5554        assert!(output.contains("self.http.get("));
5555    }
5556
5557    #[test]
5558    fn generate_tool_call_expression() {
5559        let source = r#"
5560            agent Fetcher {
5561                use Http
5562
5563                on start {
5564                    let response = Http.get("https://httpbin.org/get");
5565                    yield(0);
5566                }
5567            }
5568            run Fetcher;
5569        "#;
5570
5571        let output = generate_source(source);
5572        // Tool call should generate self.http.get(...).await (no ?, handled by try/catch)
5573        assert!(output
5574            .contains("self.http.get((\"https://httpbin.org/get\".to_string()).clone()).await"));
5575    }
5576
5577    fn generate_test_source(source: &str) -> String {
5578        let lex_result = lex(source).expect("lexing failed");
5579        let source_arc: Arc<str> = Arc::from(source);
5580        let (program, errors) = parse(lex_result.tokens(), source_arc);
5581        assert!(errors.is_empty(), "parse errors: {errors:?}");
5582        let program = program.expect("should parse");
5583        super::generate_test_program(&program, "test").main_rs
5584    }
5585
5586    #[test]
5587    fn generate_mock_tool() {
5588        let source = r#"
5589            test "mocks http tool" {
5590                mock tool Http.get -> "mocked response";
5591                mock tool Http.post -> fail("network error");
5592                assert_eq(1, 1);
5593            }
5594        "#;
5595
5596        let output = generate_test_source(source);
5597        // Should generate MockToolRegistry
5598        assert!(output.contains("let _mock_tools = MockToolRegistry::new();"));
5599        // Should register mock responses
5600        assert!(output.contains("_mock_tools.register(\"Http\", \"get\", MockResponse::value("));
5601        assert!(output.contains("_mock_tools.register(\"Http\", \"post\", MockResponse::fail("));
5602    }
5603
5604    #[test]
5605    fn generate_mock_infer_and_tool() {
5606        let source = r#"
5607            test "mocks both infer and tool" {
5608                mock divine -> "hello";
5609                mock tool Http.get -> "response";
5610                assert_true(true);
5611            }
5612        "#;
5613
5614        let output = generate_test_source(source);
5615        // Should have both mock client and registry
5616        assert!(output.contains("MockLlmClient::with_responses"));
5617        assert!(output.contains("MockToolRegistry::new()"));
5618    }
5619
5620    #[test]
5621    fn generate_supervisor_declaration() {
5622        let source = r#"
5623            agent Worker {
5624                count: Int
5625
5626                on start {
5627                    yield(self.count);
5628                }
5629            }
5630
5631            supervisor AppSupervisor {
5632                strategy: OneForOne
5633
5634                children {
5635                    Worker { restart: Transient, count: 0 }
5636                }
5637            }
5638
5639            run AppSupervisor;
5640        "#;
5641
5642        let output = generate_source(source);
5643
5644        // Check supervisor struct is generated
5645        assert!(output.contains("// Supervisor: AppSupervisor"));
5646        assert!(output.contains("struct AppSupervisor;"));
5647
5648        // Check supervisor main is generated with config from grove.toml (defaults)
5649        assert!(output.contains("Supervisor::new(Strategy::OneForOne"));
5650        assert!(output.contains("RestartConfig { max_restarts: 5"));
5651        assert!(output.contains("Duration::from_secs(60)"));
5652        assert!(output.contains("supervisor.add_child(\"Worker\", RestartPolicy::Transient"));
5653        assert!(output.contains("supervisor.run()"));
5654    }
5655
5656    #[test]
5657    fn generate_supervisor_with_custom_config() {
5658        let source = r#"
5659            agent Worker {
5660                on start { yield(0); }
5661            }
5662
5663            supervisor AppSupervisor {
5664                strategy: OneForAll
5665
5666                children {
5667                    Worker { restart: Permanent }
5668                }
5669            }
5670
5671            run AppSupervisor;
5672        "#;
5673
5674        let lex_result = lex(source).expect("lexing failed");
5675        let source_arc: Arc<str> = Arc::from(source);
5676        let (program, errors) = parse(lex_result.tokens(), source_arc);
5677        assert!(errors.is_empty());
5678        let program = program.expect("should parse");
5679
5680        // Use custom supervision config (simulating grove.toml values)
5681        let config = CodegenConfig {
5682            runtime_dep: RuntimeDep::default(),
5683            persistence: PersistenceBackend::Memory,
5684            supervision: SupervisionConfig {
5685                max_restarts: 10,
5686                within_seconds: 120,
5687            },
5688            observability: ObservabilityConfig::default(),
5689            ..Default::default()
5690        };
5691        let output = generate_with_full_config(&program, "test", config).main_rs;
5692
5693        // Check custom config values are used
5694        assert!(output.contains("RestartConfig { max_restarts: 10"));
5695        assert!(output.contains("Duration::from_secs(120)"));
5696        assert!(output.contains("Strategy::OneForAll"));
5697    }
5698
5699    // =========================================================================
5700    // Persistence backend configuration tests
5701    // =========================================================================
5702
5703    fn generate_with_backend(source: &str, backend: PersistenceBackend) -> String {
5704        let lex_result = lex(source).expect("lexing failed");
5705        let source_arc: Arc<str> = Arc::from(source);
5706        let (program, errors) = parse(lex_result.tokens(), source_arc);
5707        assert!(errors.is_empty(), "parse errors: {errors:?}");
5708        let program = program.expect("should parse");
5709
5710        let config = CodegenConfig {
5711            runtime_dep: RuntimeDep::default(),
5712            persistence: backend,
5713            supervision: SupervisionConfig::default(),
5714            observability: ObservabilityConfig::default(),
5715            ..Default::default()
5716        };
5717        generate_with_full_config(&program, "test", config).main_rs
5718    }
5719
5720    #[test]
5721    fn generate_persistence_memory_backend() {
5722        let source = r#"
5723            agent Counter {
5724                @persistent count: Int
5725                on start {
5726                    yield(self.count.get());
5727                }
5728            }
5729            run Counter;
5730        "#;
5731
5732        let output = generate_with_backend(source, PersistenceBackend::Memory);
5733        assert!(
5734            output.contains("MemoryCheckpointStore::new()"),
5735            "memory backend should use MemoryCheckpointStore"
5736        );
5737    }
5738
5739    #[test]
5740    fn generate_persistence_sqlite_backend() {
5741        let source = r#"
5742            agent Counter {
5743                @persistent count: Int
5744                on start {
5745                    yield(self.count.get());
5746                }
5747            }
5748            run Counter;
5749        "#;
5750
5751        let output = generate_with_backend(
5752            source,
5753            PersistenceBackend::Sqlite {
5754                path: ".sage/data.db".to_string(),
5755            },
5756        );
5757        assert!(
5758            output.contains("SyncSqliteStore::open(\".sage/data.db\")"),
5759            "sqlite backend should use SyncSqliteStore with correct path"
5760        );
5761    }
5762
5763    #[test]
5764    fn generate_persistence_postgres_backend() {
5765        let source = r#"
5766            agent Counter {
5767                @persistent count: Int
5768                on start {
5769                    yield(self.count.get());
5770                }
5771            }
5772            run Counter;
5773        "#;
5774
5775        let output = generate_with_backend(
5776            source,
5777            PersistenceBackend::Postgres {
5778                url: "postgres://localhost/mydb".to_string(),
5779            },
5780        );
5781        assert!(
5782            output.contains("SyncPostgresStore::connect(\"postgres://localhost/mydb\")"),
5783            "postgres backend should use SyncPostgresStore with correct url"
5784        );
5785    }
5786
5787    #[test]
5788    fn generate_persistence_file_backend() {
5789        let source = r#"
5790            agent Counter {
5791                @persistent count: Int
5792                on start {
5793                    yield(self.count.get());
5794                }
5795            }
5796            run Counter;
5797        "#;
5798
5799        let output = generate_with_backend(
5800            source,
5801            PersistenceBackend::File {
5802                path: "./state".to_string(),
5803            },
5804        );
5805        assert!(
5806            output.contains("SyncFileStore::open(\"./state\")"),
5807            "file backend should use SyncFileStore with correct path"
5808        );
5809    }
5810
5811    #[test]
5812    fn generate_cargo_toml_with_sqlite_feature() {
5813        let source = r#"
5814            agent Counter {
5815                @persistent count: Int
5816                on start { yield(0); }
5817            }
5818            run Counter;
5819        "#;
5820
5821        let lex_result = lex(source).expect("lexing failed");
5822        let source_arc: Arc<str> = Arc::from(source);
5823        let (program, errors) = parse(lex_result.tokens(), source_arc);
5824        assert!(errors.is_empty());
5825        let program = program.expect("should parse");
5826
5827        let config = CodegenConfig {
5828            runtime_dep: RuntimeDep::CratesIo {
5829                version: "1.0.0".to_string(),
5830            },
5831            persistence: PersistenceBackend::Sqlite {
5832                path: ".sage/data.db".to_string(),
5833            },
5834            supervision: SupervisionConfig::default(),
5835            observability: ObservabilityConfig::default(),
5836            ..Default::default()
5837        };
5838        let project = generate_with_full_config(&program, "test", config);
5839
5840        assert!(
5841            project.cargo_toml.contains("persistence-sqlite"),
5842            "Cargo.toml should include persistence-sqlite feature"
5843        );
5844    }
5845
5846    #[test]
5847    fn generate_cargo_toml_no_feature_for_memory() {
5848        let source = r#"
5849            agent Counter {
5850                @persistent count: Int
5851                on start { yield(0); }
5852            }
5853            run Counter;
5854        "#;
5855
5856        let lex_result = lex(source).expect("lexing failed");
5857        let source_arc: Arc<str> = Arc::from(source);
5858        let (program, errors) = parse(lex_result.tokens(), source_arc);
5859        assert!(errors.is_empty());
5860        let program = program.expect("should parse");
5861
5862        let config = CodegenConfig {
5863            runtime_dep: RuntimeDep::CratesIo {
5864                version: "1.0.0".to_string(),
5865            },
5866            persistence: PersistenceBackend::Memory,
5867            supervision: SupervisionConfig::default(),
5868            observability: ObservabilityConfig::default(),
5869            ..Default::default()
5870        };
5871        let project = generate_with_full_config(&program, "test", config);
5872
5873        assert!(
5874            !project.cargo_toml.contains("persistence-"),
5875            "Cargo.toml should NOT include any persistence feature for memory backend"
5876        );
5877    }
5878
5879    // =========================================================================
5880    // Phase 3: Session Types & Algebraic Effects codegen tests
5881    // =========================================================================
5882
5883    #[test]
5884    fn generate_protocol_state_machine() {
5885        let source = r#"
5886            protocol PingPong {
5887                Pinger -> Ponger: Ping
5888                Ponger -> Pinger: Pong
5889            }
5890
5891            record Ping {}
5892            record Pong {}
5893
5894            agent Main {
5895                on start { yield(0); }
5896            }
5897            run Main;
5898        "#;
5899
5900        let output = generate_source(source);
5901
5902        // Check module is generated
5903        assert!(output.contains("mod protocol_ping_pong"));
5904
5905        // Check state enum
5906        assert!(output.contains("pub enum State"));
5907        assert!(output.contains("Initial"));
5908        assert!(output.contains("Done"));
5909
5910        // Check ProtocolStateMachine impl
5911        assert!(output.contains("impl ProtocolStateMachine for State"));
5912        assert!(output.contains("fn state_name(&self)"));
5913        assert!(output.contains("fn can_send(&self, msg_type: &str, from_role: &str)"));
5914        assert!(output.contains("fn can_receive(&self, msg_type: &str, to_role: &str)"));
5915        assert!(output.contains("fn transition(&mut self, msg_type: &str)"));
5916        assert!(output.contains("fn is_terminal(&self)"));
5917        assert!(output.contains("fn protocol_name(&self)"));
5918    }
5919
5920    #[test]
5921    fn generate_effect_handler_config() {
5922        let source = r#"
5923            handler FastLlm handles Infer {
5924                model: "gpt-4o"
5925                temperature: 0.7
5926                max_tokens: 1024
5927            }
5928
5929            agent Main {
5930                on start { yield(0); }
5931            }
5932            run Main;
5933        "#;
5934
5935        let output = generate_source(source);
5936
5937        // Check module is generated (FastLlm -> fast_llm)
5938        assert!(
5939            output.contains("mod handler_fast_llm"),
5940            "Should contain handler module: {}",
5941            output
5942        );
5943
5944        // Check Config struct
5945        assert!(output.contains("pub struct Config"));
5946        assert!(output.contains("pub model: &'static str"));
5947        assert!(output.contains("pub temperature: f64"));
5948        assert!(output.contains("pub max_tokens: i64"));
5949
5950        // Check CONFIG constant
5951        assert!(output.contains("pub const CONFIG: Config"));
5952        assert!(output.contains("model: \"gpt-4o\""));
5953        assert!(output.contains("temperature: 0.7"));
5954        assert!(output.contains("max_tokens: 1024"));
5955    }
5956
5957    #[test]
5958    fn generate_reply_expression_parsing() {
5959        // Note: The current codegen doesn't generate on_message handlers,
5960        // only on_start. This test verifies the reply expression parses
5961        // correctly. Full message handler codegen is a future enhancement.
5962        let source = r#"
5963            record Request {}
5964            record Response { code: Int }
5965
5966            agent Worker receives Request {
5967                on start { yield(0); }
5968                on message(msg: Request) {
5969                    reply(Response { code: 200 });
5970                }
5971            }
5972            run Worker;
5973        "#;
5974
5975        // Just verify it compiles and generates something
5976        let output = generate_source(source);
5977        assert!(output.contains("struct Worker"));
5978        assert!(output.contains("struct Request"));
5979        assert!(output.contains("struct Response"));
5980    }
5981
5982    // =========================================================================
5983    // v2.0: Explicit checkpoint() statement codegen tests
5984    // =========================================================================
5985
5986    #[test]
5987    fn generate_checkpoint_with_persistent_fields() {
5988        let source = r#"
5989            agent Counter {
5990                @persistent count: Int
5991                @persistent name: String
5992
5993                on start {
5994                    checkpoint();
5995                    yield(0);
5996                }
5997            }
5998            run Counter;
5999        "#;
6000
6001        let output = generate_source(source);
6002        // checkpoint() should generate .checkpoint() calls for each @persistent field
6003        assert!(
6004            output.contains("self_count.checkpoint()"),
6005            "Should generate checkpoint call for count field"
6006        );
6007        assert!(
6008            output.contains("self_name.checkpoint()"),
6009            "Should generate checkpoint call for name field"
6010        );
6011    }
6012
6013    #[test]
6014    fn generate_checkpoint_without_persistent_fields() {
6015        let source = r#"
6016            agent Worker {
6017                on start {
6018                    checkpoint();
6019                    yield(0);
6020                }
6021            }
6022            run Worker;
6023        "#;
6024
6025        let output = generate_source(source);
6026        // checkpoint() with no @persistent fields is a no-op comment
6027        assert!(
6028            output.contains("// checkpoint() - no @persistent fields"),
6029            "Should generate no-op comment when no persistent fields"
6030        );
6031    }
6032
6033    // =========================================================================
6034    // WASM target codegen tests
6035    // =========================================================================
6036
6037    fn generate_wasm_source(source: &str) -> String {
6038        let lex_result = lex(source).expect("lexing failed");
6039        let source_arc: Arc<str> = Arc::from(source);
6040        let (program, errors) = parse(lex_result.tokens(), source_arc);
6041        assert!(errors.is_empty(), "parse errors: {errors:?}");
6042        let program = program.expect("should parse");
6043        let config = CodegenConfig {
6044            target: CodegenTarget::Wasm,
6045            ..Default::default()
6046        };
6047        generate_with_full_config(&program, "test", config).main_rs
6048    }
6049
6050    fn generate_wasm_cargo_toml(source: &str) -> String {
6051        let lex_result = lex(source).expect("lexing failed");
6052        let source_arc: Arc<str> = Arc::from(source);
6053        let (program, errors) = parse(lex_result.tokens(), source_arc);
6054        assert!(errors.is_empty(), "parse errors: {errors:?}");
6055        let program = program.expect("should parse");
6056        let config = CodegenConfig {
6057            target: CodegenTarget::Wasm,
6058            ..Default::default()
6059        };
6060        generate_with_full_config(&program, "test", config).cargo_toml
6061    }
6062
6063    #[test]
6064    fn generate_wasm_entry_point() {
6065        let source = r#"
6066            agent Main {
6067                on start {
6068                    print("hello wasm");
6069                    yield(42);
6070                }
6071            }
6072            run Main;
6073        "#;
6074
6075        let output = generate_wasm_source(source);
6076        assert!(
6077            output.contains("#[wasm_bindgen(start)]"),
6078            "Should have wasm_bindgen(start) entry point"
6079        );
6080        assert!(
6081            output.contains("console_error_panic_hook::set_once()"),
6082            "Should set panic hook"
6083        );
6084        assert!(
6085            output.contains("sage_runtime::trace::init()"),
6086            "Should init tracing"
6087        );
6088        assert!(
6089            output.contains("sage_runtime::spawn("),
6090            "Should spawn agent"
6091        );
6092        assert!(
6093            output.contains("wasm_bindgen_futures::spawn_local("),
6094            "Should spawn_local to await result"
6095        );
6096        assert!(
6097            !output.contains("tokio::main"),
6098            "Should NOT have tokio::main"
6099        );
6100        assert!(
6101            !output.contains("ctrl_c"),
6102            "Should NOT have signal handlers"
6103        );
6104    }
6105
6106    #[test]
6107    fn generate_wasm_imports() {
6108        let source = r#"
6109            agent Main {
6110                on start { yield(0); }
6111            }
6112            run Main;
6113        "#;
6114
6115        let output = generate_wasm_source(source);
6116        assert!(
6117            output.contains("use wasm_bindgen::prelude::*;"),
6118            "Should import wasm_bindgen prelude"
6119        );
6120    }
6121
6122    #[test]
6123    fn generate_wasm_cargo_toml_cdylib() {
6124        let source = r#"
6125            agent Main {
6126                on start { yield(0); }
6127            }
6128            run Main;
6129        "#;
6130
6131        let toml = generate_wasm_cargo_toml(source);
6132        assert!(
6133            toml.contains("cdylib"),
6134            "Should have cdylib crate type"
6135        );
6136        assert!(
6137            toml.contains("wasm-bindgen"),
6138            "Should depend on wasm-bindgen"
6139        );
6140        assert!(
6141            toml.contains("console_error_panic_hook"),
6142            "Should depend on console_error_panic_hook"
6143        );
6144        assert!(
6145            !toml.contains("tokio"),
6146            "Should NOT depend on tokio"
6147        );
6148        assert!(
6149            toml.contains("wasm-release"),
6150            "Should have wasm-release profile"
6151        );
6152    }
6153
6154    #[test]
6155    fn generate_wasm_with_tools() {
6156        let source = r#"
6157            agent Main {
6158                use Http
6159
6160                on start {
6161                    let resp = Http.get("https://example.com");
6162                    yield(0);
6163                }
6164            }
6165            run Main;
6166        "#;
6167
6168        let output = generate_wasm_source(source);
6169        assert!(
6170            output.contains("HttpClient::from_env()"),
6171            "Should construct HTTP tool"
6172        );
6173        assert!(
6174            output.contains("wasm_bindgen(start)"),
6175            "Should still have WASM entry point"
6176        );
6177    }
6178
6179    #[test]
6180    fn generate_wasm_with_persistent() {
6181        let source = r#"
6182            agent Main {
6183                @persistent count: Int
6184
6185                on start {
6186                    yield(0);
6187                }
6188            }
6189            run Main;
6190        "#;
6191
6192        let output = generate_wasm_source(source);
6193        assert!(
6194            output.contains("MemoryCheckpointStore::new()"),
6195            "Should use memory checkpoint store on WASM: {output}"
6196        );
6197        assert!(
6198            output.contains("Persisted::new("),
6199            "Should wrap persistent field"
6200        );
6201    }
6202}