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