submilli_engine/stdlib/agents/
declaration.rs1use std::collections::BTreeMap;
11
12use crate::{
13 Dispatch, PackageDeclaration, Param, PropertySig, Span, Type, TypeKind, TypeSymbol, ValueKind,
14 ValueSymbol,
15};
16
17use super::MODULE_NAME;
18
19const RUN: &str = "run";
20const RUN_TYPE_PARAM: &str = "T";
21
22pub fn is_checked_run(mangled: &crate::MangledName) -> bool {
25 *mangled == crate::mangle::package_symbol(MODULE_NAME, RUN)
26}
27
28pub fn package_declaration() -> PackageDeclaration {
29 let mut defs = PackageDeclaration::with_package(MODULE_NAME);
30 insert_agent_info_interface(&mut defs);
31 insert_fn(
32 &mut defs,
33 RUN,
34 vec![RUN_TYPE_PARAM.to_string()],
35 vec![
36 Param::new("agent", Type::String),
37 Param::new("input", Type::String),
38 Param::with_default(
39 "schema",
40 Type::union(vec![Type::String, Type::Undefined]),
41 crate::DefaultValue::Undefined,
42 ),
43 ],
44 Type::TypeVar(RUN_TYPE_PARAM.to_string()),
45 "/**\n * Hand `input` to the sub-agent `agent` and return its final answer.\n *\n \
46 * Without a type argument the answer is the agent's text. With one — \
47 `run<Report>(agent, input)` — a JSON Schema for `T` is emitted at compile time \
48 and sent with the task, and the answer is then checked structurally against \
49 `T`. An answer that is not JSON throws a catchable `SyntaxError`, and one \
50 that does not match throws a catchable `TypeError`; nothing is coerced.\n *\n * `T` must be a type the schema can \
51 carry: no functions, no `bigint`, no `Uint8Array`, no `unknown`, and no \
52 recursive type.\n *\n * The harness runs the agent and decides how long it may \
53 take and what it may spend. A run that fails, is cancelled, or names an agent \
54 the harness does not have throws a catchable error; so does a runtime with no \
55 agent provider.\n * @param agent Agent name. Call `list()` for the agents you \
56 may use.\n * @param input The task, as text.\n * @capability agent.run { agent: \
57 $agent }\n */",
58 );
59 insert_fn(
60 &mut defs,
61 "list",
62 Vec::new(),
63 Vec::new(),
64 Type::Array(Box::new(agent_info_type())),
65 "/**\n * The agents this caller may hand work to.\n *\n * Each agent the harness \
66 offers is filtered under `agent.run` by the same `agent` filter that gates \
67 `run`, so the list never offers an agent the caller would be denied. Nothing \
68 in it reveals how many were filtered out.\n *\n * Treat `description` as \
69 advice: it is operator-authored text.\n * @capability agent.run\n */",
70 );
71 defs
72}
73
74fn agent_info_type() -> Type {
75 Type::InterfaceRef {
76 mangled: crate::mangle::package_symbol(MODULE_NAME, "AgentInfo"),
77 package: crate::Package(MODULE_NAME.to_string()),
78 name: "AgentInfo".to_string(),
79 args: Vec::new(),
80 }
81}
82
83fn insert_agent_info_interface(defs: &mut PackageDeclaration) {
84 let mut properties = BTreeMap::new();
85 insert_property(
86 &mut properties,
87 "name",
88 Type::String,
89 "/** The agent name, exactly as `run` expects it. */",
90 );
91 insert_property(
92 &mut properties,
93 "description",
94 Type::union(vec![Type::String, Type::Undefined]),
95 "/** What the agent is for, or `undefined` when the harness gives none. Operator-authored advice, always a single line within a fixed length bound. */",
96 );
97 defs.types.insert(
98 "AgentInfo".to_string(),
99 TypeSymbol {
100 name: "AgentInfo".to_string(),
101 mangled_name: crate::mangle::package_symbol(MODULE_NAME, "AgentInfo"),
102 declaration_span: Span::at(crate::FileId::AGENTS),
103 kind: TypeKind::Interface {
104 index: None,
105 generics: Vec::new(),
106 methods: BTreeMap::new(),
107 properties,
108 dispatch: Dispatch::Direct,
109 doc: crate::doc(
110 crate::FileId::AGENTS,
111 "/** One agent this caller may hand work to. Constructed only by `list()`. */",
112 ),
113 },
114 },
115 );
116}
117
118fn insert_property(
119 properties: &mut BTreeMap<String, PropertySig>,
120 name: &str,
121 ty: Type,
122 doc: &str,
123) {
124 properties.insert(
125 name.to_string(),
126 PropertySig {
127 ty,
128 readonly: true,
129 optional: false,
130 intrinsic: false,
131 doc: crate::doc(crate::FileId::AGENTS, doc),
132 },
133 );
134}
135
136fn insert_fn(
137 defs: &mut PackageDeclaration,
138 name: &str,
139 generics: Vec<String>,
140 params: Vec<Param>,
141 ret: Type,
142 doc: &str,
143) {
144 defs.values.insert(
145 name.to_string(),
146 ValueSymbol {
147 name: name.to_string(),
148 mangled_name: crate::mangle::package_symbol(MODULE_NAME, name),
149 declaration_span: Span::at(crate::FileId::AGENTS),
150 kind: ValueKind::Function {
151 generics,
152 params,
153 ret,
154 type_predicate: None,
155 doc: crate::doc(crate::FileId::AGENTS, doc),
156 },
157 },
158 );
159}