@@ -10,9 +10,9 @@ use std::thread;
1010use std:: time:: { Duration , Instant } ;
1111
1212use rustscript_vm:: {
13- CallOutcome , CallReturn , HostApiBuilder , HostApiCatalog , HostFunctionRegistry ,
14- HostFunctionSchema , HostParamSchema , HostTypeSchema , Value , Vm , VmError , VmResult ,
15- catalog_import_schemas, standard_host_catalog,
13+ CallOutcome , CallReturn , CancellationReason , HostApiBuilder , HostApiCatalog ,
14+ HostFunctionRegistry , HostFunctionSchema , HostParamSchema , HostTypeSchema , Value , Vm , VmError ,
15+ VmResult , catalog_import_schemas, standard_host_catalog,
1616} ;
1717use serde_json:: { Value as JsonValue , json} ;
1818
@@ -258,6 +258,8 @@ pub trait AgentProviderHost: Send + Sync {
258258 fn call ( & self , request : & JsonValue , cancellation : & RunCancellation ) -> JsonValue ;
259259}
260260
261+ pub type ControlCheckHook = Arc < dyn Fn ( & RunCancellation ) + Send + Sync > ;
262+
261263/// Injectable host bridges for one compiled runner.
262264#[ derive( Clone , Default ) ]
263265pub struct AgentHostBridges {
@@ -273,6 +275,9 @@ pub struct AgentHostBridges {
273275 pub filesystem : Option < Arc < FilesystemCapability > > ,
274276 pub processes : Option < Arc < ProcessCapability > > ,
275277 pub artifacts : Option < Arc < ArtifactCapability > > ,
278+ /// Optional test hook invoked from `agent::control_check` before reading
279+ /// cancellation/deadline. Production callers leave this unset.
280+ pub control_hook : Option < ControlCheckHook > ,
276281}
277282
278283/// Per-VM state installed before `run(context)`.
@@ -290,12 +295,21 @@ pub struct AgentHostState {
290295 pub processes : Option < Arc < ProcessCapability > > ,
291296 pub artifacts : Option < Arc < ArtifactCapability > > ,
292297 pub ( crate ) leases : Arc < Mutex < HashMap < String , ExecutionLease > > > ,
298+ pub ( crate ) control_hook : Option < ControlCheckHook > ,
293299}
294300
295301impl AgentHostState {
296302 fn control_error ( & self ) -> Option < JsonValue > {
297- if self . cancellation . requested ( ) . is_some ( ) {
298- return Some ( typed_fail ( "cancelled" , "run was cancelled" ) ) ;
303+ if let Some ( hook) = & self . control_hook {
304+ hook ( & self . cancellation ) ;
305+ }
306+ if let Some ( reason) = self . cancellation . requested ( ) {
307+ return Some ( match reason {
308+ CancellationReason :: Deadline => {
309+ typed_fail ( "deadline_elapsed" , "run deadline elapsed" )
310+ }
311+ _ => typed_fail ( "cancelled" , "run was cancelled" ) ,
312+ } ) ;
299313 }
300314 if self . cancellation . deadline_passed ( ) {
301315 return Some ( typed_fail ( "deadline_elapsed" , "run deadline elapsed" ) ) ;
0 commit comments