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 mod llm;
22pub mod secrets;
23pub mod security;
24pub mod session;
25pub mod shared;
26pub mod skills;
27pub mod test;
33pub mod url;
34pub mod uuid;
35pub mod variables;
36
37use wasmtime::Linker;
38
39use crate::runtime::StoreData;
40use crate::{MangledName, PackageDeclaration, Type};
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
49#[non_exhaustive]
50pub enum OptionalPackage {
51 Agents,
53 Skills,
55}
56
57impl OptionalPackage {
58 pub const ALL: &'static [OptionalPackage] = &[Self::Agents, Self::Skills];
59
60 pub const fn module_name(self) -> &'static str {
61 match self {
62 Self::Agents => agents::MODULE_NAME,
63 Self::Skills => skills::MODULE_NAME,
64 }
65 }
66
67 pub fn from_module_name(name: &str) -> Option<Self> {
68 Self::ALL
69 .iter()
70 .copied()
71 .find(|package| package.module_name() == name)
72 }
73
74 fn package_declaration(self) -> PackageDeclaration {
75 match self {
76 Self::Agents => agents::package_declaration(),
77 Self::Skills => skills::package_declaration(),
78 }
79 }
80
81 fn install(self, linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
82 match self {
83 Self::Agents => agents::install(linker),
84 Self::Skills => skills::install(linker),
85 }
86 }
87
88 const fn bit(self) -> u8 {
89 match self {
90 Self::Agents => 1,
91 Self::Skills => 1 << 1,
92 }
93 }
94}
95
96pub(crate) struct SchemaCheckedCall {
101 pub callee: &'static str,
103 pub example: &'static str,
105 pub checks: &'static str,
107 pub answerer: &'static str,
109 pub written_params: &'static str,
111 pub written_param_count: usize,
112 pub untyped_hint: &'static str,
115 pub untyped_hint_for_unknown: &'static str,
117 pub untyped_hint_for_no_schema: &'static str,
119 pub untyped_result: Type,
121}
122
123impl SchemaCheckedCall {
124 pub fn schema_argument_ordinal(&self) -> &'static str {
126 match self.written_param_count {
127 0 => "first",
128 1 => "second",
129 2 => "third",
130 3 => "fourth",
131 _ => "last",
132 }
133 }
134}
135
136pub(crate) fn schema_checked_call(mangled: &MangledName) -> Option<SchemaCheckedCall> {
138 if llm::declaration::is_checked_call(mangled) {
139 return Some(SchemaCheckedCall {
140 callee: "llm.call",
141 example: "llm.call<Severity>(model, prompt)",
142 checks: "the model's response",
143 answerer: "the model",
144 written_params: "the model and the prompt",
145 written_param_count: 2,
146 untyped_hint: "read the `Completion` envelope yourself",
147 untyped_hint_for_unknown: "read the `Completion` envelope's `ok` and `text` yourself",
148 untyped_hint_for_no_schema: "parse the `Completion` text yourself",
149 untyped_result: llm::declaration::untyped_result_type(mangled),
150 });
151 }
152 if agents::declaration::is_checked_run(mangled) {
153 return Some(SchemaCheckedCall {
154 callee: "agents.run",
155 example: "agents.run<Report>(agent, input)",
156 checks: "the agent's result",
157 answerer: "the agent",
158 written_params: "the agent and the input",
159 written_param_count: 2,
160 untyped_hint: "read the returned text yourself",
161 untyped_hint_for_unknown: "read the returned text yourself",
162 untyped_hint_for_no_schema: "parse the returned text yourself",
163 untyped_result: Type::String,
164 });
165 }
166 None
167}
168
169#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
177pub struct Stdlib {
178 optional: u8,
179}
180
181impl Stdlib {
182 pub const fn core() -> Self {
184 Stdlib { optional: 0 }
185 }
186
187 #[must_use]
188 pub const fn with(self, package: OptionalPackage) -> Self {
189 Stdlib {
190 optional: self.optional | package.bit(),
191 }
192 }
193
194 pub const fn enables(self, package: OptionalPackage) -> bool {
195 self.optional & package.bit() != 0
196 }
197
198 pub fn optional_packages(self) -> impl Iterator<Item = OptionalPackage> {
199 OptionalPackage::ALL
200 .iter()
201 .copied()
202 .filter(move |package| self.enables(*package))
203 }
204
205 pub fn contains_module(self, name: &str) -> bool {
207 OptionalPackage::from_module_name(name).map_or_else(
208 || {
209 core_package_declarations()
210 .iter()
211 .any(|defs| defs.package_name == name)
212 },
213 |package| self.enables(package),
214 )
215 }
216
217 pub fn package_declarations(self) -> Vec<PackageDeclaration> {
219 let mut declarations = core_package_declarations();
220 declarations.extend(
221 self.optional_packages()
222 .map(OptionalPackage::package_declaration),
223 );
224 declarations.sort_by(|a, b| a.package_name.cmp(&b.package_name));
226 declarations
227 }
228
229 pub fn install_host_functions(self, linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
230 install_core_host_functions(linker)?;
231 for package in self.optional_packages() {
232 package.install(linker)?;
233 }
234 Ok(())
235 }
236}
237
238pub(crate) fn unavailable_import(module: &str) -> Option<(String, Vec<String>)> {
244 if module == test::MODULE_NAME {
245 return Some((
246 format!("package `{module}` not found"),
247 vec![
248 "`submilli:test` is only available to test files run via `submilli build test`; it is not importable from a program run with `submilli run`"
249 .to_string(),
250 ],
251 ));
252 }
253 let package = OptionalPackage::from_module_name(module)?;
254 Some((
255 format!("package `{module}` is not available in this harness"),
256 vec![format!(
257 "`{}` exists only where the harness running the program provides it",
258 package.module_name()
259 )],
260 ))
261}
262
263pub fn stdlib_package_declarations() -> Vec<PackageDeclaration> {
265 Stdlib::core().package_declarations()
266}
267
268pub fn install_host_functions(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
269 Stdlib::core().install_host_functions(linker)
270}
271
272fn core_package_declarations() -> Vec<PackageDeclaration> {
273 vec![
274 code::package_declaration(),
276 crypto::package_declaration(),
277 embedding::package_declaration(),
278 fs::package_declaration(),
279 git::package_declaration(),
280 http::package_declaration(),
281 llm::package_declaration(),
282 secrets::package_declaration(),
283 security::package_declaration(),
284 session::package_declaration(),
285 url::package_declaration(),
286 uuid::package_declaration(),
287 variables::package_declaration(),
288 ]
289}
290
291fn install_core_host_functions(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
292 code::install(linker)?;
293 crypto::install(linker)?;
294 embedding::install(linker)?;
295 fs::install(linker)?;
296 git::install(linker)?;
297 http::install(linker)?;
298 llm::install(linker)?;
299 secrets::install(linker)?;
300 security::install(linker)?;
301 session::install(linker)?;
302 url::install(linker)?;
303 uuid::install(linker)?;
304 variables::install(linker)?;
305 Ok(())
306}
307
308pub(crate) fn host_views() -> impl Iterator<Item = &'static abi::HostView> {
310 [
311 agents::HOST_VIEWS,
312 embedding::HOST_VIEWS,
313 fs::HOST_VIEWS,
314 http::HOST_VIEWS,
315 llm::HOST_VIEWS,
316 session::HOST_VIEWS,
317 skills::HOST_VIEWS,
318 url::HOST_VIEWS,
319 ]
320 .into_iter()
321 .flatten()
322 .copied()
323}
324
325pub(crate) fn install_store_bound(
327 linker: &mut Linker<StoreData>,
328 store: &mut wasmtime::Store<StoreData>,
329) -> wasmtime::Result<()> {
330 git::class::install(linker, store)
331}
332
333#[cfg(test)]
334mod optional_tests;
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339
340 #[test]
344 fn every_host_backed_interface_has_a_view_of_its_properties() {
345 let mut declared = std::collections::BTreeMap::new();
346 let all = Stdlib::core()
347 .with(OptionalPackage::Agents)
348 .with(OptionalPackage::Skills);
349 for defs in all.package_declarations() {
350 for (name, symbol) in &defs.types {
351 if let crate::TypeKind::Interface {
352 properties,
353 dispatch: crate::Dispatch::Direct,
354 ..
355 } = &symbol.kind
356 && !properties.is_empty()
357 {
358 let names: std::collections::BTreeSet<String> =
359 properties.keys().cloned().collect();
360 declared.insert(
361 (defs.package_name.clone(), name.as_str().to_string()),
362 names,
363 );
364 }
365 }
366 }
367 let mut viewed = std::collections::BTreeMap::new();
368 for view in host_views() {
369 let names: std::collections::BTreeSet<String> = view
370 .properties
371 .iter()
372 .map(|(name, _)| (*name).to_string())
373 .collect();
374 assert_eq!(
375 names.len(),
376 view.properties.len(),
377 "{} repeats a property",
378 view.interface
379 );
380 viewed.insert((view.module.to_string(), view.interface.to_string()), names);
381 }
382 assert_eq!(viewed, declared);
383 }
384
385 #[test]
389 fn views_sharing_a_backing_struct_show_the_same_properties() {
390 let engine = crate::RuntimeConfig::default().engine().unwrap();
391 let mut seen: Vec<(wasmtime::StructType, &abi::HostView)> = Vec::new();
392 for view in host_views() {
393 let backing = (view.backing)(&engine).unwrap();
394 for (other, earlier) in &seen {
395 if *other == backing {
396 assert_eq!(
397 earlier.properties, view.properties,
398 "{} and {} share a layout",
399 earlier.interface, view.interface
400 );
401 }
402 }
403 seen.push((backing, view));
404 }
405 }
406
407 fn every_set() -> Vec<Stdlib> {
409 let mut sets = vec![Stdlib::core()];
410 for package in OptionalPackage::ALL {
411 let with: Vec<Stdlib> = sets.iter().map(|set| set.with(*package)).collect();
412 sets.extend(with);
413 }
414 sets
415 }
416
417 #[test]
418 fn declarations_are_sorted_for_every_set() {
419 for set in every_set() {
420 let names: Vec<String> = set
421 .package_declarations()
422 .into_iter()
423 .map(|defs| defs.package_name)
424 .collect();
425 let mut sorted = names.clone();
426 sorted.sort();
427 assert_eq!(names, sorted, "{set:?}");
428 }
429 }
430
431 #[test]
432 fn a_set_contains_exactly_its_packages() {
433 for set in every_set() {
434 let names: Vec<String> = set
435 .package_declarations()
436 .into_iter()
437 .map(|defs| defs.package_name)
438 .collect();
439 for name in &names {
440 assert!(set.contains_module(name), "{set:?} misses {name}");
441 }
442 for package in OptionalPackage::ALL {
443 assert_eq!(
444 names.iter().any(|name| name == package.module_name()),
445 set.enables(*package),
446 "{set:?} and {package:?}"
447 );
448 }
449 }
450 }
451
452 #[test]
453 fn core_is_the_global_list() {
454 let core: Vec<String> = Stdlib::core()
455 .package_declarations()
456 .into_iter()
457 .map(|defs| defs.package_name)
458 .collect();
459 let global: Vec<String> = stdlib_package_declarations()
460 .into_iter()
461 .map(|defs| defs.package_name)
462 .collect();
463 assert_eq!(core, global);
464 assert!(!Stdlib::core().contains_module(test::MODULE_NAME));
465 }
466}