@@ -165,6 +165,7 @@ impl RegisteredSkill {
165165pub struct AgentEngine {
166166 llm : Arc < dyn LlmProvider > ,
167167 memory : Arc < dyn MemoryStore > ,
168+ state_cache : Arc < dyn crate :: StateProjectionCache > ,
168169 skill_store : Option < Arc < dyn SkillStore > > ,
169170 skills : Arc < DashMap < String , RegisteredSkill > > ,
170171 planner : Option < Arc < dyn Planner > > ,
@@ -175,12 +176,22 @@ impl AgentEngine {
175176 Self {
176177 llm,
177178 memory,
179+ state_cache : Arc :: new ( crate :: InMemoryStateCache :: new ( ) ) ,
178180 skill_store : None ,
179181 skills : Arc :: new ( DashMap :: new ( ) ) ,
180182 planner : None ,
181183 }
182184 }
183185
186+ pub fn state_cache ( & self ) -> Arc < dyn crate :: StateProjectionCache > {
187+ self . state_cache . clone ( )
188+ }
189+
190+ pub fn with_state_cache ( mut self , state_cache : Arc < dyn crate :: StateProjectionCache > ) -> Self {
191+ self . state_cache = state_cache;
192+ self
193+ }
194+
184195 pub fn with_skill_store ( mut self , skill_store : Arc < dyn SkillStore > ) -> Self {
185196 self . skill_store = Some ( skill_store) ;
186197 self
@@ -269,7 +280,7 @@ impl AgentEngine {
269280 trigger : request. trigger . clone ( ) ,
270281 intent : None ,
271282 } ;
272- if let Err ( err) = self . memory . append_trigger ( trigger_record) . await {
283+ if let Err ( err) = self . memory . append_trigger ( trigger_record. clone ( ) ) . await {
273284 return Ok ( AdvanceResult {
274285 outcome : Some ( storage_failure_outcome ( trigger_id, 0 , err. message ) ) ,
275286 emitted_events : Vec :: new ( ) ,
@@ -278,15 +289,21 @@ impl AgentEngine {
278289 } ) ;
279290 }
280291
281- let mut snapshot = match self . memory . load_session ( & request. session_id ) . await {
282- Ok ( snapshot) => snapshot,
283- Err ( err) => {
284- return Ok ( AdvanceResult {
285- outcome : Some ( storage_failure_outcome ( trigger_id, 0 , err. message ) ) ,
286- emitted_events : Vec :: new ( ) ,
287- state_delta : AgentStateDelta :: default ( ) ,
288- wake_request : None ,
289- } ) ;
292+ let mut snapshot = match self . state_cache . get_projection ( & request. session_id ) . await {
293+ Ok ( Some ( mut cached) ) => {
294+ cached. records . push ( SessionRecord :: Trigger ( trigger_record) ) ;
295+ cached
296+ }
297+ _ => match self . memory . load_session ( & request. session_id ) . await {
298+ Ok ( snapshot) => snapshot,
299+ Err ( err) => {
300+ return Ok ( AdvanceResult {
301+ outcome : Some ( storage_failure_outcome ( trigger_id, 0 , err. message ) ) ,
302+ emitted_events : Vec :: new ( ) ,
303+ state_delta : AgentStateDelta :: default ( ) ,
304+ wake_request : None ,
305+ } ) ;
306+ }
290307 }
291308 } ;
292309 counter ! ( "rain_engine.triggers_total" ) . increment ( 1 ) ;
@@ -681,7 +698,7 @@ impl AgentEngine {
681698 return Ok ( build_advance_result ( outcome, emitted_events) ) ;
682699 }
683700
684- let available_skills = self
701+ let mut available_skills = self
685702 . skills
686703 . iter ( )
687704 . filter ( |skill| {
@@ -695,13 +712,38 @@ impl AgentEngine {
695712 . map ( |skill| skill. value ( ) . definition ( ) )
696713 . collect :: < Vec < _ > > ( ) ;
697714
715+ // Enforce Strategy Preferences: If a tool has a high failure rate, append a warning
716+ // to its description so the LLM avoids using it.
717+ let preferences: HashMap < _ , _ > = context
718+ . records
719+ . iter ( )
720+ . filter_map ( |record| {
721+ if let SessionRecord :: StrategyPreference ( pref) = record {
722+ if let Some ( skill_name) = & pref. skill_name {
723+ return Some ( ( skill_name. clone ( ) , pref. reason . clone ( ) ) ) ;
724+ }
725+ }
726+ None
727+ } )
728+ . collect ( ) ;
729+
730+ for def in & mut available_skills {
731+ if let Some ( reason) = preferences. get ( & def. manifest . name ) {
732+ def. manifest . description = format ! (
733+ "{} [CRITICAL WARNING: Avoid using this tool if possible. Reason: {}]" ,
734+ def. manifest. description, reason
735+ ) ;
736+ }
737+ }
738+
739+ let plan = context. active_execution_plan ( ) ;
698740 let provider_request = ProviderRequest {
699741 trigger : trigger. clone ( ) ,
700742 context : context. to_snapshot ( steps_executed) ,
701743 available_skills,
702744 config : context. metadata . provider . clone ( ) ,
703745 policy : context. metadata . policy . clone ( ) ,
704- contents : build_provider_contents ( & trigger, & context. records ) ,
746+ contents : build_provider_contents ( & trigger, & context. records , plan . as_ref ( ) ) ,
705747 } ;
706748 let provider_started = Instant :: now ( ) ;
707749 let decision = match tokio:: time:: timeout (
@@ -1618,21 +1660,26 @@ impl AgentEngine {
16181660 . append_outcome ( & context. session_id , outcome. clone ( ) )
16191661 . await
16201662 {
1621- error ! ( "failed to persist outcome: {}" , err. message) ;
1622- return Ok ( storage_failure_outcome (
1623- context. metadata . trigger_id . clone ( ) ,
1624- steps_executed,
1625- err. message ,
1626- ) ) ;
1663+ warn ! ( session_id = %context. session_id, "failed to record outcome: {}" , err. message) ;
16271664 }
1628- context
1629- . records
1630- . push ( SessionRecord :: Outcome ( outcome. clone ( ) ) ) ;
1631- let session_id = context. session_id . clone ( ) ;
1632- let snapshot = context. to_snapshot ( steps_executed) ;
1665+
1666+ context. records . push ( SessionRecord :: Outcome ( outcome. clone ( ) ) ) ;
1667+
16331668 let outcome_clone = outcome. clone ( ) ;
1634- self . run_self_improvement ( session_id , snapshot , outcome_clone)
1669+ self . run_self_improvement ( context , outcome_clone)
16351670 . await ?;
1671+
1672+ // Update the state projection cache AFTER self improvement
1673+ let _ = self . state_cache . set_projection (
1674+ & context. session_id ,
1675+ SessionSnapshot {
1676+ session_id : context. session_id . clone ( ) ,
1677+ records : context. records . clone ( ) ,
1678+ last_sequence_no : None ,
1679+ latest_outcome : Some ( outcome. clone ( ) ) ,
1680+ }
1681+ ) . await ;
1682+
16361683 Ok ( EngineOutcome {
16371684 trigger_id : outcome. trigger_id ,
16381685 stop_reason,
@@ -1645,19 +1692,20 @@ impl AgentEngine {
16451692 }
16461693
16471694 #[ instrument(
1648- skip( self , snapshot , outcome) ,
1695+ skip( self , context , outcome) ,
16491696 fields(
1650- session_id = %session_id,
1651- trigger_id = %snapshot . trigger_id,
1697+ session_id = %context . session_id,
1698+ trigger_id = %context . metadata . trigger_id,
16521699 stop_reason = ?outcome. stop_reason
16531700 )
16541701 ) ]
16551702 async fn run_self_improvement (
16561703 & self ,
1657- session_id : String ,
1658- snapshot : AgentContextSnapshot ,
1704+ context : & mut AgentContext ,
16591705 outcome : OutcomeRecord ,
16601706 ) -> Result < ( ) , EngineError > {
1707+ let snapshot = context. to_snapshot ( outcome. steps_executed ) ;
1708+ let session_id = context. session_id . clone ( ) ;
16611709 let policy = snapshot. policy . self_improvement . clone ( ) ;
16621710 if !policy. enabled {
16631711 return Ok ( ( ) ) ;
@@ -1680,11 +1728,14 @@ impl AgentEngine {
16801728 self . memory
16811729 . append_reflection ( & session_id, reflection. clone ( ) )
16821730 . await ?;
1731+ context. records . push ( SessionRecord :: Reflection ( reflection) ) ;
16831732
16841733 for performance in summarize_tool_performance ( & snapshot. history ) {
16851734 self . memory
16861735 . append_tool_performance ( & session_id, performance. clone ( ) )
16871736 . await ?;
1737+ context. records . push ( SessionRecord :: ToolPerformance ( performance. clone ( ) ) ) ;
1738+
16881739 if performance. calls > 0 {
16891740 counter ! (
16901741 "rain_engine.tool_performance_summaries_total" ,
@@ -1706,8 +1757,9 @@ impl AgentEngine {
17061757 confidence : 0.68 ,
17071758 } ;
17081759 self . memory
1709- . append_strategy_preference ( & session_id, preference)
1760+ . append_strategy_preference ( & session_id, preference. clone ( ) )
17101761 . await ?;
1762+ context. records . push ( SessionRecord :: StrategyPreference ( preference) ) ;
17111763 }
17121764 }
17131765
@@ -1717,8 +1769,9 @@ impl AgentEngine {
17171769
17181770 if let Some ( rollback) = maybe_rollback_regression ( & snapshot, & outcome) {
17191771 self . memory
1720- . append_policy_tuning ( & session_id, rollback)
1772+ . append_policy_tuning ( & session_id, rollback. clone ( ) )
17211773 . await ?;
1774+ context. records . push ( SessionRecord :: PolicyTuning ( rollback) ) ;
17221775 counter ! ( "rain_engine.self_improvement_rollbacks_total" ) . increment ( 1 ) ;
17231776 return Ok ( ( ) ) ;
17241777 }
@@ -1736,8 +1789,9 @@ impl AgentEngine {
17361789 PolicyTuningAction :: Proposed | PolicyTuningAction :: RolledBack => { }
17371790 }
17381791 self . memory
1739- . append_policy_tuning ( & session_id, tuning)
1792+ . append_policy_tuning ( & session_id, tuning. clone ( ) )
17401793 . await ?;
1794+ context. records . push ( SessionRecord :: PolicyTuning ( tuning) ) ;
17411795
17421796 let profile_patch = ProfilePatchRecord {
17431797 patch_id : format ! ( "profile-patch-{}" , Uuid :: new_v4( ) ) ,
@@ -1748,8 +1802,9 @@ impl AgentEngine {
17481802 applied : true ,
17491803 } ;
17501804 self . memory
1751- . append_profile_patch ( & session_id, profile_patch)
1805+ . append_profile_patch ( & session_id, profile_patch. clone ( ) )
17521806 . await ?;
1807+ context. records . push ( SessionRecord :: ProfilePatch ( profile_patch) ) ;
17531808
17541809 Ok ( ( ) )
17551810 }
@@ -1986,12 +2041,13 @@ fn maybe_rollback_regression(
19862041 if !snapshot. policy . self_improvement . rollback_on_regression {
19872042 return None ;
19882043 }
2044+ // MaxStepsReached is not a regression. Hitting the step limit at a higher budget
2045+ // is progress, not a failure of the policy overlay itself.
19892046 if !matches ! (
19902047 outcome. stop_reason,
19912048 StopReason :: ProviderFailure
19922049 | StopReason :: DeadlineExceeded
19932050 | StopReason :: PolicyAborted
1994- | StopReason :: MaxStepsReached
19952051 ) {
19962052 return None ;
19972053 }
@@ -2048,7 +2104,11 @@ fn propose_policy_tuning(
20482104 ) ;
20492105 }
20502106 StopReason :: MaxStepsReached => {
2051- patch. max_steps = Some ( increase_usize_by_percent ( snapshot. policy . max_steps , delta) ) ;
2107+ // Scale up aggressively if we keep hitting the step limit (delta, then 2x delta, etc.)
2108+ // We use the terminal observation count as a multiplier
2109+ let multiplier = terminal_observation_count ( & snapshot. history ) . max ( 1 ) as f64 ;
2110+ let aggressive_delta = ( delta * multiplier) . min ( 200.0 ) ; // Cap at 200% increase per try
2111+ patch. max_steps = Some ( increase_usize_by_percent ( snapshot. policy . max_steps , aggressive_delta) ) ;
20522112 reason = Some (
20532113 "Session hit max steps; increasing future step budget within guardrails."
20542114 . to_string ( ) ,
@@ -2082,6 +2142,7 @@ fn propose_policy_tuning(
20822142 status : match improvement. mode {
20832143 SelfImprovementMode :: Advisory => PolicyOverlayStatus :: Proposed ,
20842144 SelfImprovementMode :: AutoWithGuardrails => PolicyOverlayStatus :: Applied ,
2145+ SelfImprovementMode :: Shadow => PolicyOverlayStatus :: Proposed , // Will be elevated to Applied by the shadow task
20852146 } ,
20862147 reason,
20872148 evidence_window_records : snapshot. history . len ( ) ,
@@ -2103,6 +2164,10 @@ fn propose_policy_tuning(
21032164 } else {
21042165 match improvement. mode {
21052166 SelfImprovementMode :: Advisory => PolicyTuningAction :: Proposed ,
2167+ SelfImprovementMode :: Shadow => {
2168+ overlay. status = PolicyOverlayStatus :: Proposed ; // Start as proposed in shadow mode
2169+ PolicyTuningAction :: Proposed
2170+ } ,
21062171 SelfImprovementMode :: AutoWithGuardrails => PolicyTuningAction :: Applied ,
21072172 }
21082173 } ;
@@ -2627,6 +2692,7 @@ fn derive_trigger_kernel_events(
26272692fn build_provider_contents (
26282693 trigger : & AgentTrigger ,
26292694 history : & [ SessionRecord ] ,
2695+ active_plan : Option < & ExecutionPlanRecord > ,
26302696) -> Vec < ProviderMessage > {
26312697 let mut messages = Vec :: new ( ) ;
26322698 let mut intent_by_trigger = HashMap :: < String , String > :: new ( ) ;
@@ -2706,6 +2772,19 @@ fn build_provider_contents(
27062772 parts : build_trigger_parts ( trigger) ,
27072773 } ) ;
27082774
2775+ // Inject Chain of Thought active plan if one exists
2776+ if let Some ( plan) = active_plan {
2777+ messages. push ( ProviderMessage {
2778+ role : ProviderRole :: System ,
2779+ parts : vec ! [ ProviderContentPart :: Text ( format!(
2780+ "You are currently executing an active, multi-step plan.\n \n OBJECTIVE: {}\n \n You are on step {} of {}.\n \n Please stay focused on the objective and execute the necessary actions for this specific step." ,
2781+ plan. objective,
2782+ plan. current_step_index + 1 ,
2783+ plan. steps. len( )
2784+ ) ) ] ,
2785+ } ) ;
2786+ }
2787+
27092788 messages
27102789}
27112790
0 commit comments