1pub(crate) mod abi;
12pub mod agents;
13pub mod capabilities;
14pub mod code;
15pub mod crypto;
16pub(crate) mod dot_segments;
17pub mod embedding;
18pub mod fs;
19pub mod git;
20pub mod http;
21pub(crate) mod json_schema;
22pub mod llm;
23pub mod secrets;
24pub mod security;
25pub mod session;
26pub mod shared;
27pub mod skills;
28pub mod test;
34pub mod url;
35pub mod uuid;
36pub mod variables;
37
38use wasmtime::Linker;
39
40use crate::PackageDeclaration;
41use crate::runtime::StoreData;
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
50#[non_exhaustive]
51pub enum OptionalPackage {
52 Agents,
54 Skills,
56}
57
58impl OptionalPackage {
59 pub const ALL: &'static [OptionalPackage] = &[Self::Agents, Self::Skills];
60
61 pub const fn module_name(self) -> &'static str {
62 match self {
63 Self::Agents => agents::MODULE_NAME,
64 Self::Skills => skills::MODULE_NAME,
65 }
66 }
67
68 pub fn from_module_name(name: &str) -> Option<Self> {
69 Self::ALL
70 .iter()
71 .copied()
72 .find(|package| package.module_name() == name)
73 }
74
75 fn package_declaration(self) -> PackageDeclaration {
76 match self {
77 Self::Agents => agents::package_declaration(),
78 Self::Skills => skills::package_declaration(),
79 }
80 }
81
82 fn install(self, linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
83 match self {
84 Self::Agents => agents::install(linker),
85 Self::Skills => skills::install(linker),
86 }
87 }
88
89 const fn bit(self) -> u8 {
90 match self {
91 Self::Agents => 1,
92 Self::Skills => 1 << 1,
93 }
94 }
95}
96
97#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
105pub struct Stdlib {
106 optional: u8,
107}
108
109impl Stdlib {
110 pub const fn core() -> Self {
112 Stdlib { optional: 0 }
113 }
114
115 #[must_use]
116 pub const fn with(self, package: OptionalPackage) -> Self {
117 Stdlib {
118 optional: self.optional | package.bit(),
119 }
120 }
121
122 pub const fn enables(self, package: OptionalPackage) -> bool {
123 self.optional & package.bit() != 0
124 }
125
126 pub fn optional_packages(self) -> impl Iterator<Item = OptionalPackage> {
127 OptionalPackage::ALL
128 .iter()
129 .copied()
130 .filter(move |package| self.enables(*package))
131 }
132
133 pub fn contains_module(self, name: &str) -> bool {
135 OptionalPackage::from_module_name(name).map_or_else(
136 || {
137 core_package_declarations()
138 .iter()
139 .any(|defs| defs.package_name == name)
140 },
141 |package| self.enables(package),
142 )
143 }
144
145 pub fn package_declarations(self) -> Vec<PackageDeclaration> {
147 let mut declarations = core_package_declarations();
148 declarations.extend(
149 self.optional_packages()
150 .map(OptionalPackage::package_declaration),
151 );
152 declarations.sort_by(|a, b| a.package_name.cmp(&b.package_name));
154 declarations
155 }
156
157 pub fn install_host_functions(self, linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
158 install_core_host_functions(linker)?;
159 for package in self.optional_packages() {
160 package.install(linker)?;
161 }
162 Ok(())
163 }
164}
165
166pub(crate) fn unavailable_import(module: &str) -> Option<(String, Vec<String>)> {
172 if module == test::MODULE_NAME {
173 return Some((
174 format!("package `{module}` not found"),
175 vec![
176 "`submilli:test` is only available to test files run via `submilli build test`; it is not importable from a program run with `submilli run`"
177 .to_string(),
178 ],
179 ));
180 }
181 let package = OptionalPackage::from_module_name(module)?;
182 Some((
183 format!("package `{module}` is not available in this harness"),
184 vec![format!(
185 "`{}` exists only where the harness running the program provides it",
186 package.module_name()
187 )],
188 ))
189}
190
191pub fn stdlib_package_declarations() -> Vec<PackageDeclaration> {
193 Stdlib::core().package_declarations()
194}
195
196pub fn install_host_functions(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
197 Stdlib::core().install_host_functions(linker)
198}
199
200fn core_package_declarations() -> Vec<PackageDeclaration> {
201 vec![
202 code::package_declaration(),
204 crypto::package_declaration(),
205 embedding::package_declaration(),
206 fs::package_declaration(),
207 git::package_declaration(),
208 http::package_declaration(),
209 llm::package_declaration(),
210 secrets::package_declaration(),
211 security::package_declaration(),
212 session::package_declaration(),
213 url::package_declaration(),
214 uuid::package_declaration(),
215 variables::package_declaration(),
216 ]
217}
218
219fn install_core_host_functions(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
220 code::install(linker)?;
221 crypto::install(linker)?;
222 embedding::install(linker)?;
223 fs::install(linker)?;
224 git::install(linker)?;
225 http::install(linker)?;
226 llm::install(linker)?;
227 secrets::install(linker)?;
228 security::install(linker)?;
229 session::install(linker)?;
230 url::install(linker)?;
231 uuid::install(linker)?;
232 variables::install(linker)?;
233 Ok(())
234}
235
236pub(crate) fn host_views() -> impl Iterator<Item = &'static abi::HostView> {
238 [
239 agents::HOST_VIEWS,
240 embedding::HOST_VIEWS,
241 fs::HOST_VIEWS,
242 http::HOST_VIEWS,
243 session::HOST_VIEWS,
244 skills::HOST_VIEWS,
245 url::HOST_VIEWS,
246 ]
247 .into_iter()
248 .flatten()
249 .copied()
250}
251
252pub(crate) fn install_store_bound(
254 linker: &mut Linker<StoreData>,
255 store: &mut wasmtime::Store<StoreData>,
256) -> wasmtime::Result<()> {
257 git::class::install(linker, store)
258}
259
260#[cfg(test)]
261mod optional_tests;
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[test]
273 fn every_runtime_generic_host_function_is_a_checked_call() {
274 let all = Stdlib::core()
275 .with(OptionalPackage::Agents)
276 .with(OptionalPackage::Skills);
277 let mut seen = Vec::new();
278 for defs in all.package_declarations() {
279 for mangled in &defs.runtime_generics {
280 assert!(
281 session::declaration::is_checked_get(mangled)
282 || json_schema::schema_checked_call(mangled).is_some(),
283 "{} is runtime-generic but the typechecker does not gate it",
284 mangled.as_str()
285 );
286 seen.push(mangled.as_str().to_string());
287 }
288 }
289 seen.sort();
290 assert_eq!(
291 seen,
292 [
293 "submilli:agents#run",
294 "submilli:llm#batch",
295 "submilli:llm#call",
296 "submilli:session#get",
297 ],
298 "the host functions that read their type descriptors"
299 );
300 }
301
302 #[test]
306 fn every_host_backed_interface_has_a_view_of_its_properties() {
307 let mut declared = std::collections::BTreeMap::new();
308 let all = Stdlib::core()
309 .with(OptionalPackage::Agents)
310 .with(OptionalPackage::Skills);
311 for defs in all.package_declarations() {
312 for (name, symbol) in &defs.types {
313 if let crate::TypeKind::Interface {
314 properties,
315 dispatch: crate::Dispatch::Direct,
316 ..
317 } = &symbol.kind
318 && !properties.is_empty()
319 {
320 let names: std::collections::BTreeSet<String> =
321 properties.keys().cloned().collect();
322 declared.insert(
323 (defs.package_name.clone(), name.as_str().to_string()),
324 names,
325 );
326 }
327 }
328 }
329 let mut viewed = std::collections::BTreeMap::new();
330 for view in host_views() {
331 let names: std::collections::BTreeSet<String> = view
332 .properties
333 .iter()
334 .map(|(name, _)| (*name).to_string())
335 .collect();
336 assert_eq!(
337 names.len(),
338 view.properties.len(),
339 "{} repeats a property",
340 view.interface
341 );
342 viewed.insert((view.module.to_string(), view.interface.to_string()), names);
343 }
344 assert_eq!(viewed, declared);
345 }
346
347 #[test]
351 fn views_sharing_a_backing_struct_show_the_same_properties() {
352 let engine = crate::RuntimeConfig::default().engine().unwrap();
353 let mut seen: Vec<(wasmtime::StructType, &abi::HostView)> = Vec::new();
354 for view in host_views() {
355 let backing = (view.backing)(&engine).unwrap();
356 for (other, earlier) in &seen {
357 if *other == backing {
358 assert_eq!(
359 earlier.properties, view.properties,
360 "{} and {} share a layout",
361 earlier.interface, view.interface
362 );
363 }
364 }
365 seen.push((backing, view));
366 }
367 }
368
369 fn every_set() -> Vec<Stdlib> {
371 let mut sets = vec![Stdlib::core()];
372 for package in OptionalPackage::ALL {
373 let with: Vec<Stdlib> = sets.iter().map(|set| set.with(*package)).collect();
374 sets.extend(with);
375 }
376 sets
377 }
378
379 #[test]
380 fn declarations_are_sorted_for_every_set() {
381 for set in every_set() {
382 let names: Vec<String> = set
383 .package_declarations()
384 .into_iter()
385 .map(|defs| defs.package_name)
386 .collect();
387 let mut sorted = names.clone();
388 sorted.sort();
389 assert_eq!(names, sorted, "{set:?}");
390 }
391 }
392
393 #[test]
394 fn a_set_contains_exactly_its_packages() {
395 for set in every_set() {
396 let names: Vec<String> = set
397 .package_declarations()
398 .into_iter()
399 .map(|defs| defs.package_name)
400 .collect();
401 for name in &names {
402 assert!(set.contains_module(name), "{set:?} misses {name}");
403 }
404 for package in OptionalPackage::ALL {
405 assert_eq!(
406 names.iter().any(|name| name == package.module_name()),
407 set.enables(*package),
408 "{set:?} and {package:?}"
409 );
410 }
411 }
412 }
413
414 #[test]
415 fn core_is_the_global_list() {
416 let core: Vec<String> = Stdlib::core()
417 .package_declarations()
418 .into_iter()
419 .map(|defs| defs.package_name)
420 .collect();
421 let global: Vec<String> = stdlib_package_declarations()
422 .into_iter()
423 .map(|defs| defs.package_name)
424 .collect();
425 assert_eq!(core, global);
426 assert!(!Stdlib::core().contains_module(test::MODULE_NAME));
427 }
428}