@@ -1902,20 +1902,13 @@ def _init_autonomy(self, autonomy: Any, verification_hooks: Optional[List[Any]]
19021902 from .autonomy import AutonomyConfig , AutonomyTrigger , DoomLoopTracker
19031903
19041904 if autonomy is True :
1905- self .autonomy_config = {}
19061905 config = AutonomyConfig ()
19071906 elif isinstance (autonomy , dict ):
1908- self .autonomy_config = autonomy .copy ()
19091907 config = AutonomyConfig .from_dict (autonomy )
19101908 # Extract verification_hooks from dict if provided
19111909 if "verification_hooks" in autonomy and not verification_hooks :
19121910 self ._verification_hooks = autonomy .get ("verification_hooks" , [])
19131911 elif isinstance (autonomy , AutonomyConfig ):
1914- self .autonomy_config = {
1915- "max_iterations" : autonomy .max_iterations ,
1916- "doom_loop_threshold" : autonomy .doom_loop_threshold ,
1917- "auto_escalate" : autonomy .auto_escalate ,
1918- }
19191912 config = autonomy
19201913 # Extract verification_hooks from AutonomyConfig if provided
19211914 if autonomy .verification_hooks and not verification_hooks :
@@ -1927,8 +1920,31 @@ def _init_autonomy(self, autonomy: Any, verification_hooks: Optional[List[Any]]
19271920 self ._doom_loop_tracker = None
19281921 return
19291922
1923+ # Preserve ALL AutonomyConfig fields in the dict (G14 fix: no lossy extraction)
1924+ self .autonomy_config = {
1925+ "enabled" : config .enabled ,
1926+ "level" : config .level ,
1927+ "max_iterations" : config .max_iterations ,
1928+ "doom_loop_threshold" : config .doom_loop_threshold ,
1929+ "auto_escalate" : config .auto_escalate ,
1930+ "observe" : config .observe ,
1931+ "completion_promise" : config .completion_promise ,
1932+ "clear_context" : config .clear_context ,
1933+ }
1934+ # Also preserve any extra user-provided keys from dict input
1935+ if isinstance (autonomy , dict ):
1936+ for k , v in autonomy .items ():
1937+ if k not in self .autonomy_config :
1938+ self .autonomy_config [k ] = v
1939+
1940+ # Store the AutonomyConfig object for typed access
1941+ self ._autonomy_config_obj = config
1942+
19301943 self ._autonomy_trigger = AutonomyTrigger ()
19311944 self ._doom_loop_tracker = DoomLoopTracker (threshold = config .doom_loop_threshold )
1945+
1946+ # Wire level → approval bridge (G3 fix)
1947+ self ._bridge_autonomy_level (config .level )
19321948
19331949 def analyze_prompt (self , prompt : str ) -> set :
19341950 """Analyze prompt for autonomy signals.
@@ -1986,6 +2002,22 @@ def _reset_doom_loop(self) -> None:
19862002 if self ._doom_loop_tracker is not None :
19872003 self ._doom_loop_tracker .reset ()
19882004
2005+ def _bridge_autonomy_level (self , level : str ) -> None :
2006+ """Bridge autonomy level to approval system (G3 fix).
2007+
2008+ When level is 'full_auto', sets PRAISONAI_AUTO_APPROVE env var
2009+ so the SDK approval registry auto-approves tool calls.
2010+
2011+ Args:
2012+ level: Autonomy level string (suggest, auto_edit, full_auto)
2013+ """
2014+ import os
2015+ if level == "full_auto" :
2016+ os .environ ["PRAISONAI_AUTO_APPROVE" ] = "true"
2017+ else :
2018+ # Don't remove if already set by CLI or user
2019+ pass
2020+
19892021 def run_autonomous (
19902022 self ,
19912023 prompt : str ,
@@ -2112,13 +2144,39 @@ def run_autonomous(
21122144 started_at = started_at ,
21132145 )
21142146
2115- # Record the action
2147+ response_str = str (response )
2148+
2149+ # Record the action for doom loop tracking (G1 fix: was missing)
2150+ self ._record_action (
2151+ "chat" , {"prompt" : prompt }, response_str [:200 ], True
2152+ )
2153+
2154+ # Record for action history
21162155 actions_taken .append ({
21172156 "iteration" : iterations ,
2118- "response" : str ( response ) [:500 ],
2157+ "response" : response_str [:500 ],
21192158 })
21202159
2121- response_str = str (response )
2160+ # Observability logging (G10 fix: wire observe field)
2161+ if self .autonomy_config .get ("observe" ):
2162+ logging .getLogger (__name__ ).info (
2163+ f"[autonomy] iteration={ iterations } stage={ stage } "
2164+ f"response_len={ len (response_str )} "
2165+ )
2166+
2167+ # Auto-save session after each iteration (memory integration)
2168+ self ._auto_save_session ()
2169+
2170+ # Auto-escalate stage if stuck (G11 fix: wire auto_escalate)
2171+ if (self .autonomy_config .get ("auto_escalate" )
2172+ and iterations > 1
2173+ and stage in ("direct" , "heuristic" )):
2174+ stage_order = ["direct" , "heuristic" , "planned" , "autonomous" ]
2175+ idx = stage_order .index (stage ) if stage in stage_order else 0
2176+ if idx < len (stage_order ) - 1 :
2177+ stage = stage_order [idx + 1 ]
2178+ if self .autonomy_config .get ("observe" ):
2179+ logging .getLogger (__name__ ).info (f"[autonomy] auto-escalated to stage={ stage } " )
21222180
21232181 # Check for completion promise FIRST (structured signal)
21242182 if effective_promise :
@@ -2328,13 +2386,37 @@ async def main():
23282386 started_at = started_at ,
23292387 )
23302388
2331- # Record the action
2389+ response_str = str (response )
2390+
2391+ # Record the action for doom loop tracking (G2 fix: was missing)
2392+ self ._record_action (
2393+ "chat" , {"prompt" : prompt }, response_str [:200 ], True
2394+ )
2395+
2396+ # Record for action history
23322397 actions_taken .append ({
23332398 "iteration" : iterations ,
2334- "response" : str ( response ) [:500 ],
2399+ "response" : response_str [:500 ],
23352400 })
23362401
2337- response_str = str (response )
2402+ # Observability logging (G10 fix: wire observe field)
2403+ if self .autonomy_config .get ("observe" ):
2404+ logging .getLogger (__name__ ).info (
2405+ f"[autonomy-async] iteration={ iterations } stage={ stage } "
2406+ f"response_len={ len (response_str )} "
2407+ )
2408+
2409+ # Auto-save session after each async iteration (memory integration)
2410+ self ._auto_save_session ()
2411+
2412+ # Auto-escalate stage if stuck (G11 fix: wire auto_escalate)
2413+ if (self .autonomy_config .get ("auto_escalate" )
2414+ and iterations > 1
2415+ and stage in ("direct" , "heuristic" )):
2416+ stage_order = ["direct" , "heuristic" , "planned" , "autonomous" ]
2417+ idx = stage_order .index (stage ) if stage in stage_order else 0
2418+ if idx < len (stage_order ) - 1 :
2419+ stage = stage_order [idx + 1 ]
23382420
23392421 # Check for completion promise FIRST (structured signal)
23402422 if effective_promise :
@@ -2390,6 +2472,9 @@ async def main():
23902472 # Yield control to allow other async tasks to run
23912473 await asyncio .sleep (0 )
23922474
2475+ # Final auto-save before returning
2476+ self ._auto_save_session ()
2477+
23932478 # Max iterations reached
23942479 return AutonomyResult (
23952480 success = False ,
0 commit comments