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, ConsecutiveFailureRecovery, Content, ContextCompaction,
33 ContextWindowManager, DenyAllApprovalHandler, FinishReason, Language, Middleware, PlanItem, PlanStepStatus,
34 PostLlmCtx, PreLlmCtx, ReasoningConfig, ReasoningEffort, RetryOnError, RiskLevel, RunOutcome, RuntimeEvent,
35 SafetyConfig, SessionId, Tool, ToolContext, ToolDecision, ToolMetadata, ToolPolicy, ToolRegistry,
36 TurnFactMiddleware, TurnToolLimitMiddleware, UpdatePlanTool, UserMessageCtx, estimate_messages_tokens,
37 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 ChildPermissionMode, ChildReport, ChildResultEvent, ControlConfig, MultiAgentConfig,
60};
61#[cfg(feature = "multi-agent")]
64pub use agent_works::multi_agent::fan_in::{ChildResultRoute, ChildResultRouter};
65#[cfg(feature = "multi-agent")]
66pub use agent_works::multi_agent::registry::{AgentSnapshot, RegistrySnapshot};
67
68pub use agent_base::UserEvent;
74pub use agent_base::engine::max_turns_nudge::{MaxTurnsNudgeConfig, MaxTurnsNudgeMiddleware};
76pub use agent_base::llm_trait;
79pub use agent_works::guard::{DefaultGuard, DefaultGuardConfig, ReasoningOnlyAction};
81pub use llm_unified::create_provider;
83
84#[cfg(feature = "shell")]
86pub use phi_kernel_tools::local_shell::LocalShellTool;
87
88#[cfg(feature = "skill")]
90pub use agent_works::skill::{
91 MAX_CATALOG_SKILLS, Skill, SkillCatalogRefreshMiddleware, SkillResolver, SkillTelemetry, SkillTool,
92 prompt_skill::PromptSkill, refresh_catalog, render_catalog, strip_catalog,
93};
94
95#[cfg(feature = "mcp")]
97pub use agent_works::mcp::{McpServeConfig, McpServer, McpServerConfig, McpServerTransport, McpTransport};
98
99#[cfg(feature = "compression")]
101pub use agent::CompressionMiddleware;
102pub use agent::{
103 PhiAgent, PhiAgentConfig, base_agent_builder, base_agent_builder_no_compression, base_agent_builder_with_excludes,
104 base_agent_builder_with_options, clear_compression_cache, run_compact_session,
105};
106pub use agent_works::prompt::{
107 DynamicToolsFragment, EnvironmentFragment, FragmentContext, PromptFragment, compose_fragments,
108};
109pub use cli::{ApprovalItem, ApprovalMode, AutoApprovalHandler, QueuedApprovalHandler};
110pub use config::{LlmConfig, resolve_llm_config};
111pub use event_log::{event_to_jsonl, event_to_value, save_turn_log};
112pub use prompt::{build_system_prompt, build_system_prompt_cn, build_system_prompt_with_fragments};
113pub use render::{
114 EventRenderer, JsonStreamRenderer, NullRenderer, OutputFormat, create_renderer, create_stdout_renderer,
115};
116pub use session::{
117 SessionContext, SnapshotInfo, cleanup_expired_sessions, create_snapshot, delete_snapshot, list_snapshots,
118 resolve_session, restore_snapshot, validate_session_id, validate_snapshot_name,
119};
120
121pub fn format_number(n: u64) -> String {
127 if n >= 1_000_000 {
128 format!("{:.1}M", n as f64 / 1_000_000.0)
129 } else if n >= 1_000 {
130 format!("{:.1}K", n as f64 / 1_000.0)
131 } else {
132 n.to_string()
133 }
134}
135
136#[cfg(test)]
137mod proptests {
138 use super::*;
139
140 proptest::proptest! {
141 #[test]
142 fn format_number_never_panics(n: u64) {
143 let _ = format_number(n);
144 }
145
146 #[test]
147 fn format_number_correct_suffix(n: u64) {
148 let s = format_number(n);
149 if n < 1_000 {
150 proptest::prop_assert_eq!(s, n.to_string());
151 } else if n < 1_000_000 {
152 proptest::prop_assert!(s.ends_with('K'), "expected K suffix for {}, got '{}'", n, s);
153 } else {
154 proptest::prop_assert!(s.ends_with('M'), "expected M suffix for {}, got '{}'", n, s);
155 }
156 }
157
158 #[test]
159 fn format_number_boundary_correct(n in 0u64..1_001) {
160 let s = format_number(n);
162 if n < 1_000 {
163 proptest::prop_assert!(!s.contains('K'));
164 proptest::prop_assert!(!s.contains('M'));
165 } else {
166 proptest::prop_assert!(s.ends_with('K'));
167 }
168 }
169 }
170}