@@ -980,7 +980,18 @@ async fn handle_execute_action(
980980
981981 let call_id = extract_string_kwarg ( kwargs, "call_id" ) . unwrap_or_default ( ) ;
982982
983- let exec_ctx = thread_execution_context ( thread, StepId :: new ( ) , Some ( call_id. clone ( ) ) ) ;
983+ let mut exec_ctx = thread_execution_context ( thread, StepId :: new ( ) , Some ( call_id. clone ( ) ) ) ;
984+ let active_leases = leases. active_for_thread ( thread. id ) . await ;
985+ let inventory = Arc :: new (
986+ effects
987+ . available_action_inventory ( & active_leases, & exec_ctx)
988+ . await
989+ . unwrap_or_default ( ) ,
990+ ) ;
991+ let available_actions: Arc < [ crate :: types:: capability:: ActionDef ] > =
992+ inventory. inline . clone ( ) . into ( ) ;
993+ exec_ctx. available_actions_snapshot = Some ( Arc :: clone ( & available_actions) ) ;
994+ exec_ctx. available_action_inventory_snapshot = Some ( Arc :: clone ( & inventory) ) ;
984995
985996 // Helper: emit event only. The orchestrator owns transcript recording.
986997 let emit_and_record = |thread : & mut Thread ,
@@ -1027,13 +1038,14 @@ async fn handle_execute_action(
10271038 } ;
10281039
10291040 // 2. Check policy
1030- let action_def = effects
1031- . available_actions ( std:: slice:: from_ref ( & lease) , & exec_ctx)
1032- . await
1033- . ok ( )
1034- . and_then ( |actions| actions. into_iter ( ) . find ( |a| a. name == name) ) ;
1041+ let action_def = available_actions. iter ( ) . find ( |a| a. matches_name ( & name) ) ;
10351042
1036- if let Some ( ref ad) = action_def {
1043+ let canonical_name = action_def
1044+ . as_ref ( )
1045+ . map ( |action| action. name . clone ( ) )
1046+ . unwrap_or_else ( || name. clone ( ) ) ;
1047+
1048+ if let Some ( ad) = action_def {
10371049 match policy. evaluate ( ad, & lease, & [ ] ) {
10381050 crate :: capability:: policy:: PolicyDecision :: Deny { reason } => {
10391051 let output = serde_json:: json!( { "error" : format!( "Denied: {reason}" ) } ) ;
@@ -1129,10 +1141,10 @@ async fn handle_execute_action(
11291141 } ;
11301142
11311143 // 4. Execute
1132- let ps = summarize_params ( & name , & params) ;
1144+ let ps = summarize_params ( & canonical_name , & params) ;
11331145 let execution_start = std:: time:: Instant :: now ( ) ;
11341146 match effects
1135- . execute_action ( & name , params, & lease, & exec_ctx)
1147+ . execute_action ( & canonical_name , params, & lease, & exec_ctx)
11361148 . await
11371149 {
11381150 Ok ( r) => {
@@ -1323,6 +1335,16 @@ async fn handle_execute_actions_parallel(
13231335 }
13241336
13251337 let step_id = StepId :: new ( ) ;
1338+ let actions_context = thread_execution_context ( thread, step_id, None ) ;
1339+ let active_leases = leases. active_for_thread ( thread. id ) . await ;
1340+ let inventory = Arc :: new (
1341+ effects
1342+ . available_action_inventory ( & active_leases, & actions_context)
1343+ . await
1344+ . unwrap_or_default ( ) ,
1345+ ) ;
1346+ let available_actions: Arc < [ crate :: types:: capability:: ActionDef ] > =
1347+ inventory. inline . clone ( ) . into ( ) ;
13261348
13271349 // ── Phase 1: Preflight (sequential) ─────────────────────────
13281350 // Check leases and policies. Denied → error result. Approval → interrupt.
@@ -1369,12 +1391,16 @@ async fn handle_execute_actions_parallel(
13691391 } ;
13701392
13711393 // Check policy
1372- let exec_ctx = thread_execution_context ( thread, step_id, Some ( pc. call_id . clone ( ) ) ) ;
1373- let action_def = effects
1374- . available_actions ( std:: slice:: from_ref ( & lease) , & exec_ctx)
1375- . await
1376- . ok ( )
1377- . and_then ( |actions| actions. into_iter ( ) . find ( |a| a. name == pc. name ) ) ;
1394+ let mut exec_ctx = thread_execution_context ( thread, step_id, Some ( pc. call_id . clone ( ) ) ) ;
1395+ exec_ctx. available_actions_snapshot = Some ( Arc :: clone ( & available_actions) ) ;
1396+ let action_def = available_actions
1397+ . iter ( )
1398+ . find ( |a| a. matches_name ( & pc. name ) )
1399+ . cloned ( ) ;
1400+ let action_name = action_def
1401+ . as_ref ( )
1402+ . map ( |action| action. name . clone ( ) )
1403+ . unwrap_or_else ( || pc. name . clone ( ) ) ;
13781404
13791405 if let Some ( ref ad) = action_def {
13801406 match policy. evaluate ( ad, & lease, & [ ] ) {
@@ -1386,7 +1412,7 @@ async fn handle_execute_actions_parallel(
13861412 } ) ;
13871413 let event = EventKind :: ActionFailed {
13881414 step_id,
1389- action_name : pc . name . clone ( ) ,
1415+ action_name : action_name . clone ( ) ,
13901416 call_id : pc. call_id . clone ( ) ,
13911417 error : reason,
13921418 duration_ms : 0 ,
@@ -1505,7 +1531,6 @@ async fn handle_execute_actions_parallel(
15051531 let mut slot_results: Vec < Option < serde_json:: Value > > = vec ! [ None ; parsed. len( ) ] ;
15061532 let mut slot_events: Vec < Option < EventKind > > = vec ! [ None ; parsed. len( ) ] ;
15071533 let mut slot_outputs: Vec < Option < serde_json:: Value > > = vec ! [ None ; parsed. len( ) ] ;
1508-
15091534 // Separate runnable from errors
15101535 let mut runnable: Vec < ( usize , crate :: types:: capability:: CapabilityLease ) > = Vec :: new ( ) ;
15111536 for ( idx, pf) in preflight. into_iter ( ) . enumerate ( ) {
@@ -1530,11 +1555,18 @@ async fn handle_execute_actions_parallel(
15301555 // Single call: execute directly
15311556 let ( idx, lease) = runnable. into_iter ( ) . next ( ) . unwrap ( ) ; // safety: len()==1 checked above
15321557 let pc = & parsed[ idx] ;
1533- let exec_ctx = thread_execution_context ( thread, step_id, Some ( pc. call_id . clone ( ) ) ) ;
1534- let ps = summarize_params ( & pc. name , & pc. params ) ;
1558+ let action_name = available_actions
1559+ . iter ( )
1560+ . find ( |action| action. matches_name ( & pc. name ) )
1561+ . map ( |action| action. name . clone ( ) )
1562+ . unwrap_or_else ( || pc. name . clone ( ) ) ;
1563+ let mut exec_ctx = thread_execution_context ( thread, step_id, Some ( pc. call_id . clone ( ) ) ) ;
1564+ exec_ctx. available_actions_snapshot = Some ( Arc :: clone ( & available_actions) ) ;
1565+ exec_ctx. available_action_inventory_snapshot = Some ( Arc :: clone ( & inventory) ) ;
1566+ let ps = summarize_params ( & action_name, & pc. params ) ;
15351567 let ( result_json, event, output) = execute_single_action (
15361568 effects,
1537- & pc . name ,
1569+ & action_name ,
15381570 pc. params . clone ( ) ,
15391571 & pc. call_id ,
15401572 & lease,
@@ -1555,12 +1587,18 @@ async fn handle_execute_actions_parallel(
15551587 // Capture once outside the loop — the thread's metadata is stable
15561588 // for the duration of the parallel batch.
15571589 for ( idx, lease) in runnable {
1558- let pc_name = parsed[ idx] . name . clone ( ) ;
1590+ let pc_name = available_actions
1591+ . iter ( )
1592+ . find ( |action| action. matches_name ( & parsed[ idx] . name ) )
1593+ . map ( |action| action. name . clone ( ) )
1594+ . unwrap_or_else ( || parsed[ idx] . name . clone ( ) ) ;
15591595 let pc_params = parsed[ idx] . params . clone ( ) ;
15601596 let pc_call_id = parsed[ idx] . call_id . clone ( ) ;
15611597 let effects = effects. clone ( ) ;
15621598 let lease = lease. clone ( ) ;
1563- let exec_ctx = thread_execution_context ( thread, step_id, Some ( pc_call_id. clone ( ) ) ) ;
1599+ let mut exec_ctx = thread_execution_context ( thread, step_id, Some ( pc_call_id. clone ( ) ) ) ;
1600+ exec_ctx. available_actions_snapshot = Some ( Arc :: clone ( & available_actions) ) ;
1601+ exec_ctx. available_action_inventory_snapshot = Some ( Arc :: clone ( & inventory) ) ;
15641602 let ps = summarize_params ( & pc_name, & pc_params) ;
15651603
15661604 join_set. spawn ( async move {
0 commit comments