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