1use crate::coding::guidance as coding_guidance;
25use crate::engine::{
26 answer_links_notation, language_aware_answer_for, language_aware_intent_for, normalize_prompt,
27 response_link_for_intent, stable_id, ExecutionRecipe, SelectedRule, SymbolicAnswer,
28};
29use crate::event_log::{build_evidence_links, EventLog};
30use crate::intent_formalization::{
31 record_intent_formalization, recover_write_program_rule, rewrite_bare_program_coreference_rule,
32 select_rule_for_intent, IntentFormalizationCache, IntentFormalizationCacheEntry,
33};
34use crate::language::{detect as detect_language, Language};
35use crate::probability::{ProbabilityDecisionPolicy, ProbabilityStore};
36use crate::rule_synthesis::{try_construct_unknown_rule, try_recall_approved_rule};
37use crate::seed;
38use crate::solver_diagnostics::append_diagnostic_trace;
39use crate::solver_formalization::{record_formalization, record_formalization_selection};
40use crate::solver_handler_oracle::try_unsupported_write_program;
41use crate::solver_handlers::{finalize_simple, try_agent_workspace_task, try_program_blueprint};
42use crate::solver_helpers::{
43 confidence_for, is_agent_opt_in, is_agent_request, is_cache_flush_request,
44 is_destructive_action, is_forget_request, is_inappropriate_content, is_unbounded_autonomy,
45 is_unbounded_loop, record_candidates, record_decomposition, record_validation,
46 requires_external_lookup,
47};
48use crate::solver_synthesis::try_synthesize_from_sub_results;
49use crate::solver_unknown_reasoning::{answer_unknown_prompt, UnknownReasoningConfig};
50use crate::translation::{
51 formalize_prompt_candidates, select_formalization_candidate_with_policy, FormalizationDecision,
52 FormalizationSelectionConfig,
53};
54
55#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
60pub enum ExecutionSurface {
61 #[default]
62 RustLibrary,
63 Cli,
64 HttpServer,
65 Browser,
66 Telegram,
67 DockerMicroservice,
68}
69
70impl ExecutionSurface {
71 #[must_use]
72 pub const fn slug(self) -> &'static str {
73 match self {
74 Self::RustLibrary => "rust_library",
75 Self::Cli => "cli",
76 Self::HttpServer => "http_server",
77 Self::Browser => "browser",
78 Self::Telegram => "telegram",
79 Self::DockerMicroservice => "docker_microservice",
80 }
81 }
82
83 pub(crate) fn from_env_value(raw: &str) -> Option<Self> {
84 match raw.trim().to_ascii_lowercase().as_str() {
85 "rust" | "rust_library" | "library" | "lib" => Some(Self::RustLibrary),
86 "cli" | "terminal" | "shell" => Some(Self::Cli),
87 "http" | "http_server" | "server" | "api" => Some(Self::HttpServer),
88 "browser" | "web" | "wasm" | "demo" => Some(Self::Browser),
89 "telegram" | "telegram_bot" | "bot" => Some(Self::Telegram),
90 "docker" | "docker_microservice" | "container" => Some(Self::DockerMicroservice),
91 _ => None,
92 }
93 }
94}
95
96#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
118pub enum BlueprintComposition {
119 #[default]
121 Composed,
122 Documented,
124}
125
126impl BlueprintComposition {
127 #[must_use]
129 pub const fn slug(self) -> &'static str {
130 match self {
131 Self::Composed => "composed",
132 Self::Documented => "documented",
133 }
134 }
135
136 #[must_use]
140 pub fn from_value(raw: &str) -> Option<Self> {
141 match raw.trim().to_ascii_lowercase().as_str() {
142 "composed" | "compose" | "projection" | "project" | "decomposed" => {
143 Some(Self::Composed)
144 }
145 "documented" | "document" | "full" | "verbatim" | "curated" => Some(Self::Documented),
146 _ => None,
147 }
148 }
149}
150
151#[allow(clippy::struct_excessive_bools)]
158#[derive(Debug, Clone, Copy, PartialEq)]
159pub struct SolverConfig {
160 pub guess_probability: f32,
167 pub follow_up_probability: f32,
175 pub context_sensitivity: f32,
177 pub questioning_rigor: f32,
179 pub temperature: f32,
181 pub max_decomposition_depth: u8,
183 pub recursion_mode: crate::meta_construction::RecursionMode,
188 pub selection_mode: crate::selection::SelectionMode,
194 pub skill_mode: crate::skill_ledger::SkillMode,
200 pub agent_mode: bool,
202 pub diagnostic_mode: bool,
204 pub offline: bool,
206 pub cache_ttl_seconds: u64,
208 pub definition_fusion_by_default: bool,
211 pub associative_project_promotion: bool,
215 pub execution_surface: ExecutionSurface,
217 pub blueprint_composition: BlueprintComposition,
220 pub probability_policy: ProbabilityDecisionPolicy,
226 pub forced_response_language: Option<&'static str>,
237 pub compute_budget: u32,
246}
247
248impl Default for SolverConfig {
249 fn default() -> Self {
250 Self {
251 guess_probability: 0.8,
252 follow_up_probability: 0.75,
253 context_sensitivity: 0.6,
254 questioning_rigor: 0.4,
255 temperature: 0.7,
256 max_decomposition_depth: 4,
257 recursion_mode: crate::meta_construction::RecursionMode::default(),
258 selection_mode: crate::selection::SelectionMode::default(),
259 skill_mode: crate::skill_ledger::SkillMode::default(),
260 agent_mode: false,
261 diagnostic_mode: false,
262 offline: false,
263 cache_ttl_seconds: 60 * 60 * 24 * 60,
264 definition_fusion_by_default: false,
265 associative_project_promotion: true,
266 execution_surface: ExecutionSurface::default(),
267 blueprint_composition: BlueprintComposition::default(),
268 probability_policy: ProbabilityDecisionPolicy::default(),
269 forced_response_language: None,
270 compute_budget: 512,
271 }
272 }
273}
274
275impl SolverConfig {
276 #[must_use]
281 pub fn from_env() -> Self {
282 crate::solver_helpers::config_from_env()
283 }
284}
285
286#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub enum ConversationRole {
291 User,
292 Assistant,
293}
294
295impl ConversationRole {
296 #[must_use]
298 pub const fn slug(self) -> &'static str {
299 match self {
300 Self::User => "user",
301 Self::Assistant => "assistant",
302 }
303 }
304}
305
306#[derive(Debug, Clone, PartialEq, Eq)]
312pub struct ConversationTurn {
313 pub role: ConversationRole,
314 pub content: String,
315}
316
317impl ConversationTurn {
318 #[must_use]
320 pub fn user(content: impl Into<String>) -> Self {
321 Self {
322 role: ConversationRole::User,
323 content: content.into(),
324 }
325 }
326
327 #[must_use]
329 pub fn assistant(content: impl Into<String>) -> Self {
330 Self {
331 role: ConversationRole::Assistant,
332 content: content.into(),
333 }
334 }
335}
336
337#[derive(Debug, Clone, Copy)]
339pub struct UniversalSolver {
340 pub config: SolverConfig,
341}
342
343impl Default for UniversalSolver {
344 fn default() -> Self {
345 Self {
346 config: SolverConfig::from_env(),
347 }
348 }
349}
350
351impl UniversalSolver {
352 #[must_use]
354 pub const fn new(config: SolverConfig) -> Self {
355 Self { config }
356 }
357
358 #[must_use]
363 pub fn solve(&self, prompt: &str) -> SymbolicAnswer {
364 self.solve_with_history(prompt, &[])
365 }
366
367 #[must_use]
372 pub fn solve_with_history(&self, prompt: &str, history: &[ConversationTurn]) -> SymbolicAnswer {
373 self.solve_with_history_and_probability_store(prompt, history, &ProbabilityStore::new())
374 }
375
376 #[must_use]
377 pub fn solve_with_probability_store(
378 &self,
379 prompt: &str,
380 probability_store: &ProbabilityStore,
381 ) -> SymbolicAnswer {
382 self.solve_with_history_and_probability_store(prompt, &[], probability_store)
383 }
384
385 #[must_use]
386 pub fn solve_with_history_and_probability_store(
387 &self,
388 prompt: &str,
389 history: &[ConversationTurn],
390 probability_store: &ProbabilityStore,
391 ) -> SymbolicAnswer {
392 let mut intent_cache = IntentFormalizationCache::new();
393 self.solve_with_history_probability_store_and_intent_cache(
394 prompt,
395 history,
396 probability_store,
397 &mut intent_cache,
398 )
399 }
400
401 pub(crate) fn solve_with_history_probability_store_and_intent_cache(
402 &self,
403 prompt: &str,
404 history: &[ConversationTurn],
405 probability_store: &ProbabilityStore,
406 intent_cache: &mut IntentFormalizationCache,
407 ) -> SymbolicAnswer {
408 let mut log = EventLog::new();
409
410 let _forced_language_guard = crate::language::set_forced_language(
415 self.config
416 .forced_response_language
417 .and_then(crate::language::from_slug),
418 );
419
420 for turn in history {
421 let kind: &'static str = match turn.role {
422 ConversationRole::User => "prior_turn:user",
423 ConversationRole::Assistant => "prior_turn:assistant",
424 };
425 log.append(kind, turn.content.clone());
426 }
427
428 log.append("impulse", prompt.to_owned());
429
430 let language = detect_language(prompt);
431 log.append("language", language.slug().to_owned());
432 probability_store.replay_into_event_log(&mut log, self.config.offline);
433
434 let intent_entry = if let Some(formalization) = intent_cache.get(prompt).cloned() {
435 IntentFormalizationCacheEntry {
436 formalization,
437 cache_hit: true,
438 }
439 } else {
440 let formalization_candidates = formalize_prompt_candidates(prompt, language.slug());
441 let formalization_selection = select_formalization_candidate_with_policy(
442 &formalization_candidates,
443 FormalizationSelectionConfig {
444 temperature: self.config.temperature,
445 guess_probability: self.config.guess_probability,
446 questioning_rigor: self.config.questioning_rigor,
447 },
448 prompt,
449 probability_store,
450 self.config.offline,
451 self.config.probability_policy,
452 );
453 record_formalization_selection(&mut log, &formalization_selection);
454 if let FormalizationDecision::Clarify { question, .. } =
455 &formalization_selection.decision
456 {
457 return finalize_simple(
458 prompt,
459 &mut log,
460 "clarify_interpretation",
461 "response:clarify_interpretation",
462 question,
463 0.5,
464 );
465 }
466 if let Some(candidate) = formalization_selection.selected_candidate() {
467 record_formalization(&mut log, candidate);
468 }
469 intent_cache.formalize_or_insert(
470 prompt,
471 language.slug(),
472 formalization_selection.selected_candidate(),
473 )
474 };
475 record_intent_formalization(&mut log, &intent_entry);
476 let intent_formalization = intent_entry.formalization;
477
478 if let Some(answer) = crate::requirement_contradiction::detect_and_report(
485 prompt,
486 language,
487 history,
488 self.config.temperature,
489 &mut log,
490 ) {
491 return answer;
492 }
493
494 crate::meta_core::record_meta_core(
501 &mut log,
502 &intent_formalization,
503 self.config.max_decomposition_depth,
504 self.config.recursion_mode,
505 self.config.selection_mode,
506 self.config.skill_mode,
507 );
508
509 log.append("search:local", prompt.to_owned());
510
511 let sub_impulses =
512 record_decomposition(&mut log, prompt, self.config.max_decomposition_depth);
513 let sub_results =
514 self.solve_sub_impulses(&mut log, &sub_impulses, probability_store, intent_cache);
515
516 let selected_rule = select_rule_for_intent(&intent_formalization);
517 let recalled_rule = try_recall_approved_rule(selected_rule, prompt, history, &mut log);
518 let rule = try_construct_unknown_rule(recalled_rule, prompt, history, &mut log);
519 let rule =
520 if let Some(rewrite) = rewrite_bare_program_coreference_rule(&rule, prompt, history) {
521 log.append("write_program_coreference_rewrite", rewrite.trace);
522 rewrite.rule
523 } else {
524 rule
525 };
526
527 let rule = if matches!(rule, SelectedRule::UnsupportedWriteProgram { .. }) {
533 let recovery = recover_write_program_rule(rule, prompt, history);
534 if let Some(trace) = recovery.trace {
535 log.append("write_program_context_recovery", trace);
536 }
537 if let Some(plan) = recovery.plan {
538 log.append("write_program_plan", plan);
539 }
540 recovery.rule
541 } else {
542 rule
543 };
544
545 if !matches!(rule, SelectedRule::WriteProgram(_)) {
551 let language_hint = match &rule {
552 SelectedRule::UnsupportedWriteProgram { language, .. } => language.as_deref(),
553 _ => None,
554 };
555 let normalized_for_blueprint = normalize_prompt(prompt);
556 if let Some(answer) = try_program_blueprint(
557 prompt,
558 &normalized_for_blueprint,
559 language_hint,
560 self.config.blueprint_composition,
561 &mut log,
562 ) {
563 return answer;
564 }
565 }
566
567 if let SelectedRule::UnsupportedWriteProgram { task, language } = &rule {
579 if let Some(answer) = try_unsupported_write_program(
580 prompt,
581 task.as_deref(),
582 language.as_deref(),
583 self.config.blueprint_composition,
584 &mut log,
585 ) {
586 return answer;
587 }
588 }
589
590 if let Some(answer) = try_synthesize_from_sub_results(
591 prompt,
592 &mut log,
593 &sub_results,
594 probability_store,
595 self.config,
596 ) {
597 return answer;
598 }
599
600 let is_concrete_write_program = matches!(rule, SelectedRule::WriteProgram(_));
606 if !is_concrete_write_program {
607 if let Some(answer) = crate::meta_method_dispatch::try_dispatch(
608 self,
609 prompt,
610 &intent_formalization,
611 history,
612 &mut log,
613 ) {
614 return answer;
615 }
616 }
617
618 if let Some(answer) = self.handle_policy(prompt, &mut log, language) {
619 return answer;
620 }
621
622 if matches!(rule, SelectedRule::Unknown) {
623 let intent = language_aware_intent_for(&rule, language);
624 record_candidates(&mut log, prompt, &intent);
625 if let Some(choice) = record_validation(&mut log, prompt) {
626 let response_link = response_link_for_intent(&rule, &intent);
627 return finalize_simple(
628 prompt,
629 &mut log,
630 &intent,
631 &response_link,
632 &choice.answer,
633 1.0,
634 );
635 }
636 if let Some(answer) =
640 crate::solver_terminal::try_terminal_command(prompt, language, &mut log)
641 {
642 return answer;
643 }
644 if let Some(answer) =
650 crate::solver_search::try_budget_search(prompt, &mut log, self.config)
651 {
652 return answer;
653 }
654 if requires_external_lookup(prompt) {
655 self.record_external_search(&mut log, prompt);
656 }
657 return answer_unknown_prompt(
658 prompt,
659 language,
660 &mut log,
661 UnknownReasoningConfig {
662 questioning_rigor: self.config.questioning_rigor,
663 offline: self.config.offline,
664 },
665 );
666 }
667
668 let intent = language_aware_intent_for(&rule, language);
669 log.append("intent", intent.clone());
670
671 if let SelectedRule::WriteProgram(spec) = &rule {
672 log.append(
673 "execution_status",
674 spec.language.execution.status.label().to_owned(),
675 );
676 log.append(
677 "execution_environment",
678 spec.language.execution.environment.to_owned(),
679 );
680 log.append("program_parameter:language", spec.language.slug.to_owned());
681 log.append("program_parameter:task", spec.task.slug.to_owned());
682 log.append("program_parameters", spec.parameter_summary());
683 log.append("legacy_intent", spec.legacy_intent());
684 }
685
686 record_candidates(&mut log, prompt, &intent);
687
688 let validation_choice = record_validation(&mut log, prompt);
689 if validation_choice.is_none() && log.first_of("validation").is_none() {
690 log.append(
691 "validation",
692 "accepted_without_extra_constraints".to_owned(),
693 );
694 }
695 let prior = coding_guidance::history_has_prior_code(history);
696 let base_answer = match (&validation_choice, &rule) {
697 (Some(choice), SelectedRule::Unknown) => choice.answer.clone(),
698 _ => language_aware_answer_for(&rule, language, prompt, prior),
699 };
700
701 let response_link = response_link_for_intent(&rule, &intent);
702 log.append("response", response_link.clone());
703
704 log.append("trace:simplification", "smallest_sufficient".to_owned());
705 let trace_id = log.append("trace", intent.clone());
706
707 let evidence_links = build_evidence_links(prompt, &log, &response_link);
708 let links_notation = answer_links_notation(prompt, &intent, &base_answer, &log, &trace_id);
709 let thinking_steps = log.thinking_steps_for_answer(&base_answer);
710 let answer =
711 append_diagnostic_trace(self.config.diagnostic_mode, base_answer, &links_notation);
712
713 let execution_recipe = match &rule {
714 SelectedRule::WriteProgram(spec) => Some(Box::new(ExecutionRecipe {
715 language: spec.language.code_fence.to_owned(),
716 source: crate::code_editing::apply_inline_hello_world_source_replacement(
717 prompt,
718 spec.template.code,
719 *spec,
720 ),
721 path: spec.language.save_as.to_owned(),
722 commands: spec
723 .language
724 .execution
725 .check_command
726 .into_iter()
727 .chain(std::iter::once(spec.language.execution.run_command))
728 .map(str::to_owned)
729 .collect(),
730 })),
731 _ => None,
732 };
733
734 SymbolicAnswer {
735 intent,
736 answer,
737 confidence: confidence_for(&rule, validation_choice.as_ref()),
738 evidence_links,
739 thinking_steps,
740 links_notation,
741 execution_recipe,
742 }
743 }
744
745 fn handle_policy(
746 &self,
747 prompt: &str,
748 log: &mut EventLog,
749 language: Language,
750 ) -> Option<SymbolicAnswer> {
751 let normalized = prompt.to_lowercase();
752
753 if is_inappropriate_content(&normalized) {
754 log.append("policy:inappropriate_content", prompt.to_owned());
755 let lang_slug = language.slug();
756 let fallback = "That message contains inappropriate content. Please keep the conversation respectful.";
757 let body = seed::response_for("inappropriate_content", lang_slug)
758 .unwrap_or_else(|| String::from(fallback));
759 return Some(Self::finalize_policy(
760 prompt,
761 log,
762 "inappropriate_content",
763 language,
764 &body,
765 ));
766 }
767
768 if is_unbounded_autonomy(&normalized) && !is_agent_opt_in(&normalized) {
769 log.append("policy:chat_bounded_autonomy", prompt.to_owned());
770 return Some(Self::finalize_policy(
771 prompt,
772 log,
773 "bounded_autonomy",
774 language,
775 concat!(
776 "I can only run a bounded chat reply per message. To take repeated, ",
777 "open-ended actions I need an explicit opt-in to agent mode, and agent ",
778 "mode runs in an isolated sandbox so the host stays safe."
779 ),
780 ));
781 }
782
783 if is_forget_request(&normalized) {
784 log.append("policy:add_only_history", prompt.to_owned());
785 return Some(Self::finalize_policy(
786 prompt,
787 log,
788 "add_only_history",
789 language,
790 concat!(
791 "The link network is append-only. To retract a fact, send the explicit ",
792 "retraction protocol; it will append a superseding event without erasing ",
793 "history."
794 ),
795 ));
796 }
797
798 if is_cache_flush_request(&normalized) {
799 log.append(
800 "policy:cache_flush_requires_confirmation",
801 prompt.to_owned(),
802 );
803 return Some(Self::finalize_policy(
804 prompt,
805 log,
806 "cache_flush_requires_confirmation",
807 language,
808 "Flushing the source cache is an auditable action. Confirm explicitly.",
809 ));
810 }
811
812 if is_agent_request(&normalized) && is_destructive_action(&normalized) {
813 log.append("agent_mode:opted_in", prompt.to_owned());
814 log.append(
815 "policy:destructive_action_requires_confirmation",
816 prompt.to_owned(),
817 );
818 return Some(Self::finalize_policy(
819 prompt,
820 log,
821 "destructive_action_requires_confirmation",
822 language,
823 concat!(
824 "Destructive agent actions require an explicit human confirmation. ",
825 "The action will run inside an isolated sandbox once confirmed."
826 ),
827 ));
828 }
829
830 if is_agent_request(&normalized) && is_unbounded_loop(&normalized) {
831 log.append("agent_mode:opted_in", prompt.to_owned());
832 log.append("policy:agent_time_budget", prompt.to_owned());
833 return Some(Self::finalize_policy(
834 prompt,
835 log,
836 "agent_time_budget",
837 language,
838 concat!(
839 "Agent execution is bounded by a documented time budget; unbounded ",
840 "loops are refused. Re-send a bounded version inside an isolated sandbox."
841 ),
842 ));
843 }
844
845 if is_agent_request(&normalized) {
846 if self.config.execution_surface != ExecutionSurface::HttpServer {
852 if let Some(answer) = try_agent_workspace_task(prompt, &normalized, log) {
853 return Some(answer);
854 }
855 }
856 log.append("agent_mode:opted_in", prompt.to_owned());
857 log.append("agent_mode:active", prompt.to_owned());
858 log.append("action_log", prompt.to_owned());
859 return Some(Self::finalize_policy(
860 prompt,
861 log,
862 "agent_action",
863 language,
864 concat!(
865 "Agent mode is opted in for this message. The action will run inside ",
866 "an isolated sandbox (docker, webvm or sandbox-equivalent) and every ",
867 "step will be appended to the action log."
868 ),
869 ));
870 }
871
872 None
873 }
874
875 fn finalize_policy(
876 prompt: &str,
877 log: &mut EventLog,
878 intent_slug: &str,
879 _language: Language,
880 body: &str,
881 ) -> SymbolicAnswer {
882 let intent = format!("policy_{intent_slug}");
883 let response_link = format!("response:policy:{intent_slug}");
884 finalize_simple(prompt, log, &intent, &response_link, body, 0.5)
885 }
886
887 fn record_external_search(&self, log: &mut EventLog, prompt: &str) {
888 if self.config.offline {
889 log.append("search:external", "skipped:offline".to_owned());
890 return;
891 }
892 log.append("search:external", prompt.to_owned());
893 let source_id = stable_id("source", prompt);
894 let fetched_at = "1970-01-01T00:00:00Z";
895 let sha256 = stable_id("sha256", prompt);
896 log.append(
897 "source:http",
898 format!("https://example.org/{source_id} fetched_at={fetched_at} sha256={sha256}"),
899 );
900 log.append("cache_hit", source_id);
901 }
902}
903
904#[must_use]
908pub fn solve(prompt: &str) -> SymbolicAnswer {
909 UniversalSolver::default().solve(prompt)
910}
911
912#[must_use]
914pub fn solve_with_history(prompt: &str, history: &[ConversationTurn]) -> SymbolicAnswer {
915 UniversalSolver::default().solve_with_history(prompt, history)
916}