Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ serde_json = { version = "1.0", features = ["raw_value"] }
serde_urlencoded = "0.7"
serde_with = "2.0.1"
serde_yaml = "0.8"
session-sharing-protocol = { git = "https://github.com/warpdotdev/session-sharing-protocol.git", rev = "3a12b871dfd1019a66057e4d9b7d5c812b73ee8c" }
session-sharing-protocol = { git = "https://github.com/warpdotdev/session-sharing-protocol.git", rev = "5a0ad6135809feee9da2e9efae8bd6b54b89172e" }
similar = { version = "2.7", features = ["inline"] }
simplelog = "0.12.2"
smallvec = "1.6.1"
Expand Down
3 changes: 2 additions & 1 deletion app/src/ai/agent_conversations_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1477,7 +1477,8 @@ impl AgentConversationsModel {
| BlocklistAIHistoryEvent::ConversationOwnershipTransferred { .. }
| BlocklistAIHistoryEvent::NewConversationRequestComplete { .. }
| BlocklistAIHistoryEvent::OrchestrationConfigUpdated { .. }
| BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { .. } => {}
| BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { .. }
| BlocklistAIHistoryEvent::LocalSharedSessionEstablished { .. } => {}

BlocklistAIHistoryEvent::ConversationServerTokenAssigned { .. } => {
ctx.emit(AgentConversationsModelEvent::ConversationUpdated {
Expand Down
3 changes: 2 additions & 1 deletion app/src/ai/agent_sdk/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2591,7 +2591,8 @@ impl AgentDriver {
| BlocklistAIHistoryEvent::ConversationOwnershipTransferred { .. }
| BlocklistAIHistoryEvent::NewConversationRequestComplete { .. }
| BlocklistAIHistoryEvent::OrchestrationConfigUpdated { .. }
| BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { .. } => (),
| BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { .. }
| BlocklistAIHistoryEvent::LocalSharedSessionEstablished { .. } => (),
}
});

Expand Down
7 changes: 2 additions & 5 deletions app/src/ai/agent_sdk/driver/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use std::time::Duration;

use futures::channel::oneshot;
use session_sharing_protocol::common::{Role, SessionId};
use session_sharing_protocol::sharer::SessionSourceType;
use warp_cli::share::{ShareAccessLevel, ShareRequest, ShareSubject};
use warp_completer::completer::CommandOutput;
use warp_core::command::ExitCode;
Expand All @@ -31,7 +30,7 @@ use crate::terminal::model::grid::RespectDisplayedOutput;
use crate::terminal::model::index::Point;
use crate::terminal::model::session::ExecuteCommandOptions;
use crate::terminal::model::RespectObfuscatedSecrets;
use crate::terminal::shared_session::{self, IsSharedSessionCreator};
use crate::terminal::shared_session::{self, IsSharedSessionCreator, SharedSessionSource};
use crate::terminal::shell::ShellType;
use crate::terminal::view::ConversationRestorationInNewPaneType;
use crate::terminal::TerminalView;
Expand Down Expand Up @@ -120,9 +119,7 @@ fn create_terminal_view(
) -> Result<ViewHandle<TerminalView>, AgentDriverError> {
let is_shared_session_creator = if options.should_share {
IsSharedSessionCreator::Yes {
source_type: SessionSourceType::AmbientAgent {
task_id: options.task_id.map(|t| t.to_string()),
},
source: SharedSessionSource::ambient_agent(options.task_id.map(|t| t.to_string())),
}
} else {
IsSharedSessionCreator::No
Expand Down
3 changes: 2 additions & 1 deletion app/src/ai/blocklist/action_model/execute/start_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ impl StartAgentExecutor {
| BlocklistAIHistoryEvent::UpdatedConversationArtifacts { .. }
| BlocklistAIHistoryEvent::ConversationOwnershipTransferred { .. } => {}
BlocklistAIHistoryEvent::OrchestrationConfigUpdated { .. }
| BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { .. } => {}
| BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { .. }
| BlocklistAIHistoryEvent::LocalSharedSessionEstablished { .. } => {}
}
}

Expand Down
9 changes: 9 additions & 0 deletions app/src/ai/blocklist/history_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2572,6 +2572,13 @@ pub enum BlocklistAIHistoryEvent {
ConversationUsageMetadataUpdated {
conversation_id: AIConversationId,
},

/// Emitted when a sharer-owned conversation establishes a local
/// shared session.
LocalSharedSessionEstablished {
conversation_id: AIConversationId,
session_id: session_sharing_protocol::common::SessionId,
},
}

impl BlocklistAIHistoryEvent {
Expand Down Expand Up @@ -2653,6 +2660,8 @@ impl BlocklistAIHistoryEvent {
// orchestrator footer reading descendant credits) can't be
// disambiguated by a single owner pane.
BlocklistAIHistoryEvent::ConversationUsageMetadataUpdated { .. } => None,
// Conversation-scoped; subscribers resolve the owning view via conversation_id.
BlocklistAIHistoryEvent::LocalSharedSessionEstablished { .. } => None,
}
}
}
Expand Down
102 changes: 102 additions & 0 deletions app/src/ai/blocklist/local_shared_session_link_model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use super::history_model::{BlocklistAIHistoryEvent, BlocklistAIHistoryModel};
use crate::ai::agent::conversation::AIConversationId;
use crate::server::server_api::ai::AIClient;
use crate::server::server_api::ServerApiProvider;
use session_sharing_protocol::common::SessionId;
use std::sync::Arc;
use warpui::{Entity, ModelContext, SingletonEntity};

/// Ensures that session ID for locally owned shared conversations is linked
/// to their `ai_tasks` row in the DB. This enables viewers to reconstruct
/// the conversation's orchestration state.
pub struct LocalSharedSessionLinkModel {
ai_client: Arc<dyn AIClient>,
}

pub enum LocalSharedSessionLinkModelEvent {}

impl LocalSharedSessionLinkModel {
pub fn new(ctx: &mut ModelContext<Self>) -> Self {
let ai_client = ServerApiProvider::as_ref(ctx).get_ai_client();
Self::new_with_ai_client(ai_client, ctx)
}

/// Test-friendly constructor.
fn new_with_ai_client(ai_client: Arc<dyn AIClient>, ctx: &mut ModelContext<Self>) -> Self {
let history_model = BlocklistAIHistoryModel::handle(ctx);
ctx.subscribe_to_model(&history_model, |me, event, ctx| {
me.handle_history_event(event, ctx);
});

Self { ai_client }
}

/// Test-only constructor that lets tests inject a mock `AIClient`.
#[cfg(test)]
pub(super) fn new_with_ai_client_for_test(
ai_client: Arc<dyn AIClient>,
ctx: &mut ModelContext<Self>,
) -> Self {
Self::new_with_ai_client(ai_client, ctx)
}

fn handle_history_event(&self, event: &BlocklistAIHistoryEvent, ctx: &mut ModelContext<Self>) {
if let BlocklistAIHistoryEvent::LocalSharedSessionEstablished {
conversation_id,
session_id,
} = event
{
self.on_local_shared_session_established(*conversation_id, *session_id, ctx);
}
}

/// Links the conversation's `task_id` to `session_id` on the server.
/// Skips viewers, remote-child placeholders, and conversations without
/// a `task_id` (pre-StreamInit).
fn on_local_shared_session_established(
&self,
conversation_id: AIConversationId,
session_id: SessionId,
ctx: &mut ModelContext<Self>,
) {
let Some(conversation) =
BlocklistAIHistoryModel::as_ref(ctx).conversation(&conversation_id)
else {
return;
};
if conversation.is_viewing_shared_session() {
return;
}
if conversation.is_remote_child() {
return;
}
let Some(task_id) = conversation.task_id() else {
return;
};

let ai_client = self.ai_client.clone();
ctx.spawn(
async move {
if let Err(err) = ai_client
.update_agent_task(task_id, None, Some(session_id), None, None)
.await
{
log::warn!(
"LocalSharedSessionLinkModel: failed to link task {task_id} to shared session {session_id}: {err:#}"
);
}
},
|_, _, _| {},
);
}
}

impl Entity for LocalSharedSessionLinkModel {
type Event = LocalSharedSessionLinkModelEvent;
}

impl SingletonEntity for LocalSharedSessionLinkModel {}

#[cfg(test)]
#[path = "local_shared_session_link_model_tests.rs"]
mod tests;
Loading
Loading