pub mod context_reference; pub mod fragments; pub mod ids; pub mod journal; pub mod prefix_cache; pub mod request; pub mod role; pub mod secret_eq; pub mod session; pub mod tool_parser; pub use context_reference::{ ContextReference, ContextReferenceKind, ContextReferenceSource, MediaAttachmentReference, media_attachment_references, }; use codewhale_config::{ConfigToml, ProviderKind}; use codewhale_hooks::HookDispatcher; use codewhale_state::StateStore; /// Compatibility configuration and hooks. /// /// Conversation execution and durable history belong to the held canonical /// RuntimeThreadManager/Engine. Legacy State data is retained for read-only /// recovery and bound alias publication, never as a second transcript writer. pub struct Runtime { pub config: ConfigToml, state: StateStore, pub hooks: HookDispatcher, } impl Runtime { pub fn new(config: ConfigToml, state: StateStore, hooks: HookDispatcher) -> Self { Self { config, state, hooks, } } /// Existing compatibility data and receipts; no transcript controller. pub fn state_store(&self) -> &StateStore { &self.state } pub fn update_config(&mut self, config: ConfigToml) { self.config = config; } /// Returns the default model provider from the resolved configuration. pub fn provider_default(&self) -> ProviderKind { self.config.provider } } /// How many leading entries of `offered` repeat the end of `persisted`: the /// longest suffix of `persisted` that is also a prefix of `offered`. /// Longest persisted suffix repeated by an offered history prefix. Linear /// work preserves genuine repeated messages without an unbounded nested scan. pub fn persisted_overlap(persisted: &[T], offered: &[T]) -> usize { if offered.is_empty() { return 0; } let mut failure = vec![0; offered.len()]; for index in 1..offered.len() { let mut matched = failure[index - 1]; while matched > 0 && offered[index] != offered[matched] { matched = failure[matched - 1]; } if offered[index] == offered[matched] { matched += 1; } failure[index] = matched; } let mut matched = 0; for item in persisted { if matched == offered.len() { matched = failure[matched - 1]; } while matched > 0 && *item != offered[matched] { matched = failure[matched - 1]; } if *item == offered[matched] { matched += 1; } } matched } #[cfg(test)] mod tests { use super::*; use uuid::Uuid; fn temp_core_state(name: &str) -> StateStore { let dir = std::env::temp_dir().join(format!("codewhale-core-{name}-{}", Uuid::new_v4().simple())); std::fs::create_dir_all(&dir).expect("create temp state dir"); StateStore::open(Some(dir.join("state.db"))).expect("open state store") } #[test] fn runtime_bookkeeping_keeps_the_legacy_store_read_only() { let store = temp_core_state("read-only-runtime"); let before = store.list_threads(Default::default()).unwrap(); let mut runtime = Runtime::new(ConfigToml::default(), store, HookDispatcher::default()); runtime.update_config(ConfigToml::default()); assert_eq!( runtime .state_store() .list_threads(Default::default()) .unwrap() .len(), before.len() ); let reopened = Runtime::new( runtime.config.clone(), runtime.state_store().clone(), HookDispatcher::default(), ); assert!( reopened .state_store() .list_threads(Default::default()) .unwrap() .is_empty() ); } }