1#![deny(clippy::unwrap_used, clippy::expect_used)]
18#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
19
20pub mod acp;
21pub mod agent;
22pub(crate) mod atomic;
23pub mod checkpoint;
24pub mod checkpoint_log;
25pub mod compact;
26pub mod config;
27pub mod config_file;
28pub mod coordinator;
29pub mod cost;
30pub mod error;
31pub mod event;
32pub mod hooks;
33#[cfg(feature = "http")]
34pub mod http;
35pub mod kernel;
36pub mod llm;
37pub mod logging;
38#[cfg(feature = "mcp")]
39pub mod mcp;
40#[cfg(feature = "mcp")]
41pub mod mcp_server;
42pub mod memory;
43pub mod message;
44pub mod migrate;
45pub mod multi;
46pub mod paths;
47pub mod permissions;
48pub mod providers;
49pub mod providers_cache;
50pub mod rewind;
51pub mod run_core;
52pub mod runtime;
53pub mod runtime_goal;
54pub mod session;
55pub mod skills;
56pub(crate) mod skills_injector;
57pub mod storage;
58pub mod system_prompt;
59pub mod tasks;
60#[cfg(feature = "coordinator-mode")]
61pub(crate) mod team;
62pub mod tool_set_provider;
63pub mod tools;
64pub mod transcript;
65#[cfg(feature = "weixin")]
66pub mod weixin;
67
68pub use agent::{FinishReason, PermissionDecision};
69pub use checkpoint::{CheckpointId, CheckpointInfo, RestoreStats, ShadowRepo};
70pub use checkpoint_log::{
71 read_log as read_checkpoint_log, truncate_to_turn as truncate_checkpoint_log,
72 CheckpointLogWriter, CheckpointRecord, TouchedVia,
73};
74pub use compact::reinject::{build_file_reinjector_from_env, build_skill_reinjector_from_env};
75pub use compact::{Compactor, FileReinjector, Microcompactor, SkillReinjector};
76pub use config::Config;
77pub use error::{Error, Result};
78pub use event::{AgentEvent, ChannelSink, CompositeSink, EventSink, NullSink};
79pub use hooks::ExternalHookRunner;
80pub use hooks::ToolTimingHook;
81pub use hooks::{Hook, HookAction, HookEvent, HookRegistry, SdkHookForwarder};
82pub use kernel::{AgentKernel, AgentKernelBuilder, TurnContext, TurnOutcome};
83pub use llm::{
84 context_window_tokens_for_model, default_compact_threshold_chars, estimate_tokens, pricing_for,
85 ChatProvider, Completion, ContextBreakdown, ModelPricing, RetryPolicy, TokenUsage, ToolCall,
86 ToolSpec,
87};
88#[cfg(feature = "mcp")]
89pub use mcp::{
90 discover_mcp_servers, load_mcp_config, new_elicitation_slot, ElicitationHandler,
91 ElicitationRequest, McpClient, McpPrompt, McpPromptArgument, McpPromptMessage, McpResource,
92 McpResourceContent, McpServer, McpServerConfig, McpTool, McpToolSpec, ServerCapabilities,
93 SharedElicitationHandler,
94};
95#[cfg(feature = "mcp")]
96pub use mcp_server::{McpServerManager, McpServerRunner};
97pub use message::{Message, Role};
98pub use migrate::{migrate_workspace, MigrateReport};
99pub use multi::{
100 register_subagent_if_enabled, AgentMessage, AgentPool, AgentRole, MessageBus, MessageType,
101 SharedMemory,
102};
103pub use paths::{
104 legacy_paths_in_workspace, user_data_dir, user_scratchpad_path, user_sessions_dir,
105 user_shadow_git_dir, user_workspace_dir, workspace_hash,
106};
107pub use permissions::auto_classifier::{AutoClassifier, DenialTracker};
108pub use permissions::PermissionMode;
109pub use permissions::{LayeredPermissionsConfig, PermissionLayer, RuleSource};
110pub use permissions::{RuleBehavior, SharedPermissions};
111pub use providers::{
112 all_presets, all_presets_dynamic, all_presets_effective, find_preset, find_preset_effective,
113 find_preset_extended, ModelSpec, ProviderPreset,
114};
115pub use rewind::{
116 apply_rewind, checkpoint_log_path, detect_conflicts, plan_rewind, ConflictReport, RewindPlan,
117 RewindResult,
118};
119pub use runtime::{AgentRuntime, AgentRuntimeBuilder, RuntimeOutcome};
120pub use session::OrphanToolCall;
121pub use session::SessionFile;
122pub use session::SessionPersistenceSink;
123pub use session::SessionReader;
124pub use session::SessionStatus;
125pub use session::SessionWriter;
126pub use session::{
127 entry_to_message, truncate_transcript_to_turn, SessionLock, SessionMeta, TranscriptEntry,
128 TruncateStats,
129};
130pub use skills::{
131 discover_skills, skill_index, skills_for_injection, Skill, SkillMode, SkillParam, SkillRef,
132 SkillScript, SkillSection,
133};
134#[cfg(feature = "cloud-runtime")]
135pub use storage::RedisSessionStore;
136#[cfg(feature = "cloud-runtime")]
137pub use storage::S3StorageBackend;
138pub use storage::{
139 AgentCheckpointState, LocalStorageBackend, NoopSessionStore, SessionStore, StorageBackend,
140};
141pub use system_prompt::{assemble_system_prompt, AssembledPrompt, PromptSegments};
142pub use tool_set_provider::{
143 LocalToolSetProvider, PolicyToolSetProvider, SandboxMode, ToolSetProvider,
144};
145pub use tools::policy_sandbox::{FsPolicy, PolicyConfig, ShellPolicy};
146pub use tools::PermissionHook;
147pub use tools::{
148 build_standard_tools, build_standard_tools_with_roots, new_shared_sandbox_roots, AccessTier,
149 AuditMeta, EnterPlanModeTool, ExitPlanModeTool, PlanApprovalGate, PlanApprovalResult,
150 PlanModeRequestGate, PlanModeRequestResult, ReadFileState, RequestPlanModeTool,
151 SharedSandboxRoots, TodoItem, TodoStatus, TodoWriteTool, Tool, ToolDispatch, ToolRegistry,
152 ToolSideEffect, TouchedFiles, AUDIT_ERR_MAX_BYTES,
153};
154pub use transcript::{TranscriptFile, TranscriptMeta};
155
156#[cfg(any(test, feature = "test-utils"))]
157pub mod test_util;
158
159#[inline]
163pub fn truncate_str(s: &str, max_bytes: usize) -> &str {
164 if s.len() <= max_bytes {
165 return s;
166 }
167 let end = max_bytes.min(s.len());
168 &s[..rewind_to_char_boundary(s, end)]
169}
170
171#[cfg_attr(test, mutants::skip)]
177fn rewind_to_char_boundary(s: &str, mut end: usize) -> usize {
178 while end > 0 && !s.is_char_boundary(end) {
179 end -= 1;
180 }
181 end
182}
183
184#[cfg(test)]
185mod tests {
186 use super::truncate_str;
187
188 #[test]
189 fn truncate_str_short_string_returns_unchanged() {
190 assert_eq!(truncate_str("hello", 10), "hello");
191 }
192
193 #[test]
194 fn truncate_str_exact_length_returns_unchanged() {
195 assert_eq!(truncate_str("hello", 5), "hello");
196 }
197
198 #[test]
199 fn truncate_str_over_limit_returns_prefix() {
200 assert_eq!(truncate_str("hello world", 5), "hello");
201 }
202
203 #[test]
204 fn truncate_str_empty_string() {
205 assert_eq!(truncate_str("", 10), "");
206 }
207
208 #[test]
209 fn truncate_str_zero_max_returns_empty() {
210 assert_eq!(truncate_str("hello", 0), "");
211 }
212
213 #[test]
214 fn truncate_str_multibyte_does_not_split_char() {
215 let s = "日本語";
218 let truncated = truncate_str(s, 4);
219 assert!(s.is_char_boundary(truncated.len()));
221 assert_eq!(truncated, "日");
222 }
223
224 #[test]
225 fn truncate_str_ascii_one_byte_over() {
226 assert_eq!(truncate_str("abcdef", 5), "abcde");
227 }
228}