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;
35
36use wasmtime::Linker;
37
38use crate::runtime::StoreData;
39use crate::{MangledName, PackageDeclaration, Type};
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
48#[non_exhaustive]
49pub enum OptionalPackage {
50 Agents,
52 Skills,
54}
55
56impl OptionalPackage {
57 pub const ALL: &'static [OptionalPackage] = &[Self::Agents, Self::Skills];
58
59 pub const fn module_name(self) -> &'static str {
60 match self {
61 Self::Agents => agents::MODULE_NAME,
62 Self::Skills => skills::MODULE_NAME,
63 }
64 }
65
66 pub fn from_module_name(name: &str) -> Option<Self> {
67 Self::ALL
68 .iter()
69 .copied()
70 .find(|package| package.module_name() == name)
71 }
72
73 fn package_declaration(self) -> PackageDeclaration {
74 match self {
75 Self::Agents => agents::package_declaration(),
76 Self::Skills => skills::package_declaration(),
77 }
78 }
79
80 fn install(self, linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
81 match self {
82 Self::Agents => agents::install(linker),
83 Self::Skills => skills::install(linker),
84 }
85 }
86
87 const fn bit(self) -> u8 {
88 match self {
89 Self::Agents => 1,
90 Self::Skills => 1 << 1,
91 }
92 }
93}
94
95pub(crate) struct SchemaCheckedCall {
100 pub callee: &'static str,
102 pub example: &'static str,
104 pub checks: &'static str,
106 pub answerer: &'static str,
108 pub written_params: &'static str,
110 pub written_param_count: usize,
111 pub untyped_hint: &'static str,
114 pub untyped_hint_for_unknown: &'static str,
116 pub untyped_hint_for_no_schema: &'static str,
118 pub untyped_result: Type,
120}
121
122impl SchemaCheckedCall {
123 pub fn schema_argument_ordinal(&self) -> &'static str {
125 match self.written_param_count {
126 0 => "first",
127 1 => "second",
128 2 => "third",
129 3 => "fourth",
130 _ => "last",
131 }
132 }
133}
134
135pub(crate) fn schema_checked_call(mangled: &MangledName) -> Option<SchemaCheckedCall> {
137 if llm::declaration::is_checked_call(mangled) {
138 return Some(SchemaCheckedCall {
139 callee: "llm.call",
140 example: "llm.call<Severity>(model, prompt)",
141 checks: "the model's response",
142 answerer: "the model",
143 written_params: "the model and the prompt",
144 written_param_count: 2,
145 untyped_hint: "read the `Completion` envelope yourself",
146 untyped_hint_for_unknown: "read the `Completion` envelope's `ok` and `text` yourself",
147 untyped_hint_for_no_schema: "parse the `Completion` text yourself",
148 untyped_result: llm::declaration::untyped_result_type(mangled),
149 });
150 }
151 if agents::declaration::is_checked_run(mangled) {
152 return Some(SchemaCheckedCall {
153 callee: "agents.run",
154 example: "agents.run<Report>(agent, input)",
155 checks: "the agent's result",
156 answerer: "the agent",
157 written_params: "the agent and the input",
158 written_param_count: 2,
159 untyped_hint: "read the returned text yourself",
160 untyped_hint_for_unknown: "read the returned text yourself",
161 untyped_hint_for_no_schema: "parse the returned text yourself",
162 untyped_result: Type::String,
163 });
164 }
165 None
166}
167
168#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
176pub struct Stdlib {
177 optional: u8,
178}
179
180impl Stdlib {
181 pub const fn core() -> Self {
183 Stdlib { optional: 0 }
184 }
185
186 #[must_use]
187 pub const fn with(self, package: OptionalPackage) -> Self {
188 Stdlib {
189 optional: self.optional | package.bit(),
190 }
191 }
192
193 pub const fn enables(self, package: OptionalPackage) -> bool {
194 self.optional & package.bit() != 0
195 }
196
197 pub fn optional_packages(self) -> impl Iterator<Item = OptionalPackage> {
198 OptionalPackage::ALL
199 .iter()
200 .copied()
201 .filter(move |package| self.enables(*package))
202 }
203
204 pub fn contains_module(self, name: &str) -> bool {
206 OptionalPackage::from_module_name(name).map_or_else(
207 || {
208 core_package_declarations()
209 .iter()
210 .any(|defs| defs.package_name == name)
211 },
212 |package| self.enables(package),
213 )
214 }
215
216 pub fn package_declarations(self) -> Vec<PackageDeclaration> {
218 let mut declarations = core_package_declarations();
219 declarations.extend(
220 self.optional_packages()
221 .map(OptionalPackage::package_declaration),
222 );
223 declarations.sort_by(|a, b| a.package_name.cmp(&b.package_name));
225 declarations
226 }
227
228 pub fn install_host_functions(self, linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
229 install_core_host_functions(linker)?;
230 for package in self.optional_packages() {
231 package.install(linker)?;
232 }
233 Ok(())
234 }
235}
236
237pub(crate) fn unavailable_import(module: &str) -> Option<(String, Vec<String>)> {
243 if module == test::MODULE_NAME {
244 return Some((
245 format!("package `{module}` not found"),
246 vec![
247 "`submilli:test` is only available to test files run via `submilli build test`; it is not importable from a program run with `submilli run`"
248 .to_string(),
249 ],
250 ));
251 }
252 let package = OptionalPackage::from_module_name(module)?;
253 Some((
254 format!("package `{module}` is not available in this harness"),
255 vec![format!(
256 "`{}` exists only where the harness running the program provides it",
257 package.module_name()
258 )],
259 ))
260}
261
262pub fn stdlib_package_declarations() -> Vec<PackageDeclaration> {
264 Stdlib::core().package_declarations()
265}
266
267pub fn install_host_functions(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
268 Stdlib::core().install_host_functions(linker)
269}
270
271fn core_package_declarations() -> Vec<PackageDeclaration> {
272 vec![
273 code::package_declaration(),
275 crypto::package_declaration(),
276 embedding::package_declaration(),
277 fs::package_declaration(),
278 git::package_declaration(),
279 http::package_declaration(),
280 llm::package_declaration(),
281 secrets::package_declaration(),
282 security::package_declaration(),
283 session::package_declaration(),
284 url::package_declaration(),
285 uuid::package_declaration(),
286 ]
287}
288
289fn install_core_host_functions(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
290 code::install(linker)?;
291 crypto::install(linker)?;
292 embedding::install(linker)?;
293 fs::install(linker)?;
294 git::install(linker)?;
295 http::install(linker)?;
296 llm::install(linker)?;
297 secrets::install(linker)?;
298 security::install(linker)?;
299 session::install(linker)?;
300 url::install(linker)?;
301 uuid::install(linker)?;
302 Ok(())
303}
304
305pub(crate) fn install_store_bound(
307 linker: &mut Linker<StoreData>,
308 store: &mut wasmtime::Store<StoreData>,
309) -> wasmtime::Result<()> {
310 git::class::install(linker, store)
311}
312
313#[cfg(test)]
314mod optional_tests;
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 fn every_set() -> Vec<Stdlib> {
322 let mut sets = vec![Stdlib::core()];
323 for package in OptionalPackage::ALL {
324 let with: Vec<Stdlib> = sets.iter().map(|set| set.with(*package)).collect();
325 sets.extend(with);
326 }
327 sets
328 }
329
330 #[test]
331 fn declarations_are_sorted_for_every_set() {
332 for set in every_set() {
333 let names: Vec<String> = set
334 .package_declarations()
335 .into_iter()
336 .map(|defs| defs.package_name)
337 .collect();
338 let mut sorted = names.clone();
339 sorted.sort();
340 assert_eq!(names, sorted, "{set:?}");
341 }
342 }
343
344 #[test]
345 fn a_set_contains_exactly_its_packages() {
346 for set in every_set() {
347 let names: Vec<String> = set
348 .package_declarations()
349 .into_iter()
350 .map(|defs| defs.package_name)
351 .collect();
352 for name in &names {
353 assert!(set.contains_module(name), "{set:?} misses {name}");
354 }
355 for package in OptionalPackage::ALL {
356 assert_eq!(
357 names.iter().any(|name| name == package.module_name()),
358 set.enables(*package),
359 "{set:?} and {package:?}"
360 );
361 }
362 }
363 }
364
365 #[test]
366 fn core_is_the_global_list() {
367 let core: Vec<String> = Stdlib::core()
368 .package_declarations()
369 .into_iter()
370 .map(|defs| defs.package_name)
371 .collect();
372 let global: Vec<String> = stdlib_package_declarations()
373 .into_iter()
374 .map(|defs| defs.package_name)
375 .collect();
376 assert_eq!(core, global);
377 assert!(!Stdlib::core().contains_module(test::MODULE_NAME));
378 }
379}