1#![warn(missing_docs)]
11
12pub mod agent;
13pub mod bridge;
14pub mod cli;
15pub mod config;
16pub mod event_log;
17pub mod prompt;
19pub mod render;
20pub mod session;
22
23pub use agent_base::{
31 AgentError, AgentResult, AgentRuntime, AllowAllApprovalHandler, ApprovalDecision, ApprovalHandler, ApprovalRequest,
32 ChatMessage, CheckpointData, CheckpointStep, CompactionKind, CompactionOutcome, ConsecutiveFailureRecovery,
33 Content, ContextCompaction, ContextWindowManager, DenyAllApprovalHandler, FinishReason, Language, Middleware,
34 PlanItem, PlanStepStatus, PostLlmCtx, PreLlmCtx, ReasoningConfig, ReasoningEffort, RetryOnError, RiskLevel,
35 RunOutcome, RuntimeEvent, SafetyConfig, SessionId, Tool, ToolContext, ToolDecision, ToolMetadata, ToolPolicy,
36 ToolRegistry, TurnFactMiddleware, TurnToolLimitMiddleware, UpdatePlanTool, UserMessageCtx,
37 estimate_messages_tokens, first_system_prompt,
38};
39pub use agent_works::AgentBuilder;
42pub use agent_works::rotation_policy::{
43 DEFAULT_SEED_MESSAGE, TokenBudgetAction, TokenBudgetConfig, TokenBudgetCore, TokenBudgetState,
44 build_context_window_info, token_budget_base_overhead,
45};
46
47#[cfg(feature = "telemetry")]
49pub use phi_telemetry::{
50 SessionMetrics, SessionOutcome, SessionSummary, TurnMetrics, TurnOutcome, list_all_metrics, load_metrics,
51 save_metrics, try_load_metrics,
52};
53
54#[cfg(feature = "focus")]
56pub use agent_works::focus::{Context as FocusContext, Focus, FocusError, FocusInput, FocusOutput};
57#[cfg(feature = "multi-agent")]
58pub use agent_works::multi_agent::{
59 CapabilityResolution, ChildPermissionMode, ChildReport, ChildResultEvent, ChildToolCapability, ControlConfig,
60 MultiAgentConfig, resolve_capability,
61};
62#[cfg(feature = "multi-agent")]
65pub use agent_works::multi_agent::fan_in::{ChildResultRoute, ChildResultRouter};
66#[cfg(feature = "multi-agent")]
67pub use agent_works::multi_agent::registry::{AgentSnapshot, RegistrySnapshot};
68
69#[cfg(feature = "memory")]
75pub use agent_works::memory::MemoryConfig;
76
77pub use agent_base::UserEvent;
83pub use agent_base::engine::max_turns_nudge::{MaxTurnsNudgeConfig, MaxTurnsNudgeMiddleware};
85pub use agent_base::engine::repeat_tool_limit::{RepeatToolLimitConfig, RepeatToolLimitMiddleware};
87pub use agent_base::llm_trait;
90pub use agent_base::types::NoticeKind;
93pub use agent_works::guard::{DefaultGuard, DefaultGuardConfig, ReasoningOnlyAction};
95pub use llm_unified::create_provider;
97
98#[cfg(feature = "shell")]
100pub use phi_kernel_tools::local_shell::LocalShellTool;
101
102#[cfg(feature = "skill")]
104pub use agent_works::skill::{
105 MAX_CATALOG_SKILLS, Skill, SkillCatalogRefreshMiddleware, SkillResolver, SkillTelemetry, SkillTool,
106 catalog::demote_h2_headings, prompt_skill::PromptSkill, prompt_skill::SkillScope, refresh_catalog, render_catalog,
107 strip_catalog,
108};
109
110#[cfg(feature = "mcp")]
112pub use agent_works::mcp::{McpServeConfig, McpServer, McpServerConfig, McpServerTransport, McpTransport};
113
114#[cfg(feature = "compression")]
116pub use agent::CompressionMiddleware;
117pub use agent::{
118 PhiAgent, PhiAgentConfig, base_agent_builder, base_agent_builder_no_compression, base_agent_builder_with_excludes,
119 base_agent_builder_with_options, clear_compression_cache, run_compact_session,
120};
121pub use agent_works::prompt::{
122 DynamicToolsFragment, EnvironmentFragment, FragmentContext, PromptFragment, compose_fragments,
123};
124pub use cli::{ApprovalItem, ApprovalMode, AutoApprovalHandler, QueuedApprovalHandler};
125pub use config::{LlmConfig, resolve_llm_config};
126pub use event_log::{event_to_jsonl, event_to_value, save_turn_log};
127pub use prompt::{build_system_prompt, build_system_prompt_cn, build_system_prompt_with_fragments};
128pub use render::{
129 EventRenderer, JsonStreamRenderer, NullRenderer, OutputFormat, create_renderer, create_stdout_renderer,
130};
131pub use session::{
132 SessionContext, SessionInfo, SnapshotInfo, cleanup_expired_sessions, clear_messages_jsonl, create_snapshot,
133 delete_snapshot, list_sessions, list_snapshots, load_session_messages, persist_window_messages, read_session_title,
134 resolve_session, restore_snapshot, validate_session_id, validate_snapshot_name, write_session_title,
135};
136
137pub fn format_number(n: u64) -> String {
143 if n >= 1_000_000 {
144 format!("{:.1}M", n as f64 / 1_000_000.0)
145 } else if n >= 1_000 {
146 format!("{:.1}K", n as f64 / 1_000.0)
147 } else {
148 n.to_string()
149 }
150}
151
152#[cfg(test)]
153mod proptests {
154 use super::*;
155
156 proptest::proptest! {
157 #[test]
158 fn format_number_never_panics(n: u64) {
159 let _ = format_number(n);
160 }
161
162 #[test]
163 fn format_number_correct_suffix(n: u64) {
164 let s = format_number(n);
165 if n < 1_000 {
166 proptest::prop_assert_eq!(s, n.to_string());
167 } else if n < 1_000_000 {
168 proptest::prop_assert!(s.ends_with('K'), "expected K suffix for {}, got '{}'", n, s);
169 } else {
170 proptest::prop_assert!(s.ends_with('M'), "expected M suffix for {}, got '{}'", n, s);
171 }
172 }
173
174 #[test]
175 fn format_number_boundary_correct(n in 0u64..1_001) {
176 let s = format_number(n);
178 if n < 1_000 {
179 proptest::prop_assert!(!s.contains('K'));
180 proptest::prop_assert!(!s.contains('M'));
181 } else {
182 proptest::prop_assert!(s.ends_with('K'));
183 }
184 }
185 }
186
187 #[test]
188 fn format_number_renders_exact_boundaries() {
189 assert_eq!(format_number(0), "0");
192 assert_eq!(format_number(42), "42");
193 assert_eq!(format_number(999), "999");
194 assert_eq!(format_number(1_000), "1.0K");
195 assert_eq!(format_number(1_500), "1.5K");
196 assert_eq!(format_number(999_999), "1000.0K");
197 assert_eq!(format_number(1_000_000), "1.0M");
198 assert_eq!(format_number(2_500_000), "2.5M");
199 }
200}