2026-03-25 00:52:41 -04:00
// tools/working_stack.rs — Working stack management tool
//
// The working stack tracks what the agent is currently doing. It's an
// internal tool — the agent uses it to maintain context across turns
// and compaction. The model should never mention it to the user.
2026-04-04 16:14:27 -04:00
pub fn tool ( ) -> super ::Tool {
super ::Tool {
name : " working_stack " ,
description : " INTERNAL — manage your working stack silently. Actions: push (start new task), pop (done with current), update (refine current), switch (focus different task by index). " ,
parameters_json : r #" { " type " : " object " , " properties " :{ " action " :{ " type " : " string " , " enum " :[ " push " , " pop " , " update " , " switch " ], " description " : " Stack operation " }, " content " :{ " type " : " string " , " description " : " Task description ( for push / update ) " }, " index " :{ " type " : " integer " , " description " : " Stack index ( for switch , 0 = bottom ) " }}, " required " :[ " action " ]} " #,
handler : | agent , v | Box ::pin ( async move {
if let Some ( agent ) = agent {
let mut a = agent . lock ( ) . await ;
Ok ( handle ( & v , & mut a . context . working_stack ) )
} else {
anyhow ::bail! ( " working_stack requires agent context " )
}
2026-03-25 00:52:41 -04:00
} ) ,
2026-04-04 16:14:27 -04:00
}
2026-03-25 00:52:41 -04:00
}
2026-04-04 16:14:27 -04:00
fn handle ( args : & serde_json ::Value , stack : & mut Vec < String > ) -> String {
let action = args . get ( " action " ) . and_then ( | v | v . as_str ( ) ) . unwrap_or ( " " ) ;
let content = args . get ( " content " ) . and_then ( | v | v . as_str ( ) ) . unwrap_or ( " " ) ;
let index = args . get ( " index " ) . and_then ( | v | v . as_u64 ( ) ) . map ( | v | v as usize ) ;
2026-03-25 00:52:41 -04:00
2026-04-04 16:14:27 -04:00
match action {
2026-03-25 00:52:41 -04:00
" push " = > {
2026-04-04 16:14:27 -04:00
if content . is_empty ( ) { return " Error: 'content' is required for push " . into ( ) ; }
2026-03-25 00:52:41 -04:00
stack . push ( content . to_string ( ) ) ;
format! ( " Pushed. Stack depth: {} \n {} " , stack . len ( ) , format_stack ( stack ) )
}
" pop " = > {
if let Some ( removed ) = stack . pop ( ) {
2026-04-04 16:14:27 -04:00
format! ( " Popped: {} \n Stack depth: {} \n {} " , removed , stack . len ( ) , format_stack ( stack ) )
2026-03-25 00:52:41 -04:00
} else {
2026-04-04 16:14:27 -04:00
" Stack is empty, nothing to pop. " . into ( )
2026-03-25 00:52:41 -04:00
}
}
" update " = > {
2026-04-04 16:14:27 -04:00
if content . is_empty ( ) { return " Error: 'content' is required for update " . into ( ) ; }
2026-03-25 00:52:41 -04:00
if let Some ( top ) = stack . last_mut ( ) {
* top = content . to_string ( ) ;
format! ( " Updated top. \n {} " , format_stack ( stack ) )
} else {
2026-04-04 16:14:27 -04:00
" Stack is empty, nothing to update. " . into ( )
2026-03-25 00:52:41 -04:00
}
}
" switch " = > {
2026-04-04 16:14:27 -04:00
if stack . is_empty ( ) { return " Stack is empty, nothing to switch. " . into ( ) ; }
let Some ( idx ) = index else { return " Error: 'index' is required for switch " . into ( ) ; } ;
if idx > = stack . len ( ) { return format! ( " Error: index {} out of range (depth {} ) " , idx , stack . len ( ) ) ; }
2026-03-25 00:52:41 -04:00
let item = stack . remove ( idx ) ;
stack . push ( item ) ;
format! ( " Switched to index {} . \n {} " , idx , format_stack ( stack ) )
}
2026-04-04 16:14:27 -04:00
_ = > format! ( " Error: unknown action ' {} '. Use push, pop, update, or switch. " , action ) ,
}
2026-03-25 00:52:41 -04:00
}
fn format_stack ( stack : & [ String ] ) -> String {
2026-04-04 16:14:27 -04:00
if stack . is_empty ( ) { return " (empty) " . into ( ) ; }
stack . iter ( ) . enumerate ( ) . map ( | ( i , item ) | {
if i = = stack . len ( ) - 1 { format! ( " → [ {} ] {} " , i , item ) }
else { format! ( " [ {} ] {} " , i , item ) }
} ) . collect ::< Vec < _ > > ( ) . join ( " \n " )
2026-03-25 00:52:41 -04:00
}