1#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
2use std::sync::Arc;
26
27use everruns_capability::CapabilityError;
28
29#[cfg(feature = "ui-capabilities")]
32pub mod a2ui;
33pub mod agent_instructions;
34pub mod attach_skill;
35pub mod auto_tool_search;
36pub mod btw;
37pub mod budgeting;
38pub mod channel_context;
39pub mod claude_tool_search;
40pub mod compaction;
41pub mod current_time;
42pub mod error_disclosure;
43mod framework_config;
44pub mod guardrails;
45pub mod human_intent;
46pub mod infinity_context;
47pub mod loop_detection;
48pub mod message_metadata;
49pub mod native_async_tools;
50pub mod openai_tool_search;
51#[cfg(feature = "ui-capabilities")]
52pub mod openui;
53pub mod parallel_tool_calls;
54pub use native_async_tools::NativeAsyncToolsCapability;
55pub mod progress_guard;
56pub mod prompt_caching;
57pub mod prompt_canary_guardrail;
58pub mod self_budget;
59pub mod skills;
60pub mod skills_scoped;
61pub mod soft_approval;
62pub mod stateless_todo_list;
63pub mod system_commands;
64pub mod tool_approval;
65pub mod tool_call_repair;
66pub mod tool_output_distillation;
67pub mod tool_output_persistence;
68pub mod tool_search;
69pub mod usage_limit_auto_continue;
70
71pub(crate) use everruns_core::capabilities::{
74 Capability, CapabilityLocalization, CapabilityRegistry, CapabilityStatus, Fact, FactsContext,
75 ModelViewContext, ModelViewProvider, RiskLevel, SystemPromptContext, ToolDefinitionHook,
76 Volatility,
77};
78pub(crate) use everruns_core::tool_hooks;
79#[allow(unused_imports)]
80pub(crate) use everruns_core::{
82 DEFAULT_ORG_PUBLIC_ID, McpToolInvoker, UtilityLlmService, retain_complete_llm_tool_exchanges,
83};
84#[allow(unused_imports)]
85pub(crate) use everruns_core::{
87 budget, capability_dto, capability_types, command, command_host, events, guardrail_checks,
88 llm_conversions, llm_error_hook, mcp_server, message, message_filter, output_guardrail,
89 runtime_agent, session, session_file, session_files, session_resource, session_schedule, skill,
90 tool_context, tool_fingerprint, tool_narration, tool_output_sanitizer, tools, utility_llm,
91};
92#[allow(unused_imports)]
93pub(crate) use everruns_provider::driver_registry::{
94 LlmCompletionMetadata, LlmMessage, LlmMessageRole, LlmResponse, LlmResponseStream,
95};
96#[allow(unused_imports)]
97pub(crate) use everruns_provider::error::{AgentLoopError, Result};
98#[allow(unused_imports)]
99pub(crate) use everruns_provider::typed_id::{PrincipalId, WorkspaceId};
100#[allow(unused_imports)]
101pub(crate) use everruns_provider::user_facing_error::codes as user_facing_error_codes;
102#[allow(unused_imports)]
103pub(crate) use everruns_provider::{
104 driver_registry, error, model_profiles, provider, tool_types, typed_id, user_facing_error,
105};
106
107#[cfg(test)]
108mod test_fixtures;
109
110#[cfg(feature = "ui-capabilities")]
111pub use a2ui::{A2UI_CAPABILITY_ID, A2UiCapability};
112pub use agent_instructions::{
113 AGENT_INSTRUCTIONS_CAPABILITY_ID, AGENTS_MD_PATH, AgentInstructionsCapability,
114 AgentInstructionsConfig, DEFAULT_AGENT_INSTRUCTIONS_FILE, MAX_AGENT_INSTRUCTIONS_FILES,
115 MAX_AGENTS_MD_SIZE, format_agents_md_content, format_instruction_file_content,
116};
117pub use attach_skill::AttachSkillCapability;
118pub use auto_tool_search::{AUTO_TOOL_SEARCH_CAPABILITY_ID, AutoToolSearchCapability};
119pub use btw::{BTW_CAPABILITY_ID, BtwCapability};
120pub use budgeting::{BUDGETING_CAPABILITY_ID, BudgetingCapability};
121pub use channel_context::{CHANNEL_CONTEXT_CAPABILITY_ID, ChannelContextCapability};
122pub use claude_tool_search::{CLAUDE_TOOL_SEARCH_CAPABILITY_ID, ClaudeToolSearchCapability};
123pub use compaction::{
124 COMPACTION_CAPABILITY_ID, CompactionCapability, CompactionStep,
125 CompactionStrategy as RuntimeCompactionStrategy, CostControlConfig, CostControlMaskingResult,
126 HierarchicalMemoryConfig, MaskingSummaryFormat, MemoryTier, ObservationMaskingConfig,
127 ObservationMaskingResult, RuntimeCompactionConfig, SessionCompactionMetrics,
128 SummarizationConfig, aggressive_trim, apply_cost_control_masking, apply_hierarchical_memory,
129 apply_observation_masking, build_model_view_messages, build_summarization_prompt,
130 build_summary_message, classify_memory_tiers, compose_summary_with_recent, estimate_tokens,
131 estimate_total_tokens, format_messages_for_summarization, should_compact_for_cost,
132 should_compact_proactively, total_tool_result_bytes,
133};
134pub use current_time::{CURRENT_TIME_CAPABILITY_ID, CurrentTimeCapability, GetCurrentTimeTool};
135pub use error_disclosure::{
136 ERROR_DISCLOSURE_CAPABILITY_ID, ErrorDisclosureCapability, resolve_error_disclosure,
137};
138pub use framework_config::{CompactionConfig, CompactionStrategy, ToolSearch};
139pub use guardrails::{GUARDRAILS_CAPABILITY_ID, GuardrailsCapability};
140pub use human_intent::{HUMAN_INTENT_CAPABILITY_ID, HumanIntentCapability};
141pub use infinity_context::{
142 INFINITY_CONTEXT_CAPABILITY_ID, InfinityContextCapability, InfinityContextFilterOnlyCapability,
143 QueryHistoryTool,
144};
145pub use loop_detection::{LOOP_DETECTION_CAPABILITY_ID, LoopDetectionCapability};
146pub use message_metadata::{
147 MESSAGE_METADATA_CAPABILITY_ID, MessageMetadataCapability, MessageMetadataConfig,
148 MessageMetadataField, render_annotation, strip_leading_timestamp_annotations,
149};
150pub use openai_tool_search::{
151 DEFAULT_TOOL_SEARCH_THRESHOLD, OPENAI_TOOL_SEARCH_CAPABILITY_ID, OpenAiToolSearchCapability,
152 model_supports_native_tool_search,
153};
154#[cfg(feature = "ui-capabilities")]
155pub use openui::{OPENUI_CAPABILITY_ID, OpenUiCapability};
156pub use parallel_tool_calls::{
157 PARALLEL_TOOL_CALLS_CAPABILITY_ID, ParallelToolCallsCapability, ParallelToolCallsMode,
158 parallel_tool_calls_from_config,
159};
160pub use progress_guard::{PROGRESS_GUARD_CAPABILITY_ID, ProgressGuardCapability};
161pub use prompt_caching::{PROMPT_CACHING_CAPABILITY_ID, PromptCachingCapability};
162pub use prompt_canary_guardrail::{
163 DEFAULT_REPLACEMENT as PROMPT_CANARY_DEFAULT_REPLACEMENT,
164 PROMPT_CANARY_GUARDRAIL_CAPABILITY_ID, PromptCanaryGuardrailCapability,
165 REASON_CODE_SYSTEM_PROMPT_LEAK,
166};
167pub use self_budget::{SELF_BUDGET_CAPABILITY_ID, SelfBudgetCapability};
168pub use skills::{SKILLS_CAPABILITY_ID, Skills, SkillsCapability};
169pub use skills_scoped::{
170 ScopedSkillsCapability, SkillDirResolver, SkillScope, SkillsConfig, VfsSkillDirResolver,
171};
172pub use soft_approval::{
173 ApprovalModeStore, PendingApproval, PendingApprovalStore, SOFT_APPROVAL_CAPABILITY_ID,
174 SessionApprovalModes, SoftApprovalCapability, render_approval_block,
175};
176pub use stateless_todo_list::{
177 STATELESS_TODO_LIST_CAPABILITY_ID, StatelessTodoList, StatelessTodoListCapability,
178 WriteTodosTool,
179};
180pub use system_commands::{SYSTEM_COMMANDS_CAPABILITY_ID, SystemCommandsCapability};
181pub use tool_approval::{
182 ApprovalDecision, ApprovalMode, TOOL_APPROVAL_CAPABILITY_ID, ToolApprovalCapability,
183 ToolApprover,
184};
185pub use tool_call_repair::{
186 DEFAULT_MAX_REPROMPTS, MAX_SALVAGE_INPUT_BYTES, RepairOutcome, SalvageResult,
187 TOOL_CALL_REPAIR_CAPABILITY_ID, ToolCallRepairCapability, ToolCallRepairConfig,
188 salvage_tool_arguments, tool_call_repair_capability,
189};
190pub use tool_output_distillation::{
191 DistillOutputHook, TOOL_OUTPUT_DISTILLATION_CAPABILITY_ID, ToolOutputDistillationCapability,
192};
193pub use tool_output_persistence::{
194 PersistOutputHook, TOOL_OUTPUT_PERSISTENCE_CAPABILITY_ID, ToolOutputPersistenceCapability,
195};
196pub use tool_search::{
197 TOOL_SEARCH_CAPABILITY_ID, TOOL_SEARCH_TOOL_NAME, ToolSearchCapability, ToolSearchTool,
198};
199pub use usage_limit_auto_continue::{
200 AutoContinueConfig, USAGE_LIMIT_AUTO_CONTINUE_CAPABILITY_ID, UsageLimitAutoContinueCapability,
201 resolve_usage_limit_auto_continue,
202};
203
204#[allow(unused_imports)] pub(crate) mod capabilities {
207 pub(crate) use crate::attach_skill;
208 pub(crate) use crate::{
209 AUTO_TOOL_SEARCH_CAPABILITY_ID, AutoToolSearchCapability, CLAUDE_TOOL_SEARCH_CAPABILITY_ID,
210 CURRENT_TIME_CAPABILITY_ID, CurrentTimeCapability, DEFAULT_TOOL_SEARCH_THRESHOLD,
211 FactsContext, OPENAI_TOOL_SEARCH_CAPABILITY_ID, OpenAiToolSearchCapability,
212 STATELESS_TODO_LIST_CAPABILITY_ID, StatelessTodoListCapability, TOOL_SEARCH_CAPABILITY_ID,
213 TOOL_SEARCH_TOOL_NAME, ToolSearchCapability, ToolSearchTool, Volatility,
214 };
215 pub(crate) use everruns_core::capabilities::*;
216}
217
218pub fn register_runtime_capabilities(
223 registry: &mut CapabilityRegistry,
224) -> std::result::Result<(), CapabilityError> {
225 register_capabilities_atomically(registry, runtime_capabilities())
226}
227
228pub fn register_portable_capabilities(
235 registry: &mut CapabilityRegistry,
236) -> std::result::Result<(), CapabilityError> {
237 register_capabilities_atomically(registry, portable_capabilities())
238}
239
240pub fn portable_capability_registry() -> std::result::Result<CapabilityRegistry, CapabilityError> {
242 let mut registry = CapabilityRegistry::new();
243 register_portable_capabilities(&mut registry)?;
244 Ok(registry)
245}
246
247pub fn register_default_tools(registry: &mut everruns_core::ToolRegistry) {
254 registry.register(GetCurrentTimeTool);
255 registry.register(WriteTodosTool);
256}
257
258pub fn register_monitor_tools(registry: &mut everruns_core::ToolRegistry) {
260 registry.register(GetCurrentTimeTool);
261}
262
263fn portable_capabilities() -> Vec<Arc<dyn Capability>> {
264 let mut capabilities = runtime_capabilities();
265 capabilities.push(Arc::new(UsageLimitAutoContinueCapability));
266 #[cfg(feature = "ui-capabilities")]
267 {
268 capabilities.push(Arc::new(OpenUiCapability));
269 capabilities.push(Arc::new(A2UiCapability));
270 }
271 capabilities
272}
273
274fn register_capabilities_atomically(
275 registry: &mut CapabilityRegistry,
276 capabilities: Vec<Arc<dyn Capability>>,
277) -> std::result::Result<(), CapabilityError> {
278 let mut candidate = registry.clone();
279 for capability in capabilities {
280 candidate.try_register_arc(capability)?;
281 }
282 *registry = candidate;
283 Ok(())
284}
285
286fn runtime_capabilities() -> Vec<Arc<dyn Capability>> {
287 vec![
288 Arc::new(HumanIntentCapability),
289 Arc::new(InfinityContextCapability),
290 Arc::new(SkillsCapability),
291 Arc::new(AgentInstructionsCapability),
292 Arc::new(ChannelContextCapability),
293 Arc::new(CurrentTimeCapability),
294 Arc::new(MessageMetadataCapability),
295 Arc::new(StatelessTodoListCapability),
296 Arc::new(BtwCapability),
297 Arc::new(BudgetingCapability),
298 Arc::new(SelfBudgetCapability),
299 Arc::new(CompactionCapability),
300 Arc::new(ErrorDisclosureCapability),
301 Arc::new(OpenAiToolSearchCapability::new()),
302 Arc::new(ClaudeToolSearchCapability::new()),
303 Arc::new(ToolSearchCapability::new()),
304 Arc::new(AutoToolSearchCapability::new()),
305 Arc::new(PromptCachingCapability::new()),
306 Arc::new(ParallelToolCallsCapability),
307 Arc::new(NativeAsyncToolsCapability),
308 Arc::new(SystemCommandsCapability),
309 Arc::new(ToolOutputPersistenceCapability),
310 Arc::new(ToolOutputDistillationCapability),
311 Arc::new(LoopDetectionCapability),
312 Arc::new(ProgressGuardCapability::new()),
313 Arc::new(ToolCallRepairCapability),
314 Arc::new(PromptCanaryGuardrailCapability),
315 Arc::new(GuardrailsCapability),
316 Arc::new(SoftApprovalCapability::new()),
317 ]
318}
319
320#[cfg(test)]
321mod bundle_tests {
322 use super::*;
323
324 const RUNTIME_IDS: [&str; 29] = [
325 "human_intent",
326 "infinity_context",
327 "skills",
328 "agent_instructions",
329 "channel_context",
330 "current_time",
331 "message_metadata",
332 "stateless_todo_list",
333 "btw",
334 "budgeting",
335 "self_budget",
336 "compaction",
337 "error_disclosure",
338 "openai_tool_search",
339 "claude_tool_search",
340 "tool_search",
341 "auto_tool_search",
342 "prompt_caching",
343 "parallel_tool_calls",
344 "native_async_tools",
345 "system_commands",
346 "tool_output_persistence",
347 "tool_output_distillation",
348 "loop_detection",
349 "progress_guard",
350 "tool_call_repair",
351 "prompt_canary_guardrail",
352 "guardrails",
353 "soft_approval",
354 ];
355
356 fn portable_ids() -> Vec<&'static str> {
357 let mut ids = RUNTIME_IDS.to_vec();
358 ids.push("usage_limit_auto_continue");
359 #[cfg(feature = "ui-capabilities")]
360 {
361 ids.push("openui");
362 ids.push("a2ui");
363 }
364 ids
365 }
366
367 #[test]
368 fn bundle_has_the_curated_stable_catalog() {
369 let capabilities = portable_capabilities();
370 let ids: Vec<_> = capabilities
371 .iter()
372 .map(|capability| capability.id())
373 .collect();
374 assert_eq!(ids, portable_ids());
375 }
376
377 #[test]
378 fn runtime_bundle_excludes_schedule_backed_auto_continue() {
379 let mut registry = CapabilityRegistry::new();
380 register_runtime_capabilities(&mut registry).unwrap();
381
382 assert_eq!(registry.len(), RUNTIME_IDS.len());
383 assert!(!registry.has(USAGE_LIMIT_AUTO_CONTINUE_CAPABILITY_ID));
384 }
385
386 #[test]
387 fn duplicate_registration_is_rejected_without_replacement() {
388 let mut registry = portable_capability_registry().unwrap();
389 let original = Arc::clone(registry.get(HUMAN_INTENT_CAPABILITY_ID).unwrap());
390
391 let error = register_portable_capabilities(&mut registry).unwrap_err();
392
393 assert!(error.is_duplicate());
394 assert_eq!(error.id(), HUMAN_INTENT_CAPABILITY_ID);
395 assert!(Arc::ptr_eq(
396 &original,
397 registry.get(HUMAN_INTENT_CAPABILITY_ID).unwrap()
398 ));
399 }
400
401 #[test]
402 fn late_collision_does_not_partially_register_the_bundle() {
403 let mut registry = CapabilityRegistry::new();
404 registry.register(GuardrailsCapability);
405
406 let error = register_portable_capabilities(&mut registry).unwrap_err();
407
408 assert!(error.is_duplicate());
409 assert_eq!(error.id(), GUARDRAILS_CAPABILITY_ID);
410 assert_eq!(registry.len(), 1);
411 assert!(registry.has(GUARDRAILS_CAPABILITY_ID));
412 assert!(!registry.has(CURRENT_TIME_CAPABILITY_ID));
413 }
414
415 #[test]
416 fn dependencies_are_portable_or_explicit_host_seams() {
417 let registry = portable_capability_registry().unwrap();
418 let host_dependencies = ["session_file_system"];
419 let mut external_dependencies = std::collections::BTreeSet::new();
420
421 for capability in registry.list() {
422 for dependency in capability.dependencies() {
423 if !registry.has(dependency) {
424 external_dependencies.insert(dependency);
425 }
426 assert!(
427 registry.has(dependency) || host_dependencies.contains(&dependency),
428 "{} has an undeclared external dependency on {dependency}",
429 capability.id()
430 );
431 }
432 }
433
434 assert_eq!(
435 external_dependencies,
436 host_dependencies.into_iter().collect(),
437 "host-provided capability dependencies are an explicit structural boundary"
438 );
439 }
440}