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