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 agents::HOST_VIEWS,
241 embedding::HOST_VIEWS,
242 fs::HOST_VIEWS,
243 http::HOST_VIEWS,
244 session::HOST_VIEWS,
245 skills::HOST_VIEWS,
246 url::HOST_VIEWS,
247 ]
248 .into_iter()
249 .flatten()
250 .copied()
251}
252
253pub(crate) fn install_store_bound(
255 linker: &mut Linker<StoreData>,
256 store: &mut wasmtime::Store<StoreData>,
257) -> wasmtime::Result<()> {
258 git::class::install(linker, store)
259}
260
261#[cfg(test)]
262mod optional_tests;
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 #[test]
274 fn every_runtime_generic_host_function_is_a_checked_call() {
275 let all = Stdlib::core()
276 .with(OptionalPackage::Agents)
277 .with(OptionalPackage::Skills);
278 let mut seen = Vec::new();
279 for defs in all.package_declarations() {
280 for mangled in &defs.runtime_generics {
281 assert!(
282 checked_call::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:http#Response#json",
295 "submilli:llm#batch",
296 "submilli:llm#call",
297 "submilli:session#get",
298 ],
299 "the host functions that read their type descriptors"
300 );
301 }
302
303 #[test]
307 fn every_host_backed_interface_has_a_view_of_its_properties() {
308 let mut declared = std::collections::BTreeMap::new();
309 let all = Stdlib::core()
310 .with(OptionalPackage::Agents)
311 .with(OptionalPackage::Skills);
312 for defs in all.package_declarations() {
313 for (name, symbol) in &defs.types {
314 if let crate::TypeKind::Interface {
315 properties,
316 dispatch: crate::Dispatch::Direct,
317 ..
318 } = &symbol.kind
319 && !properties.is_empty()
320 {
321 let names: std::collections::BTreeSet<String> =
322 properties.keys().cloned().collect();
323 declared.insert(
324 (defs.package_name.clone(), name.as_str().to_string()),
325 names,
326 );
327 }
328 }
329 }
330 let mut viewed = std::collections::BTreeMap::new();
331 for view in host_views() {
332 let names: std::collections::BTreeSet<String> = view
333 .properties
334 .iter()
335 .map(|(name, _)| (*name).to_string())
336 .collect();
337 assert_eq!(
338 names.len(),
339 view.properties.len(),
340 "{} repeats a property",
341 view.interface
342 );
343 viewed.insert((view.module.to_string(), view.interface.to_string()), names);
344 }
345 assert_eq!(viewed, declared);
346 }
347
348 #[test]
352 fn views_sharing_a_backing_struct_show_the_same_properties() {
353 let engine = crate::RuntimeConfig::default().engine().unwrap();
354 let mut seen: Vec<(wasmtime::StructType, &abi::HostView)> = Vec::new();
355 for view in host_views() {
356 let backing = (view.backing)(&engine).unwrap();
357 for (other, earlier) in &seen {
358 if *other == backing {
359 assert_eq!(
360 earlier.properties, view.properties,
361 "{} and {} share a layout",
362 earlier.interface, view.interface
363 );
364 }
365 }
366 seen.push((backing, view));
367 }
368 }
369
370 fn every_set() -> Vec<Stdlib> {
372 let mut sets = vec![Stdlib::core()];
373 for package in OptionalPackage::ALL {
374 let with: Vec<Stdlib> = sets.iter().map(|set| set.with(*package)).collect();
375 sets.extend(with);
376 }
377 sets
378 }
379
380 #[test]
381 fn declarations_are_sorted_for_every_set() {
382 for set in every_set() {
383 let names: Vec<String> = set
384 .package_declarations()
385 .into_iter()
386 .map(|defs| defs.package_name)
387 .collect();
388 let mut sorted = names.clone();
389 sorted.sort();
390 assert_eq!(names, sorted, "{set:?}");
391 }
392 }
393
394 #[test]
395 fn a_set_contains_exactly_its_packages() {
396 for set in every_set() {
397 let names: Vec<String> = set
398 .package_declarations()
399 .into_iter()
400 .map(|defs| defs.package_name)
401 .collect();
402 for name in &names {
403 assert!(set.contains_module(name), "{set:?} misses {name}");
404 }
405 for package in OptionalPackage::ALL {
406 assert_eq!(
407 names.iter().any(|name| name == package.module_name()),
408 set.enables(*package),
409 "{set:?} and {package:?}"
410 );
411 }
412 }
413 }
414
415 #[test]
416 fn core_is_the_global_list() {
417 let core: Vec<String> = Stdlib::core()
418 .package_declarations()
419 .into_iter()
420 .map(|defs| defs.package_name)
421 .collect();
422 let global: Vec<String> = stdlib_package_declarations()
423 .into_iter()
424 .map(|defs| defs.package_name)
425 .collect();
426 assert_eq!(core, global);
427 assert!(!Stdlib::core().contains_module(test::MODULE_NAME));
428 }
429}