1use super::{model_context::ModelContextResetReason, Session, SessionKind};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum RootThinkingMode {
11 Standard,
12 Ultra,
13}
14
15#[derive(Debug, thiserror::Error, PartialEq, Eq)]
16#[error("thinking_mode and root orchestration enabled selection conflict")]
17pub struct RootThinkingModeConflict;
18
19impl RootThinkingMode {
20 pub fn deserialize_selection<'de, D>(deserializer: D) -> Result<Option<Self>, D::Error>
22 where
23 D: serde::Deserializer<'de>,
24 {
25 <Self as serde::Deserialize>::deserialize(deserializer).map(Some)
26 }
27
28 pub fn from_enabled(enabled: bool) -> Self {
29 if enabled {
30 Self::Ultra
31 } else {
32 Self::Standard
33 }
34 }
35
36 pub fn enabled(self) -> bool {
37 self == Self::Ultra
38 }
39
40 pub fn resolve_selection(
42 mode: Option<Self>,
43 enabled: Option<bool>,
44 ) -> Result<Option<bool>, RootThinkingModeConflict> {
45 if let (Some(mode), Some(enabled)) = (mode, enabled) {
46 if mode.enabled() != enabled {
47 return Err(RootThinkingModeConflict);
48 }
49 }
50 Ok(mode.map(Self::enabled).or(enabled))
51 }
52}
53
54pub const ROOT_ORCHESTRATION_TOOLS: [&str; 18] = [
58 "SubAgent",
59 "spawn_session",
60 "Plan",
61 "Task",
62 "session_history_current",
63 "session_history",
64 "session_message",
65 "Read",
66 "Grep",
67 "Glob",
68 "GetFileInfo",
69 "ViewImage",
70 "work_overview",
71 "work_search",
72 "work_inspect",
73 "work_changes",
74 "work_update",
75 "work_dispatch",
76];
77
78pub fn orchestration_only_allows_execution_name(execution_name: &str) -> bool {
79 ROOT_ORCHESTRATION_TOOLS.contains(&execution_name)
80}
81
82pub const SUPERVISOR_ORCHESTRATION_TOOLS: [&str; 15] = [
87 "SubAgent",
88 "spawn_session",
89 "Plan",
90 "Task",
91 "session_history_current",
92 "session_history",
93 "session_message",
94 "session_control",
95 "work_overview",
96 "work_search",
97 "work_inspect",
98 "work_changes",
99 "work_update",
100 "work_dispatch",
101 "work_review",
102];
103
104pub fn orchestration_only_allows_execution_name_for_supervisor(
105 execution_name: &str,
106 executing_supervisor: bool,
107) -> bool {
108 if executing_supervisor {
109 SUPERVISOR_ORCHESTRATION_TOOLS.contains(&execution_name)
110 } else {
111 orchestration_only_allows_execution_name(execution_name)
112 }
113}
114
115#[derive(Debug, thiserror::Error, PartialEq, Eq)]
116pub enum RootToolAuthorityError {
117 #[error("only a Root Session may select orchestration-only tool authority")]
118 NotRoot,
119 #[error("Root tool authority revision cannot advance")]
120 RevisionOverflow,
121 #[error("Root tool authority snapshot is stale or inconsistent")]
122 StaleSnapshot,
123 #[error("orchestration-only Root cannot enter while legacy Plan mode is active")]
124 LegacyPlanActive,
125 #[error("orchestration-only Root cannot use a selected Skill or Workflow")]
126 WorkflowSelected,
127}
128
129impl Session {
130 pub fn supervisor_orchestration_revision(&self) -> Option<u64> {
136 self.supervisor_incarnation_id().map(|_| 1)
137 }
138
139 pub fn root_thinking_mode(&self) -> RootThinkingMode {
140 RootThinkingMode::from_enabled(self.root_orchestration_only_enabled())
141 }
142
143 pub fn root_orchestration_only_enabled(&self) -> bool {
144 self.kind == SessionKind::Root
145 && self.parent_session_id.is_none()
146 && self.root_orchestration_only
147 }
148
149 pub fn set_root_orchestration_only(
151 &mut self,
152 enabled: bool,
153 ) -> Result<(), RootToolAuthorityError> {
154 if self.kind != SessionKind::Root || self.parent_session_id.is_some() {
155 return Err(RootToolAuthorityError::NotRoot);
156 }
157 if enabled {
158 self.validate_root_orchestration_compatibility()?;
159 }
160 if self.root_orchestration_only == enabled {
161 return Ok(());
162 }
163 let next = self
164 .root_tool_authority_revision
165 .checked_add(1)
166 .ok_or(RootToolAuthorityError::RevisionOverflow)?;
167 self.root_orchestration_only = enabled;
168 self.root_tool_authority_revision = next;
169 if self.model_context_state.is_some() {
170 self.reset_model_context_epoch(ModelContextResetReason::CacheScopeChanged);
171 }
172 Ok(())
173 }
174
175 pub fn validate_root_orchestration_compatibility(&self) -> Result<(), RootToolAuthorityError> {
178 if self
179 .agent_runtime_state
180 .as_ref()
181 .is_some_and(|state| state.plan_mode.is_some())
182 {
183 return Err(RootToolAuthorityError::LegacyPlanActive);
184 }
185 if self.selected_skill_ids().is_some_and(|ids| !ids.is_empty()) {
186 return Err(RootToolAuthorityError::WorkflowSelected);
187 }
188 Ok(())
189 }
190
191 pub fn model_orchestration_only_enabled(&self) -> bool {
195 self.root_orchestration_only_enabled() || self.supervisor_incarnation_id().is_some()
196 }
197
198 pub fn allows_model_tool_execution(&self, execution_name: &str) -> bool {
200 !self.model_orchestration_only_enabled()
201 || orchestration_only_allows_execution_name_for_supervisor(
202 execution_name,
203 self.supervisor_incarnation_id().is_some(),
204 )
205 }
206
207 pub fn adopt_root_tool_authority_from(
210 &mut self,
211 latest: &Session,
212 ) -> Result<(), RootToolAuthorityError> {
213 fn root_id(session: &Session) -> &str {
217 if session.kind == SessionKind::Root && session.root_session_id.is_empty() {
218 session.id.as_str()
219 } else {
220 session.root_session_id.as_str()
221 }
222 }
223 if !super::compatibility::same_session_tool_contract(
224 self.session_version,
225 latest.session_version,
226 ) || self.id != latest.id
227 || self.kind != latest.kind
228 || self.created_at != latest.created_at
229 || self.authority_identity != latest.authority_identity
230 || self.parent_session_id != latest.parent_session_id
231 || root_id(self) != root_id(latest)
232 || self.spawn_depth != latest.spawn_depth
233 || (self.root_orchestration_only && self.root_tool_authority_revision == 0)
234 || (latest.root_orchestration_only && latest.root_tool_authority_revision == 0)
235 || latest.root_tool_authority_revision < self.root_tool_authority_revision
236 || (latest.root_tool_authority_revision == self.root_tool_authority_revision
237 && latest.root_orchestration_only != self.root_orchestration_only)
238 || latest.root_mode_transition_epoch < self.root_mode_transition_epoch
239 || (latest.root_mode_transition_epoch == self.root_mode_transition_epoch
240 && latest.root_mode_operations != self.root_mode_operations)
241 {
242 return Err(RootToolAuthorityError::StaleSnapshot);
243 }
244 let selections = latest.root_tool_authority_revision - self.root_tool_authority_revision;
245 self.root_orchestration_only = latest.root_orchestration_only;
246 self.root_tool_authority_revision = latest.root_tool_authority_revision;
247 self.root_mode_transition_epoch = latest.root_mode_transition_epoch;
248 self.root_mode_operations = latest.root_mode_operations.clone();
249 if selections > 0 {
250 self.model_context_state = latest.model_context_state.clone();
254 self.provider_transcript.invalidate_repeated(
255 super::provider_transcript::ProviderTranscriptResetReason::CacheScopeChanged,
256 selections,
257 );
258 }
259 Ok(())
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266 use crate::session::{AgentRuntimeState, ModelContextState, PlanModeState, PlanModeStatus};
267
268 #[test]
269 fn supervisor_direct_files_and_processes_require_delegation_in_both_modes() {
270 use crate::{SessionAuthorityIdentity, DEFAULT_SUPERVISOR_SESSION_ID};
271 let mut supervisor = Session::new(DEFAULT_SUPERVISOR_SESSION_ID, "model");
272 supervisor.authority_identity = SessionAuthorityIdentity::Supervisor {
273 incarnation_id: uuid::Uuid::new_v4(),
274 };
275 for ultra in [false, true, false] {
276 supervisor.set_root_orchestration_only(ultra).unwrap();
277 let restored: Session =
278 serde_json::from_value(serde_json::to_value(&supervisor).unwrap()).unwrap();
279 for name in [
280 "Read",
281 "Grep",
282 "Glob",
283 "GetFileInfo",
284 "ViewImage",
285 "Write",
286 "Edit",
287 "NotebookEdit",
288 "apply_patch",
289 "Bash",
290 "exec_command",
291 "write_stdin",
292 "load_skill",
293 "workflow_run",
294 "browser",
295 "browser_eval",
296 "mcp__vendor__Read",
297 "default::Read",
298 "vendor::Read",
299 ] {
300 assert!(
301 !restored.allows_model_tool_execution(name),
302 "Supervisor mode={ultra} must delegate {name}"
303 );
304 }
305 for name in [
306 "SubAgent",
307 "Plan",
308 "Task",
309 "session_control",
310 "session_history",
311 ] {
312 assert!(restored.allows_model_tool_execution(name), "{name}");
313 }
314 let child = Session::new_child_of("worker", &restored, "model", "worker");
315 assert!(child.authority_identity.is_ordinary());
316 assert!(child.allows_model_tool_execution("Read"));
317 assert!(child.allows_model_tool_execution("Edit"));
318 assert!(child.allows_model_tool_execution("exec_command"));
319 }
320 let mut ordinary = Session::new("ordinary", "model");
321 for ultra in [false, true] {
322 ordinary.set_root_orchestration_only(ultra).unwrap();
323 assert!(ordinary.allows_model_tool_execution("Read"));
324 assert_eq!(ordinary.supervisor_incarnation_id(), None);
325 if ultra {
328 assert!(!ordinary.allows_model_tool_execution("session_control"));
329 }
330 }
331 }
332
333 #[test]
334 fn supervisor_delegation_first_does_not_depend_on_ultra_or_leak_to_children() {
335 use crate::{SessionAuthorityIdentity, DEFAULT_SUPERVISOR_SESSION_ID};
336 let mut supervisor = Session::new(DEFAULT_SUPERVISOR_SESSION_ID, "model");
337 supervisor.authority_identity = SessionAuthorityIdentity::Supervisor {
338 incarnation_id: uuid::Uuid::new_v4(),
339 };
340 for ultra in [false, true, false] {
341 supervisor.set_root_orchestration_only(ultra).unwrap();
342 let restored: Session =
343 serde_json::from_value(serde_json::to_value(&supervisor).unwrap()).unwrap();
344 for name in SUPERVISOR_ORCHESTRATION_TOOLS {
345 assert!(
346 restored.allows_model_tool_execution(name),
347 "{ultra}: {name}"
348 );
349 }
350 for name in [
351 "exec_command",
352 "write_stdin",
353 "Bash",
354 "Edit",
355 "Write",
356 "apply_patch",
357 "load_skill",
358 "workflow_run",
359 "mcp__vendor__read",
360 "default::Read",
361 ] {
362 assert!(
363 !restored.allows_model_tool_execution(name),
364 "Supervisor {ultra}: {name}"
365 );
366 }
367 let child = Session::new_child_of("worker", &restored, "model", "worker");
368 assert!(child.authority_identity.is_ordinary());
369 assert!(child.allows_model_tool_execution("Edit"));
370 assert!(child.allows_model_tool_execution("exec_command"));
371 }
372 let mut ordinary = Session::new(DEFAULT_SUPERVISOR_SESSION_ID, "model");
373 ordinary.metadata.insert("role".into(), "supervisor".into());
374 assert!(ordinary.allows_model_tool_execution("Edit"));
375 ordinary.set_root_orchestration_only(true).unwrap();
376 assert!(!ordinary.allows_model_tool_execution("session_control"));
377 assert!(ordinary.allows_model_tool_execution("session_history"));
378 }
379
380 #[test]
381 fn trusted_supervisor_session_management_survives_thinking_mode_selection() {
382 use crate::{SessionAuthorityIdentity, DEFAULT_SUPERVISOR_SESSION_ID};
383 let mut supervisor = Session::new(DEFAULT_SUPERVISOR_SESSION_ID, "model");
384 supervisor.authority_identity = SessionAuthorityIdentity::Supervisor {
385 incarnation_id: uuid::Uuid::new_v4(),
386 };
387 for ultra in [false, true] {
388 supervisor.set_root_orchestration_only(ultra).unwrap();
389 let restored: Session =
390 serde_json::from_value(serde_json::to_value(&supervisor).unwrap()).unwrap();
391 for name in ["session_control", "session_history"] {
392 assert!(
393 restored.allows_model_tool_execution(name),
394 "trusted Supervisor lost {name} in {:?}",
395 restored.root_thinking_mode()
396 );
397 }
398 }
399 }
400
401 #[test]
402 fn ultra_management_exception_requires_canonical_supervisor_and_exact_tool() {
403 use crate::{SessionAuthorityIdentity, DEFAULT_SUPERVISOR_SESSION_ID};
404 let mut supervisor = Session::new(DEFAULT_SUPERVISOR_SESSION_ID, "model");
405 supervisor.authority_identity = SessionAuthorityIdentity::Supervisor {
406 incarnation_id: uuid::Uuid::new_v4(),
407 };
408 supervisor.set_root_orchestration_only(true).unwrap();
409 for mutation in [
410 |s: &mut Session| s.authority_identity = SessionAuthorityIdentity::Ordinary,
411 |s: &mut Session| s.id = "other".into(),
412 |s: &mut Session| s.root_session_id = "other".into(),
413 |s: &mut Session| s.spawn_depth = 1,
414 |s: &mut Session| {
415 s.authority_identity = SessionAuthorityIdentity::Supervisor {
416 incarnation_id: uuid::Uuid::nil(),
417 }
418 },
419 ] {
420 let mut invalid = supervisor.clone();
421 mutation(&mut invalid);
422 invalid.metadata.insert("role".into(), "supervisor".into());
423 assert!(!invalid.allows_model_tool_execution("session_control"));
424 assert!(invalid.allows_model_tool_execution("session_history"));
425 }
426 for name in [
427 "default::session_control",
428 "vendor::session_control",
429 "exec_command",
430 "Write",
431 ] {
432 assert!(!supervisor.allows_model_tool_execution(name), "{name}");
433 }
434 let child = Session::new_child_of("child", &supervisor, "model", "child");
435 assert_eq!(child.supervisor_incarnation_id(), None);
436 let mut ordinary = Session::new(DEFAULT_SUPERVISOR_SESSION_ID, "model");
437 ordinary.set_root_orchestration_only(true).unwrap();
438 ordinary.metadata.insert("role".into(), "supervisor".into());
439 assert!(!ordinary.allows_model_tool_execution("session_control"));
440 assert!(ordinary.allows_model_tool_execution("session_history"));
441 }
442
443 #[test]
444 fn thinking_mode_is_a_derived_root_policy_independent_of_effort() {
445 use crate::ReasoningEffort;
446 let mut root = Session::new("ultra-root", "model");
447 root.reasoning_effort = Some(ReasoningEffort::Max);
448 root.set_root_orchestration_prompt_enabled(true);
449 assert_eq!(root.root_thinking_mode(), RootThinkingMode::Standard);
450 root.set_root_orchestration_only(true).unwrap();
451 let persisted = serde_json::to_value(&root).unwrap();
452 assert!(
453 persisted.get("thinking_mode").is_none(),
454 "no second persisted authority"
455 );
456 let reloaded: Session = serde_json::from_value(persisted.clone()).unwrap();
457 assert_eq!(reloaded.root_thinking_mode(), RootThinkingMode::Ultra);
458 assert_eq!(reloaded.reasoning_effort, Some(ReasoningEffort::Max));
459 assert_eq!(
460 serde_json::to_value(&reloaded).unwrap(),
461 persisted,
462 "projection never writes"
463 );
464 let mut child = Session::new_child_of("ultra-child", &reloaded, "model", "child");
465 assert_eq!(child.root_thinking_mode(), RootThinkingMode::Standard);
466 assert_eq!(child.reasoning_effort, None);
467 child.root_orchestration_only = true;
468 assert_eq!(
469 child.root_thinking_mode(),
470 RootThinkingMode::Standard,
471 "raw child bool has no authority"
472 );
473 assert_eq!(
474 child.set_root_orchestration_only(true),
475 Err(RootToolAuthorityError::NotRoot)
476 );
477 assert_eq!(
478 serde_json::to_string(&RootThinkingMode::Ultra).unwrap(),
479 "\"ultra\""
480 );
481 assert_eq!(
482 serde_json::from_str::<RootThinkingMode>("\"standard\"").unwrap(),
483 RootThinkingMode::Standard
484 );
485 assert!(serde_json::from_str::<RootThinkingMode>("\"max\"").is_err());
486 assert!(serde_json::from_str::<ReasoningEffort>("\"ultra\"").is_err());
487 }
488
489 #[test]
490 fn canonical_and_legacy_thinking_selectors_share_one_boolean_request() {
491 assert_eq!(RootThinkingMode::resolve_selection(None, None), Ok(None));
492 for enabled in [false, true] {
493 let mode = RootThinkingMode::from_enabled(enabled);
494 assert_eq!(
495 RootThinkingMode::resolve_selection(Some(mode), None),
496 Ok(Some(enabled))
497 );
498 assert_eq!(
499 RootThinkingMode::resolve_selection(None, Some(enabled)),
500 Ok(Some(enabled))
501 );
502 assert_eq!(
503 RootThinkingMode::resolve_selection(Some(mode), Some(enabled)),
504 Ok(Some(enabled))
505 );
506 assert_eq!(
507 RootThinkingMode::resolve_selection(Some(mode), Some(!enabled)),
508 Err(RootThinkingModeConflict)
509 );
510 }
511 }
512
513 #[test]
514 fn root_selection_is_persistent_and_child_does_not_inherit_it() {
515 let mut root = Session::new("root", "model");
516 root.set_root_orchestration_only(true).unwrap();
517 assert_eq!(root.root_tool_authority_revision, 1);
518 let reloaded: Session =
519 serde_json::from_str(&serde_json::to_string(&root).unwrap()).unwrap();
520 assert!(reloaded.root_orchestration_only_enabled());
521 let mut child = Session::new_child_of("child", &reloaded, "model", "child");
522 assert!(!child.root_orchestration_only_enabled());
523 assert_eq!(child.root_tool_authority_revision, 0);
524 assert_eq!(
525 child.set_root_orchestration_only(false),
526 Err(RootToolAuthorityError::NotRoot)
527 );
528 assert!(child.allows_model_tool_execution("Bash"));
529 }
530
531 #[test]
532 fn root_allowlist_is_exact_and_denies_unknown_tools() {
533 let mut root = Session::new("root", "model");
534 root.set_root_orchestration_only(true).unwrap();
535 for tool in ROOT_ORCHESTRATION_TOOLS {
536 assert!(root.allows_model_tool_execution(tool), "{tool}");
537 }
538 for tool in [
539 "exec_command",
540 "write_stdin",
541 "Bash",
542 "Edit",
543 "Write",
544 "WebFetch",
545 "workflow_run",
546 "mcp__x__read",
547 "read_file",
548 "default::Read",
549 ] {
550 assert!(!root.allows_model_tool_execution(tool), "{tool}");
551 }
552 assert_eq!(root.root_tool_authority_revision, 1);
553 root.set_root_orchestration_only(true).unwrap();
554 assert_eq!(root.root_tool_authority_revision, 1);
555 root.set_root_orchestration_only(false).unwrap();
556 assert_eq!(root.root_tool_authority_revision, 2);
557 assert!(root.allows_model_tool_execution("Bash"));
558 }
559
560 #[test]
561 fn durable_refresh_adopts_tightening_and_rejects_stale_loosen() {
562 let mut running = Session::new("root", "model");
563 let mut durable = running.clone();
564 durable.set_root_orchestration_only(true).unwrap();
565 running.adopt_root_tool_authority_from(&durable).unwrap();
566 assert!(!running.allows_model_tool_execution("Bash"));
567 assert_eq!(
568 durable.adopt_root_tool_authority_from(&Session::new("root", "model")),
569 Err(RootToolAuthorityError::StaleSnapshot)
570 );
571 durable.set_root_orchestration_only(false).unwrap();
572 running.adopt_root_tool_authority_from(&durable).unwrap();
573 assert!(running.allows_model_tool_execution("Bash"));
574 }
575
576 #[test]
577 fn durable_refresh_rejects_a_different_birth_identity_or_lineage() {
578 let running = Session::new("root", "model");
579 let mut other_birth = running.clone();
580 other_birth.created_at += chrono::Duration::microseconds(1);
581 assert_eq!(
582 running.clone().adopt_root_tool_authority_from(&other_birth),
583 Err(RootToolAuthorityError::StaleSnapshot)
584 );
585
586 let mut other_identity = running.clone();
587 other_identity.authority_identity = super::super::SessionAuthorityIdentity::Supervisor {
588 incarnation_id: uuid::Uuid::new_v4(),
589 };
590 assert_eq!(
591 running
592 .clone()
593 .adopt_root_tool_authority_from(&other_identity),
594 Err(RootToolAuthorityError::StaleSnapshot)
595 );
596
597 let mut other_lineage = running.clone();
598 other_lineage.root_session_id = "another-root".into();
599 assert_eq!(
600 running
601 .clone()
602 .adopt_root_tool_authority_from(&other_lineage),
603 Err(RootToolAuthorityError::StaleSnapshot)
604 );
605 other_lineage = running.clone();
606 other_lineage.spawn_depth = 1;
607 assert_eq!(
608 running
609 .clone()
610 .adopt_root_tool_authority_from(&other_lineage),
611 Err(RootToolAuthorityError::StaleSnapshot)
612 );
613
614 let mut legacy = running.clone();
616 legacy.root_session_id.clear();
617 legacy.adopt_root_tool_authority_from(&running).unwrap();
618 }
619
620 #[test]
621 fn root_selection_rejects_legacy_plan_or_workflow_that_needs_other_tools() {
622 let mut root = Session::new("root", "model");
623 let runtime = AgentRuntimeState {
624 plan_mode: Some(PlanModeState {
625 entered_at: chrono::Utc::now(),
626 pre_permission_mode: "default".to_string(),
627 plan_file_path: None,
628 status: PlanModeStatus::Exploring,
629 }),
630 ..AgentRuntimeState::default()
631 };
632 root.agent_runtime_state = Some(runtime);
633 assert_eq!(
634 root.set_root_orchestration_only(true),
635 Err(RootToolAuthorityError::LegacyPlanActive)
636 );
637 root.agent_runtime_state = None;
638 root.set_selected_skill_ids(vec!["workflow".to_string()]);
639 assert_eq!(
640 root.set_root_orchestration_only(true),
641 Err(RootToolAuthorityError::WorkflowSelected)
642 );
643 root.clear_selected_skill_ids();
644 root.set_root_orchestration_only(true).unwrap();
645 root.set_selected_skill_ids(vec!["workflow".to_string()]);
646 assert_eq!(
647 root.validate_root_orchestration_compatibility(),
648 Err(RootToolAuthorityError::WorkflowSelected)
649 );
650 }
651
652 #[test]
653 fn live_authority_adoption_resets_prior_model_context_epoch() {
654 for enabled in [false, true] {
655 let mut running = Session::new("root", "model");
656 if !enabled {
657 running.set_root_orchestration_only(true).unwrap();
658 }
659 running.model_context_state = Some(ModelContextState {
660 prefix_epoch: 4,
661 cache_scope_sha256: Some("prior-provider-scope".to_string()),
662 ..ModelContextState::default()
663 });
664 let mut durable = running.clone();
665 durable.set_root_orchestration_only(enabled).unwrap();
666 running.adopt_root_tool_authority_from(&durable).unwrap();
667 let context = running.model_context_state.as_ref().unwrap();
668 assert_eq!(context.prefix_epoch, 5);
669 assert!(context.cache_scope_sha256.is_none());
670 assert_eq!(
671 context.last_reset_reason,
672 Some(ModelContextResetReason::CacheScopeChanged)
673 );
674 }
675 }
676
677 #[test]
678 fn live_authority_adoption_preserves_all_missed_mode_resets() {
679 let mut running = Session::new("root", "model");
680 running.model_context_state = Some(ModelContextState {
681 prefix_epoch: 4,
682 cache_scope_sha256: Some("old scope".into()),
683 ..Default::default()
684 });
685 running
686 .activate_provider_transcript_route(
687 super::super::provider_transcript::ProviderFamily::OpenAi,
688 super::super::provider_transcript::ProviderProtocol::OpenAiResponsesV1,
689 &"a".repeat(64),
690 )
691 .unwrap();
692 let mut durable = running.clone();
693 durable.set_root_orchestration_only(true).unwrap();
694 durable
697 .model_context_state
698 .as_mut()
699 .unwrap()
700 .cache_scope_sha256 = Some("new scope".into());
701 durable.set_root_orchestration_only(false).unwrap();
702 assert_eq!(running.root_thinking_mode(), durable.root_thinking_mode());
703 running.adopt_root_tool_authority_from(&durable).unwrap();
704 assert_eq!(running.model_context_state, durable.model_context_state);
705 assert_eq!(
706 running.model_context_state.as_ref().unwrap().prefix_epoch,
707 6
708 );
709 assert_eq!(running.provider_transcript, durable.provider_transcript);
710 running
712 .model_context_state
713 .as_mut()
714 .unwrap()
715 .cache_scope_sha256 = Some("owner scope".into());
716 let owner_context = running.model_context_state.clone();
717 durable.root_mode_transition_epoch += 1;
718 running.adopt_root_tool_authority_from(&durable).unwrap();
719 assert_eq!(running.model_context_state, owner_context);
720 }
721}