@@ -313,6 +313,10 @@ pub struct AgentDriverOptions {
313313 pub snapshot_upload_timeout : Option < Duration > ,
314314 /// Declarations script timeout override.
315315 pub snapshot_script_timeout : Option < Duration > ,
316+ /// Periodic checkpoint cadence override. Only used when
317+ /// `FeatureFlag::PeriodicHandoffCheckpoints` is enabled; deliberately separate
318+ /// from `snapshot_upload_timeout`/`snapshot_script_timeout` per-attempt budgets.
319+ pub checkpoint_interval : Option < Duration > ,
316320 /// Skip the initial `StartFromAmbientRunPrompt` so the agent waits for a
317321 /// follow-up instead of hallucinating an empty turn. Sourced from the
318322 /// `--skip-initial-turn` CLI flag, which the worker emits when the
@@ -378,6 +382,13 @@ pub struct AgentDriver {
378382 snapshot_upload_timeout : Duration ,
379383 snapshot_script_timeout : Duration ,
380384
385+ /// Periodic workspace-handoff checkpoint coordinator. `Some` only when
386+ /// `FeatureFlag::OzHandoff` and `FeatureFlag::PeriodicHandoffCheckpoints` are both
387+ /// enabled, the run has a cloud task id, and `--no-snapshot` was not set;
388+ /// `None` otherwise, in which case `run_snapshot_upload` falls back to the legacy
389+ /// one-shot upload path unchanged.
390+ checkpoint_coordinator : Option < checkpoint_coordinator:: CheckpointCoordinatorHandle > ,
391+
381392 /// Conversation ID this driver is running. Set at construction for
382393 /// resumed runs and on `ConversationServerTokenAssigned` for fresh
383394 /// runs; consumed by `unregister_streamer_consumer` at end of run.
@@ -651,6 +662,7 @@ impl AgentDriver {
651662 snapshot_disabled,
652663 snapshot_upload_timeout,
653664 snapshot_script_timeout,
665+ checkpoint_interval,
654666 skip_initial_turn,
655667 strict_mcp_startup,
656668 mcp_startup_timeout,
@@ -759,6 +771,32 @@ impl AgentDriver {
759771 _ => None ,
760772 } ;
761773
774+ // Spawn the periodic checkpoint coordinator under the same gates as the
775+ // declarations writer above, plus the dedicated rollout flag. `None` keeps
776+ // `run_snapshot_upload` on the legacy one-shot upload path unchanged.
777+ let checkpoint_coordinator = match task_id {
778+ Some ( id)
779+ if FeatureFlag :: OzHandoff . is_enabled ( )
780+ && FeatureFlag :: PeriodicHandoffCheckpoints . is_enabled ( )
781+ && !snapshot_disabled_value =>
782+ {
783+ let client = ServerApiProvider :: as_ref ( ctx) . get_harness_support_client ( ) ;
784+ Some ( checkpoint_coordinator:: CheckpointCoordinatorHandle :: new (
785+ client,
786+ id,
787+ working_dir. clone ( ) ,
788+ ctx. spawner ( ) ,
789+ checkpoint_interval
790+ . unwrap_or ( checkpoint_coordinator:: DEFAULT_CHECKPOINT_INTERVAL ) ,
791+ snapshot_script_timeout
792+ . unwrap_or ( snapshot:: DEFAULT_DECLARATIONS_SCRIPT_TIMEOUT ) ,
793+ snapshot_upload_timeout. unwrap_or ( snapshot:: DEFAULT_SNAPSHOT_UPLOAD_TIMEOUT ) ,
794+ ctx. background_executor ( ) ,
795+ ) )
796+ }
797+ _ => None ,
798+ } ;
799+
762800 Ok ( Self {
763801 terminal_driver,
764802 working_dir,
@@ -778,6 +816,7 @@ impl AgentDriver {
778816 . unwrap_or ( snapshot:: DEFAULT_SNAPSHOT_UPLOAD_TIMEOUT ) ,
779817 snapshot_script_timeout : snapshot_script_timeout
780818 . unwrap_or ( snapshot:: DEFAULT_DECLARATIONS_SCRIPT_TIMEOUT ) ,
819+ checkpoint_coordinator,
781820 run_conversation_id,
782821 parent_run_id : parent_run_id_for_self,
783822 third_party_harness_model_config,
@@ -821,6 +860,7 @@ impl AgentDriver {
821860 snapshot_disabled : false ,
822861 snapshot_upload_timeout : snapshot:: DEFAULT_SNAPSHOT_UPLOAD_TIMEOUT ,
823862 snapshot_script_timeout : snapshot:: DEFAULT_DECLARATIONS_SCRIPT_TIMEOUT ,
863+ checkpoint_coordinator : None ,
824864 run_conversation_id : None ,
825865 parent_run_id : None ,
826866 third_party_harness_model_config : None ,
@@ -3789,13 +3829,20 @@ impl AgentDriver {
37893829
37903830 // Snapshot upload is only meaningful for cloud task runs, so short-circuit before
37913831 // pulling the rest of the context onto this task.
3792- let Ok ( ( Some ( task_id) , snapshot_disabled, upload_timeout, script_timeout) ) = spawner
3832+ let Ok ( (
3833+ Some ( task_id) ,
3834+ snapshot_disabled,
3835+ upload_timeout,
3836+ script_timeout,
3837+ checkpoint_coordinator,
3838+ ) ) = spawner
37933839 . spawn ( |me, _| {
37943840 (
37953841 me. task_id ,
37963842 me. snapshot_disabled ,
37973843 me. snapshot_upload_timeout ,
37983844 me. snapshot_script_timeout ,
3845+ me. checkpoint_coordinator . clone ( ) ,
37993846 )
38003847 } )
38013848 . await
@@ -3807,6 +3854,16 @@ impl AgentDriver {
38073854 return ;
38083855 }
38093856
3857+ // When the periodic checkpoint coordinator is active, it owns the entire
3858+ // end-of-run path: `finalize` regenerates declarations, runs one last
3859+ // best-effort attempt bounded by `upload_timeout`, and commits it as the
3860+ // selected checkpoint. This replaces the legacy one-shot upload below so
3861+ // there is exactly one end-of-run snapshot path, not two.
3862+ if let Some ( coordinator) = checkpoint_coordinator {
3863+ coordinator. finalize ( upload_timeout) . await ;
3864+ return ;
3865+ }
3866+
38103867 let Ok ( ( working_dir, client) ) = spawner
38113868 . spawn ( |me, ctx| {
38123869 let client = ServerApiProvider :: as_ref ( ctx) . get_harness_support_client ( ) ;
0 commit comments