j_agent/context/
policy.rs1use crate::tools::tool_names;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
28pub enum ContextTier {
29 System = 0,
30 User = 1,
31 KeyTool = 2,
32 Assistant = 3,
33 RegularTool = 4,
34}
35
36impl ContextTier {
37 pub fn priority(self) -> u8 {
39 self as u8
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum RetentionPolicy {
46 AlwaysPreserve,
49 Placeholder,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct ToolContextPolicy {
56 pub tier: ContextTier,
57 pub retention: RetentionPolicy,
58}
59
60impl ToolContextPolicy {
61 const fn key_preserve() -> Self {
62 Self {
63 tier: ContextTier::KeyTool,
64 retention: RetentionPolicy::AlwaysPreserve,
65 }
66 }
67 const fn regular_placeholder() -> Self {
68 Self {
69 tier: ContextTier::RegularTool,
70 retention: RetentionPolicy::Placeholder,
71 }
72 }
73}
74
75pub const KEY_TOOL_NAMES: &[&str] = &[
81 tool_names::ENTER_PLAN_MODE,
83 tool_names::EXIT_PLAN_MODE,
84 tool_names::ENTER_WORKTREE,
85 tool_names::EXIT_WORKTREE,
86 tool_names::ASK,
87 tool_names::LOAD_SKILL,
88 tool_names::TODO_WRITE,
90 tool_names::TODO_READ,
91 tool_names::TASK,
92 tool_names::AGENT,
94 tool_names::SEND_MESSAGE,
95 tool_names::TEAMMATE,
96];
97
98pub fn policy_for(tool_name: &str) -> ToolContextPolicy {
102 match tool_name {
103 tool_names::ENTER_PLAN_MODE
105 | tool_names::EXIT_PLAN_MODE
106 | tool_names::ENTER_WORKTREE
107 | tool_names::EXIT_WORKTREE
108 | tool_names::ASK
109 | tool_names::LOAD_SKILL
110 | tool_names::TODO_WRITE
111 | tool_names::TODO_READ
112 | tool_names::TASK
113 | tool_names::AGENT
114 | tool_names::SEND_MESSAGE
115 | tool_names::TEAMMATE => ToolContextPolicy::key_preserve(),
116
117 _ => ToolContextPolicy::regular_placeholder(),
120 }
121}
122
123pub fn is_key_tool(tool_name: &str) -> bool {
125 policy_for(tool_name).retention == RetentionPolicy::AlwaysPreserve
126}
127
128#[cfg(test)]
130pub fn tier_for(tool_name: &str) -> ContextTier {
131 policy_for(tool_name).tier
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn key_tool_list_matches_policy() {
140 for name in KEY_TOOL_NAMES {
142 assert!(
143 is_key_tool(name),
144 "KEY_TOOL_NAMES 中的 {} 应返回 AlwaysPreserve",
145 name
146 );
147 }
148 }
149
150 #[test]
151 fn regular_tools_are_placeholder() {
152 for name in [
153 tool_names::BASH,
154 tool_names::READ,
155 tool_names::WRITE,
156 tool_names::EDIT,
157 tool_names::GLOB,
158 tool_names::GREP,
159 tool_names::WEB_FETCH,
160 ] {
161 let p = policy_for(name);
162 assert_eq!(
163 p.tier,
164 ContextTier::RegularTool,
165 "{} 应为 RegularTool",
166 name
167 );
168 assert_eq!(
169 p.retention,
170 RetentionPolicy::Placeholder,
171 "{} 应为 Placeholder",
172 name
173 );
174 }
175 }
176
177 #[test]
178 fn unknown_tool_fallback_regular() {
179 let p = policy_for("SomeNewFutureTool");
180 assert_eq!(p.tier, ContextTier::RegularTool);
181 assert_eq!(p.retention, RetentionPolicy::Placeholder);
182 }
183
184 #[test]
185 fn tier_priority_ordering() {
186 assert!(ContextTier::System.priority() < ContextTier::User.priority());
187 assert!(ContextTier::User.priority() < ContextTier::KeyTool.priority());
188 assert!(ContextTier::KeyTool.priority() < ContextTier::Assistant.priority());
189 assert!(ContextTier::Assistant.priority() < ContextTier::RegularTool.priority());
190 }
191
192 #[test]
193 fn user_mentioned_key_tools_are_preserved() {
194 for name in [
196 tool_names::ENTER_PLAN_MODE,
197 tool_names::ENTER_WORKTREE,
198 tool_names::ASK,
199 tool_names::LOAD_SKILL,
200 ] {
201 assert!(is_key_tool(name), "{} 必须是 KeyTool", name);
202 }
203 }
204}