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, ApprovalDecision, ApprovalHandler, ApprovalRequest,
32 ConsecutiveFailureRecovery, Content, FinishReason, Middleware, PlanItem, PlanStepStatus, PostLlmCtx, PreLlmCtx,
33 ReasoningConfig, ReasoningEffort, RiskLevel, RunOutcome, RuntimeEvent, SafetyConfig, SessionId, Tool, ToolContext,
34 ToolMetadata, ToolPolicy, TurnFactMiddleware, TurnToolLimitMiddleware, UpdatePlanTool, UserMessageCtx,
35};
36pub use agent_works::AgentBuilder;
37
38#[cfg(feature = "telemetry")]
40pub use phi_telemetry::{
41 SessionMetrics, SessionOutcome, SessionSummary, TurnMetrics, TurnOutcome, list_all_metrics, load_metrics,
42 save_metrics, try_load_metrics,
43};
44
45#[cfg(feature = "focus")]
47pub use agent_works::focus::{Context as FocusContext, Focus, FocusError, FocusInput, FocusOutput};
48#[cfg(feature = "multi-agent")]
49pub use agent_works::multi_agent::{ChildPermissionMode, MultiAgentConfig};
50
51#[cfg(feature = "mcp")]
53pub use agent_works::mcp::{McpServeConfig, McpServer, McpServerConfig, McpServerTransport, McpTransport};
54
55#[cfg(feature = "compression")]
57pub use agent::CompressionMiddleware;
58pub use agent::{
59 PhiAgent, PhiAgentConfig, base_agent_builder, base_agent_builder_with_excludes, base_agent_builder_with_options,
60 clear_compression_cache, run_compact_session,
61};
62pub use agent_works::prompt::{
63 DynamicToolsFragment, EnvironmentFragment, FragmentContext, PromptFragment, compose_fragments,
64};
65pub use cli::{ApprovalMode, AutoApprovalHandler};
66pub use config::{LlmConfig, resolve_llm_config};
67pub use event_log::{event_to_jsonl, event_to_value, save_turn_log};
68pub use prompt::{build_system_prompt, build_system_prompt_cn, build_system_prompt_with_fragments};
69pub use render::{
70 EventRenderer, JsonStreamRenderer, NullRenderer, OutputFormat, create_renderer, create_stdout_renderer,
71};
72pub use session::{
73 SessionContext, SnapshotInfo, cleanup_expired_sessions, create_snapshot, delete_snapshot, list_snapshots,
74 resolve_session, restore_snapshot, validate_session_id, validate_snapshot_name,
75};
76
77pub fn format_number(n: u64) -> String {
83 if n >= 1_000_000 {
84 format!("{:.1}M", n as f64 / 1_000_000.0)
85 } else if n >= 1_000 {
86 format!("{:.1}K", n as f64 / 1_000.0)
87 } else {
88 n.to_string()
89 }
90}
91
92#[cfg(test)]
93mod proptests {
94 use super::*;
95
96 proptest::proptest! {
97 #[test]
98 fn format_number_never_panics(n: u64) {
99 let _ = format_number(n);
100 }
101
102 #[test]
103 fn format_number_correct_suffix(n: u64) {
104 let s = format_number(n);
105 if n < 1_000 {
106 proptest::prop_assert_eq!(s, n.to_string());
107 } else if n < 1_000_000 {
108 proptest::prop_assert!(s.ends_with('K'), "expected K suffix for {}, got '{}'", n, s);
109 } else {
110 proptest::prop_assert!(s.ends_with('M'), "expected M suffix for {}, got '{}'", n, s);
111 }
112 }
113
114 #[test]
115 fn format_number_boundary_correct(n in 0u64..1_001) {
116 let s = format_number(n);
118 if n < 1_000 {
119 proptest::prop_assert!(!s.contains('K'));
120 proptest::prop_assert!(!s.contains('M'));
121 } else {
122 proptest::prop_assert!(s.ends_with('K'));
123 }
124 }
125 }
126}