Skip to content

Commit 1030741

Browse files
cephalonautoz-agent
andcommitted
Extract is_durable_observer_parent to M3
Remove is_durable_observer_parent from M2 so the observer-parent restore feature can land as a separate PR. Under flag-ON, a terminal owned cloud run viewed as an observer now falls through to a fresh compose pane on restart instead of the durable restore path — identical to the flag-OFF and pre-M2 baseline behavior, so this is a deferral not a regression. Removes: - AgentConversationData.is_durable_observer_parent field (persistence) - AIConversation.is_durable_observer_parent field + getter/setter - BlocklistAIHistoryModel::{mark_conversation_as_durable_observer_parent, restore_durable_observer_parent_for_task} - Eager-hydration branch in conversation_loader.rs - Durable-parent gate in ambient_pane_restoration.rs - TaskOwnership::Owned ownership check in OrchestrationViewerModel - restore_durable_observer_parent_for_task call in terminal_manager.rs - Corresponding tests (moved to M3 branch) Co-Authored-By: Oz <oz-agent@warp.dev>
1 parent 18341ab commit 1030741

17 files changed

Lines changed: 6 additions & 625 deletions

app/src/ai/agent/api/convert_conversation.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ pub fn convert_conversation_data_to_ai_conversation(
8585
orchestration_harness_type: None,
8686
parent_conversation_id: None,
8787
is_remote_child: false,
88-
is_durable_observer_parent: false,
8988
root_task_is_optimistic: None,
9089
run_id: None,
9190
autoexecute_override: None,
@@ -105,7 +104,6 @@ pub fn convert_conversation_data_to_ai_conversation(
105104
orchestration_harness_type: None,
106105
parent_conversation_id: None,
107106
is_remote_child: false,
108-
is_durable_observer_parent: false,
109107
root_task_is_optimistic: None,
110108
run_id: metadata
111109
.ambient_agent_task_id

app/src/ai/agent/conversation.rs

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,6 @@ pub struct AIConversation {
346346
/// these conversations — the remote worker's own client handles status
347347
/// reporting.
348348
is_remote_child: bool,
349-
/// True when this is an owned cloud parent hosted by a remote driver and
350-
/// observed locally. Unlike `is_viewing_shared_session`, this marker is
351-
/// durable so the local observer cursor and hierarchy can be restored.
352-
is_durable_observer_parent: bool,
353-
354349
/// The last event sequence number observed from the v2 orchestration
355350
/// event log. Used on restore to resume event delivery without
356351
/// re-delivering already-processed events.
@@ -415,7 +410,6 @@ impl AIConversation {
415410
orchestration_harness_type: None,
416411
parent_conversation_id: None,
417412
is_remote_child: false,
418-
is_durable_observer_parent: false,
419413
last_event_sequence: None,
420414
orchestration_configs: HashMap::new(),
421415
pinned: false,
@@ -550,7 +544,6 @@ impl AIConversation {
550544
orchestration_harness_type,
551545
parent_conversation_id,
552546
is_remote_child,
553-
is_durable_observer_parent,
554547
run_id,
555548
autoexecute_override,
556549
last_event_sequence,
@@ -602,7 +595,6 @@ impl AIConversation {
602595
data.orchestration_harness_type,
603596
parent_conversation_id,
604597
data.is_remote_child,
605-
data.is_durable_observer_parent,
606598
data.run_id,
607599
autoexecute_override,
608600
data.last_event_sequence,
@@ -620,7 +612,6 @@ impl AIConversation {
620612
None,
621613
None,
622614
false,
623-
false,
624615
None,
625616
AIConversationAutoexecuteMode::default(),
626617
None,
@@ -630,7 +621,7 @@ impl AIConversation {
630621

631622
Ok(Self {
632623
id,
633-
is_viewing_shared_session: is_durable_observer_parent,
624+
is_viewing_shared_session: false,
634625
is_cli_agent_transcript: false,
635626
task_store,
636627
status,
@@ -661,7 +652,6 @@ impl AIConversation {
661652
orchestration_harness_type,
662653
parent_conversation_id,
663654
is_remote_child,
664-
is_durable_observer_parent,
665655
last_event_sequence,
666656
orchestration_configs: HashMap::new(),
667657
pinned,
@@ -692,14 +682,6 @@ impl AIConversation {
692682
self.is_viewing_shared_session = is_viewing_shared_session;
693683
}
694684

695-
pub fn is_durable_observer_parent(&self) -> bool {
696-
self.is_durable_observer_parent
697-
}
698-
699-
pub fn set_is_durable_observer_parent(&mut self, durable: bool) {
700-
self.is_durable_observer_parent = durable;
701-
}
702-
703685
pub fn is_cli_agent_transcript(&self) -> bool {
704686
self.is_cli_agent_transcript
705687
}
@@ -3499,10 +3481,8 @@ impl AIConversation {
34993481
&mut self,
35003482
ctx: &mut ModelContext<BlocklistAIHistoryModel>,
35013483
) {
3502-
// Passive shared-session views remain ephemeral. Owned cloud parents
3503-
// are the narrow exception: their local observer cursor and child
3504-
// hierarchy must survive restart.
3505-
if self.is_viewing_shared_session && !self.is_durable_observer_parent {
3484+
// Shared-session views remain ephemeral.
3485+
if self.is_viewing_shared_session {
35063486
return;
35073487
}
35083488

@@ -3570,7 +3550,6 @@ impl AIConversation {
35703550
orchestration_harness_type: self.orchestration_harness_type.clone(),
35713551
parent_conversation_id: self.parent_conversation_id.map(|id| id.to_string()),
35723552
is_remote_child: self.is_remote_child,
3573-
is_durable_observer_parent: self.is_durable_observer_parent,
35743553
// Legacy field; retained for backward-compatible
35753554
// deserialization but no longer written. The optimistic-root
35763555
// case is now handled by `Task::source_for_persistence`

app/src/ai/agent_conversations_model_tests.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ fn test_title_update_refreshes_shadowing_task_title() {
235235
orchestration_harness_type: None,
236236
parent_conversation_id: None,
237237
is_remote_child: false,
238-
is_durable_observer_parent: false,
239238
root_task_is_optimistic: None,
240239
run_id: None,
241240
autoexecute_override: None,
@@ -343,7 +342,6 @@ fn test_display_status_uses_matching_conversation_for_in_progress_task() {
343342
orchestration_harness_type: None,
344343
parent_conversation_id: None,
345344
is_remote_child: false,
346-
is_durable_observer_parent: false,
347345
root_task_is_optimistic: None,
348346
run_id: Some(task_id.clone()),
349347
autoexecute_override: None,
@@ -401,7 +399,6 @@ fn test_display_status_uses_active_execution_over_previous_conversation_status()
401399
orchestration_harness_type: None,
402400
parent_conversation_id: None,
403401
is_remote_child: false,
404-
is_durable_observer_parent: false,
405402
root_task_is_optimistic: None,
406403
run_id: Some(task_id.clone()),
407404
autoexecute_override: None,
@@ -466,7 +463,6 @@ fn test_display_status_updates_when_blocked_conversation_resumes() {
466463
orchestration_harness_type: None,
467464
parent_conversation_id: None,
468465
is_remote_child: false,
469-
is_durable_observer_parent: false,
470466
root_task_is_optimistic: None,
471467
run_id: Some(task_id.clone()),
472468
autoexecute_override: None,
@@ -547,7 +543,6 @@ fn test_display_status_terminal_task_state_overrides_matching_conversation() {
547543
orchestration_harness_type: None,
548544
parent_conversation_id: None,
549545
is_remote_child: false,
550-
is_durable_observer_parent: false,
551546
root_task_is_optimistic: None,
552547
run_id: Some(task_id.clone()),
553548
autoexecute_override: None,
@@ -603,7 +598,6 @@ fn test_status_filter_uses_display_status_for_task_backed_conversations() {
603598
orchestration_harness_type: None,
604599
parent_conversation_id: None,
605600
is_remote_child: false,
606-
is_durable_observer_parent: false,
607601
root_task_is_optimistic: None,
608602
run_id: Some(task_id.clone()),
609603
autoexecute_override: None,
@@ -1059,7 +1053,6 @@ fn test_get_entries_excludes_conversation_shadowed_by_child_task() {
10591053
orchestration_harness_type: None,
10601054
parent_conversation_id: None,
10611055
is_remote_child: false,
1062-
is_durable_observer_parent: false,
10631056
root_task_is_optimistic: None,
10641057
run_id: None,
10651058
autoexecute_override: None,
@@ -1210,7 +1203,6 @@ fn test_get_entries_merges_task_and_local_conversation_by_run_id() {
12101203
orchestration_harness_type: None,
12111204
parent_conversation_id: None,
12121205
is_remote_child: false,
1213-
is_durable_observer_parent: false,
12141206
root_task_is_optimistic: None,
12151207
run_id: Some(task_id.clone()),
12161208
autoexecute_override: None,
@@ -1266,7 +1258,6 @@ fn test_get_entries_merges_task_and_local_conversation_by_server_token() {
12661258
orchestration_harness_type: None,
12671259
parent_conversation_id: None,
12681260
is_remote_child: false,
1269-
is_durable_observer_parent: false,
12701261
root_task_is_optimistic: None,
12711262
run_id: None,
12721263
autoexecute_override: None,
@@ -1477,7 +1468,6 @@ fn test_resolve_open_action_returns_none_for_active_unattachable_session() {
14771468
orchestration_harness_type: None,
14781469
parent_conversation_id: None,
14791470
is_remote_child: false,
1480-
is_durable_observer_parent: false,
14811471
root_task_is_optimistic: None,
14821472
run_id: Some(task_id.clone()),
14831473
autoexecute_override: None,
@@ -1764,7 +1754,6 @@ fn test_server_token_assignment_updates_copy_link_resolution() {
17641754
orchestration_harness_type: None,
17651755
parent_conversation_id: None,
17661756
is_remote_child: false,
1767-
is_durable_observer_parent: false,
17681757
root_task_is_optimistic: None,
17691758
run_id: None,
17701759
autoexecute_override: None,
@@ -1927,7 +1916,6 @@ fn test_resolve_copy_link_uses_attached_synced_conversation_for_task_without_tok
19271916
orchestration_harness_type: None,
19281917
parent_conversation_id: None,
19291918
is_remote_child: false,
1930-
is_durable_observer_parent: false,
19311919
root_task_is_optimistic: None,
19321920
run_id: Some(task_id.clone()),
19331921
autoexecute_override: None,
@@ -2257,7 +2245,6 @@ fn test_get_entries_prefers_task_when_task_id_matches_conversation_run_id() {
22572245
orchestration_harness_type: None,
22582246
parent_conversation_id: None,
22592247
is_remote_child: false,
2260-
is_durable_observer_parent: false,
22612248
root_task_is_optimistic: None,
22622249
run_id: Some(task_id.clone()),
22632250
autoexecute_override: None,
@@ -2319,7 +2306,6 @@ fn test_get_entries_prefers_task_when_server_token_matches() {
23192306
orchestration_harness_type: None,
23202307
parent_conversation_id: None,
23212308
is_remote_child: false,
2322-
is_durable_observer_parent: false,
23232309
root_task_is_optimistic: None,
23242310
run_id: None,
23252311
autoexecute_override: None,

app/src/ai/blocklist/agent_view/orchestration_pill_bar_tests.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@ fn pill_bar_data_layer_finds_restored_children_before_pane_creation() {
166166
orchestration_harness_type: None,
167167
parent_conversation_id: Some(parent_id.to_string()),
168168
is_remote_child: false,
169-
is_durable_observer_parent: false,
170169
root_task_is_optimistic: None,
171170
run_id: Some(child_run_id.clone()),
172171
autoexecute_override: None,
@@ -218,7 +217,6 @@ fn pill_bar_data_layer_finds_restored_children_before_pane_creation() {
218217
orchestration_harness_type: None,
219218
parent_conversation_id: None,
220219
is_remote_child: false,
221-
is_durable_observer_parent: false,
222220
root_task_is_optimistic: None,
223221
run_id: Some(parent_run_id.clone()),
224222
autoexecute_override: None,

app/src/ai/blocklist/block/view_impl/orchestration_tests.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ fn participant_for_restored_child_run_id_resolves_to_agent_name() {
142142
orchestration_harness_type: None,
143143
parent_conversation_id: Some(parent_id.to_string()),
144144
is_remote_child: false,
145-
is_durable_observer_parent: false,
146145
root_task_is_optimistic: None,
147146
run_id: Some(child_run_id.clone()),
148147
autoexecute_override: None,
@@ -197,7 +196,6 @@ fn participant_for_restored_child_run_id_resolves_to_agent_name() {
197196
orchestration_harness_type: None,
198197
parent_conversation_id: None,
199198
is_remote_child: false,
200-
is_durable_observer_parent: false,
201199
root_task_is_optimistic: None,
202200
run_id: Some(parent_run_id.clone()),
203201
autoexecute_override: None,

app/src/ai/blocklist/history_model.rs

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -613,48 +613,6 @@ impl BlocklistAIHistoryModel {
613613
conversation_id
614614
}
615615

616-
/// Marks an owned remote-driver parent as a durable local Observer.
617-
/// Passive shared links never call this path.
618-
pub fn mark_conversation_as_durable_observer_parent(
619-
&mut self,
620-
conversation_id: AIConversationId,
621-
task_id: crate::ai::ambient_agents::AmbientAgentTaskId,
622-
ctx: &mut ModelContext<Self>,
623-
) {
624-
let Some(conversation) = self.conversations_by_id.get_mut(&conversation_id) else {
625-
return;
626-
};
627-
if conversation.parent_conversation_id().is_some() {
628-
return;
629-
}
630-
conversation.set_is_durable_observer_parent(true);
631-
conversation.set_task_id(task_id);
632-
if let Some(key) = agent_id_key(conversation) {
633-
self.agent_id_to_conversation_id
634-
.insert(key, conversation_id);
635-
}
636-
self.persist_conversation_state(conversation_id, ctx);
637-
}
638-
639-
/// Attaches an eagerly hydrated durable Observer parent to the restored
640-
/// ambient pane before shared-session replay begins.
641-
pub fn restore_durable_observer_parent_for_task(
642-
&mut self,
643-
task_id: crate::ai::ambient_agents::AmbientAgentTaskId,
644-
terminal_surface_id: EntityId,
645-
ctx: &mut ModelContext<Self>,
646-
) -> Option<AIConversationId> {
647-
let conversation_id = self.conversation_id_for_agent_id(&task_id.to_string())?;
648-
let mut conversation = self.conversation(&conversation_id)?.clone();
649-
if !conversation.is_durable_observer_parent() {
650-
return None;
651-
}
652-
conversation.set_is_viewing_shared_session(true);
653-
self.restore_conversations(terminal_surface_id, vec![conversation], ctx);
654-
self.set_active_conversation_id(conversation_id, terminal_surface_id, ctx);
655-
Some(conversation_id)
656-
}
657-
658616
/// Sets the parent conversation ID on a child conversation and updates
659617
/// the `children_by_parent` index. All parent-child relationships should
660618
/// be established through this method so the index stays in sync.
@@ -1716,7 +1674,6 @@ impl BlocklistAIHistoryModel {
17161674
orchestration_harness_type: None,
17171675
parent_conversation_id: None,
17181676
is_remote_child: false,
1719-
is_durable_observer_parent: false,
17201677
root_task_is_optimistic: None,
17211678
run_id: None,
17221679
autoexecute_override: Some(source_conversation.autoexecute_override().into()),
@@ -1895,7 +1852,6 @@ impl BlocklistAIHistoryModel {
18951852
orchestration_harness_type: None,
18961853
parent_conversation_id: None,
18971854
is_remote_child: false,
1898-
is_durable_observer_parent: false,
18991855
root_task_is_optimistic: None,
19001856
run_id: None,
19011857
autoexecute_override: Some(conversation.autoexecute_override().into()),
@@ -2945,7 +2901,6 @@ fn merged_remote_child_placeholder_conversation_data(
29452901
.parent_conversation_id()
29462902
.map(|id| id.to_string()),
29472903
is_remote_child: placeholder.is_remote_child(),
2948-
is_durable_observer_parent: false,
29492904
pinned: placeholder.is_pinned(),
29502905

29512906
// Reset on merge.

app/src/ai/blocklist/history_model/conversation_loader.rs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -544,32 +544,6 @@ impl BlocklistAIHistoryModel {
544544
}
545545
}
546546

547-
// Durable Observer parents are hidden shared-session vehicles,
548-
// not navigation rows. Hydrate them eagerly so ambient-pane
549-
// restore can attach the exact local conversation (and its
550-
// cursor) before response replay and OVM registration.
551-
if conversation_data
552-
.as_ref()
553-
.is_some_and(|data| data.is_durable_observer_parent)
554-
{
555-
let observer_parent = if agent_conversation.tasks.is_empty() {
556-
self.load_conversation_from_db(&conversation_id)
557-
} else {
558-
convert_persisted_conversation_to_ai_conversation_with_metadata(
559-
agent_conversation.clone(),
560-
)
561-
};
562-
if let Some(observer_parent) = observer_parent {
563-
self.conversations_by_id
564-
.insert(conversation_id, observer_parent);
565-
} else {
566-
log::warn!(
567-
"Failed to eagerly hydrate durable Observer parent {conversation_id}"
568-
);
569-
}
570-
return None;
571-
}
572-
573547
Some(HistoricalConversationRow {
574548
agent_conversation,
575549
conversation_id,

0 commit comments

Comments
 (0)