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::{
50 ChildPermissionMode, ChildReport, ChildResultEvent, ControlConfig, MultiAgentConfig,
51};
52
53#[cfg(feature = "mcp")]
55pub use agent_works::mcp::{McpServeConfig, McpServer, McpServerConfig, McpServerTransport, McpTransport};
56
57#[cfg(feature = "compression")]
59pub use agent::CompressionMiddleware;
60pub use agent::{
61 PhiAgent, PhiAgentConfig, base_agent_builder, base_agent_builder_with_excludes, base_agent_builder_with_options,
62 clear_compression_cache, run_compact_session,
63};
64pub use agent_works::prompt::{
65 DynamicToolsFragment, EnvironmentFragment, FragmentContext, PromptFragment, compose_fragments,
66};
67pub use cli::{ApprovalMode, AutoApprovalHandler};
68pub use config::{LlmConfig, resolve_llm_config};
69pub use event_log::{event_to_jsonl, event_to_value, save_turn_log};
70pub use prompt::{build_system_prompt, build_system_prompt_cn, build_system_prompt_with_fragments};
71pub use render::{
72 EventRenderer, JsonStreamRenderer, NullRenderer, OutputFormat, create_renderer, create_stdout_renderer,
73};
74pub use session::{
75 SessionContext, SnapshotInfo, cleanup_expired_sessions, create_snapshot, delete_snapshot, list_snapshots,
76 resolve_session, restore_snapshot, validate_session_id, validate_snapshot_name,
77};
78
79pub fn format_number(n: u64) -> String {
85 if n >= 1_000_000 {
86 format!("{:.1}M", n as f64 / 1_000_000.0)
87 } else if n >= 1_000 {
88 format!("{:.1}K", n as f64 / 1_000.0)
89 } else {
90 n.to_string()
91 }
92}
93
94#[cfg(test)]
95mod proptests {
96 use super::*;
97
98 proptest::proptest! {
99 #[test]
100 fn format_number_never_panics(n: u64) {
101 let _ = format_number(n);
102 }
103
104 #[test]
105 fn format_number_correct_suffix(n: u64) {
106 let s = format_number(n);
107 if n < 1_000 {
108 proptest::prop_assert_eq!(s, n.to_string());
109 } else if n < 1_000_000 {
110 proptest::prop_assert!(s.ends_with('K'), "expected K suffix for {}, got '{}'", n, s);
111 } else {
112 proptest::prop_assert!(s.ends_with('M'), "expected M suffix for {}, got '{}'", n, s);
113 }
114 }
115
116 #[test]
117 fn format_number_boundary_correct(n in 0u64..1_001) {
118 let s = format_number(n);
120 if n < 1_000 {
121 proptest::prop_assert!(!s.contains('K'));
122 proptest::prop_assert!(!s.contains('M'));
123 } else {
124 proptest::prop_assert!(s.ends_with('K'));
125 }
126 }
127 }
128}