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