Skip to main content

everruns_core/capabilities/
self_budget.rs

1// Self-Budget Capability
2//
3// Prompt-only capability that teaches agents how to reason about a user-requested
4// *indicative* budget ("you have $7") using session usage data. Distinct from the
5// `budgeting` capability, which wires platform-enforced budgets and the
6// `check_budget` tool. `self_budget` ships NO tools — it relies on
7// `get_session_info` (already provided by the `session` capability in the generic
8// harness) for cumulative usage and on the agent's own judgment about when and
9// how to adapt behavior.
10//
11// See specs/budgeting.md (Self-Managed vs Platform-Enforced Budgets).
12
13use super::{Capability, CapabilityLocalization, CapabilityStatus};
14
15pub const SELF_BUDGET_CAPABILITY_ID: &str = "self_budget";
16
17/// Self-budget capability — prompt-only guidance for agent-managed indicative budgets.
18pub struct SelfBudgetCapability;
19
20impl Capability for SelfBudgetCapability {
21    fn id(&self) -> &str {
22        SELF_BUDGET_CAPABILITY_ID
23    }
24
25    fn name(&self) -> &str {
26        "Self-Budget"
27    }
28
29    fn description(&self) -> &str {
30        "Prompt-only guidance for reasoning about a user-requested indicative budget. \
31         The agent self-manages the target using session usage data; no tools are added \
32         and no platform enforcement is performed. Use alongside `budgeting` for \
33         authoritative platform budgets."
34    }
35
36    fn localizations(&self) -> Vec<CapabilityLocalization> {
37        vec![CapabilityLocalization::text(
38            "uk",
39            "Самокерований бюджет",
40            "Лише промптові настанови для міркування про орієнтовний бюджет, заданий користувачем. Агент самостійно керує цільовим показником на основі даних про використання сесії; жодних інструментів не додається, і платформа нічого примусово не обмежує. Використовуйте разом із `budgeting` для авторитетних платформних бюджетів.",
41        )]
42    }
43
44    fn status(&self) -> CapabilityStatus {
45        CapabilityStatus::Available
46    }
47
48    fn icon(&self) -> Option<&str> {
49        Some("gauge")
50    }
51
52    fn category(&self) -> Option<&str> {
53        Some("System")
54    }
55
56    fn system_prompt_addition(&self) -> Option<&str> {
57        Some(SELF_BUDGET_SYSTEM_PROMPT)
58    }
59
60    fn features(&self) -> Vec<&'static str> {
61        vec![]
62    }
63}
64
65const SELF_BUDGET_SYSTEM_PROMPT: &str = "User-stated budgets are agent-managed soft targets, not platform-enforced limits. Track spend with `get_session_info` around expensive phases, qualify estimates when pricing is partial, and tighten scope/output as the target nears. Do not create, modify, delete, or report them as platform budgets; if close to exhausted, inform the user and continue with a scoped-down path.";
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    // Metadata/tool-list constants covered by builtin_capabilities_satisfy_registry_invariants.
72
73    #[test]
74    fn test_capability_has_system_prompt() {
75        let cap = SelfBudgetCapability;
76        let prompt = cap.system_prompt_addition().expect("prompt present");
77        assert!(prompt.contains("agent-managed soft targets"));
78        assert!(prompt.contains("get_session_info"));
79        assert!(prompt.contains("platform-enforced limits"));
80    }
81
82    #[test]
83    fn test_capability_has_no_features() {
84        let cap = SelfBudgetCapability;
85        assert!(cap.features().is_empty());
86    }
87
88    #[test]
89    fn test_prompt_distinguishes_from_budgeting() {
90        let cap = SelfBudgetCapability;
91        let prompt = cap.system_prompt_addition().unwrap();
92        // Must NOT direct the agent to the platform-budget tool
93        assert!(!prompt.contains("check_budget"));
94        // Must NOT direct the agent to mutate budgets
95        assert!(!prompt.to_lowercase().contains("create budget"));
96    }
97}