Update AI agent, settings, and terminal modules

- Update AI agent conversation, task, and prompt builder
- Add notebooks execution module for blocklist actions
- Update Bedrock and OpenAI response translators
- Refactor settings modules across multiple subsystems
- Update terminal settings and window settings
- Update drive settings and search command settings
This commit is contained in:
Ryan Ward
2026-07-01 14:30:04 -05:00
parent 57c843d1de
commit 2f64909469
49 changed files with 683 additions and 325 deletions
+94 -32
View File
@@ -5,6 +5,7 @@ pub(super) mod edit_documents;
pub(super) mod fetch_conversation;
pub(super) mod file_glob;
pub(super) mod grep;
pub(super) mod notebooks;
pub(super) mod read_documents;
pub(super) mod read_files;
pub(super) mod read_mcp_resource;
@@ -31,6 +32,7 @@ use file_glob::FileGlobExecutor;
use futures::{future::BoxFuture, FutureExt};
use galaxy_core::{execution_mode::AppExecutionMode, features::FeatureFlag};
use grep::GrepExecutor;
use notebooks::NotebookExecutor;
use parking_lot::FairMutex;
use read_documents::ReadDocumentsExecutor;
pub(super) use read_files::ReadFilesExecutor;
@@ -253,6 +255,7 @@ pub struct BlocklistAIActionExecutor {
read_documents_executor: ModelHandle<ReadDocumentsExecutor>,
edit_documents_executor: ModelHandle<EditDocumentsExecutor>,
create_documents_executor: ModelHandle<CreateDocumentsExecutor>,
notebook_executor: ModelHandle<NotebookExecutor>,
use_computer_executor: ModelHandle<UseComputerExecutor>,
request_computer_use_executor: ModelHandle<RequestComputerUseExecutor>,
read_skill_executor: ModelHandle<ReadSkillExecutor>,
@@ -317,6 +320,7 @@ impl BlocklistAIActionExecutor {
let edit_documents_executor = ctx.add_model(|_| EditDocumentsExecutor::new());
let create_documents_executor = ctx
.add_model(|_| CreateDocumentsExecutor::new(active_session.clone(), terminal_view_id));
let notebook_executor = ctx.add_model(|_| NotebookExecutor::new());
let use_computer_executor = ctx.add_model(|_| UseComputerExecutor::new());
let request_computer_use_executor =
ctx.add_model(|_| RequestComputerUseExecutor::new(terminal_view_id));
@@ -341,6 +345,7 @@ impl BlocklistAIActionExecutor {
read_documents_executor,
edit_documents_executor,
create_documents_executor,
notebook_executor,
use_computer_executor,
request_computer_use_executor,
async_executing_actions: Default::default(),
@@ -487,15 +492,33 @@ impl BlocklistAIActionExecutor {
AIAgentActionType::SuggestPrompt { .. } => self
.suggest_prompt_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
AIAgentActionType::ReadDocuments(_) => self
.read_documents_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
AIAgentActionType::EditDocuments(_) => self
.edit_documents_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
AIAgentActionType::CreateDocuments(_) => self
.create_documents_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
AIAgentActionType::ReadDocuments(_) => {
if action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
} else {
self.read_documents_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
}
}
AIAgentActionType::EditDocuments(_) => {
if action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
} else {
self.edit_documents_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
}
}
AIAgentActionType::CreateDocuments(_) => {
if action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
} else {
self.create_documents_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx))
}
}
AIAgentActionType::UseComputer(_) => self
.use_computer_executor
.update(ctx, |executor, ctx| executor.preprocess_action(input, ctx)),
@@ -683,20 +706,41 @@ impl BlocklistAIActionExecutor {
.suggest_prompt_executor
.update(ctx, |executor, ctx| executor.execute(input, ctx))
.into(),
AIAgentActionType::ReadDocuments(_) => self
.read_documents_executor
.update(ctx, |executor, ctx| executor.execute(input, ctx))
.into(),
AIAgentActionType::EditDocuments(_) => self
.edit_documents_executor
.update(ctx, |executor, ctx| executor.execute(input, ctx))
.into(),
AIAgentActionType::CreateDocuments(_) => self
.create_documents_executor
.update(ctx, |executor, ctx| {
executor.execute(input, conversation_id, ctx)
})
.into(),
AIAgentActionType::ReadDocuments(_) => {
if action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.execute_read(input, ctx))
.into()
} else {
self.read_documents_executor
.update(ctx, |executor, ctx| executor.execute(input, ctx))
.into()
}
}
AIAgentActionType::EditDocuments(_) => {
if action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.execute_edit(input, ctx))
.into()
} else {
self.edit_documents_executor
.update(ctx, |executor, ctx| executor.execute(input, ctx))
.into()
}
}
AIAgentActionType::CreateDocuments(_) => {
if action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.execute_create(input, ctx))
.into()
} else {
self.create_documents_executor
.update(ctx, |executor, ctx| {
executor.execute(input, conversation_id, ctx)
})
.into()
}
}
AIAgentActionType::UseComputer(_) => self
.use_computer_executor
.update(ctx, |executor, ctx| executor.execute(input, ctx))
@@ -924,15 +968,33 @@ impl BlocklistAIActionExecutor {
AIAgentActionType::SuggestPrompt { .. } => self
.suggest_prompt_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
AIAgentActionType::ReadDocuments(_) => self
.read_documents_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
AIAgentActionType::EditDocuments(_) => self
.edit_documents_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
AIAgentActionType::CreateDocuments(_) => self
.create_documents_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
AIAgentActionType::ReadDocuments(_) => {
if input.action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
} else {
self.read_documents_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
}
}
AIAgentActionType::EditDocuments(_) => {
if input.action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
} else {
self.edit_documents_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
}
}
AIAgentActionType::CreateDocuments(_) => {
if input.action.tool_name.as_deref() == Some("notebook") {
self.notebook_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
} else {
self.create_documents_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx))
}
}
AIAgentActionType::UseComputer(_) => self
.use_computer_executor
.update(ctx, |executor, ctx| executor.should_autoexecute(input, ctx)),
@@ -10,7 +10,6 @@ use crate::{
artifacts::Artifact,
blocklist::BlocklistAIHistoryModel,
document::ai_document_model::{AIDocumentModel, AIDocumentVersion},
execution_profiles::profiles::AIExecutionProfilesModel,
},
notebooks::editor::model::FileLinkResolutionContext,
terminal::model::session::active_session::ActiveSession,
@@ -102,15 +101,10 @@ impl CreateDocumentsExecutor {
})
};
let profile = AIExecutionProfilesModel::as_ref(ctx)
.active_profile(Some(self.terminal_view_id), ctx);
let should_autosync = profile.data().autosync_plans_to_warp_drive;
if should_autosync {
model.update(ctx, |model, model_ctx| {
model.sync_to_warp_drive(id, model_ctx);
});
}
// Plans always sync to the Plans folder in Galaxy Drive.
model.update(ctx, |model, model_ctx| {
model.sync_to_warp_drive(id, model_ctx);
});
// Add plan artifact to the conversation.
let artifact = Artifact::Plan {
@@ -0,0 +1,207 @@
//! Executors for the `create_notebook`, `read_notebook`, and `edit_notebook` tools.
//!
//! These tools interact with Galaxy Drive CloudNotebook objects directly,
//! as opposed to the plan tools which work through AIDocumentModel.
use futures::{future::BoxFuture, FutureExt};
use galaxyui::{Entity, ModelContext, SingletonEntity};
use crate::{
ai::{
agent::{
AIAgentAction, AIAgentActionType, CreateDocumentsRequest, CreateDocumentsResult,
DocumentContext, EditDocumentsRequest, EditDocumentsResult, ReadDocumentsRequest,
ReadDocumentsResult,
},
document::ai_document_model::AIDocumentVersion,
},
cloud_object::model::persistence::CloudModel,
notebooks::CloudNotebookModel,
server::{cloud_objects::update_manager::UpdateManager, ids::ClientId},
workspaces::user_workspaces::UserWorkspaces,
};
use super::{ActionExecution, AnyActionExecution, ExecuteActionInput, PreprocessActionInput};
pub struct NotebookExecutor;
impl NotebookExecutor {
pub fn new() -> Self {
Self
}
pub(super) fn should_autoexecute(
&self,
_input: ExecuteActionInput,
_ctx: &mut ModelContext<Self>,
) -> bool {
true
}
pub(super) fn execute_create(
&mut self,
input: ExecuteActionInput,
ctx: &mut ModelContext<Self>,
) -> impl Into<AnyActionExecution> {
let ExecuteActionInput { action, .. } = input;
let AIAgentAction {
action: AIAgentActionType::CreateDocuments(CreateDocumentsRequest { documents }),
..
} = action
else {
return ActionExecution::<CreateDocumentsResult>::InvalidAction;
};
let Some(owner) = UserWorkspaces::as_ref(ctx).personal_drive(ctx) else {
return ActionExecution::Sync(
CreateDocumentsResult::Error("No personal drive available.".to_string()).into(),
);
};
let mut created = Vec::new();
for document in documents {
let client_id = ClientId::new();
let model = CloudNotebookModel {
title: document.title.clone(),
data: document.content.clone(),
ai_document_id: None,
conversation_id: None,
};
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
update_manager.create_notebook(
client_id,
owner,
None, // root of personal space
model,
crate::cloud_object::CloudObjectEventEntrypoint::Unknown,
false,
ctx,
);
});
created.push(DocumentContext {
document_id: crate::ai::document::ai_document_model::AIDocumentId::new(),
document_version: AIDocumentVersion::default(),
content: document.content.clone(),
line_ranges: vec![],
});
}
ActionExecution::Sync(
CreateDocumentsResult::Success {
created_documents: created,
}
.into(),
)
}
pub(super) fn execute_read(
&mut self,
input: ExecuteActionInput,
ctx: &mut ModelContext<Self>,
) -> impl Into<AnyActionExecution> {
let ExecuteActionInput { action, .. } = input;
let AIAgentAction {
action: AIAgentActionType::ReadDocuments(ReadDocumentsRequest { document_ids }),
..
} = action
else {
return ActionExecution::<ReadDocumentsResult>::InvalidAction;
};
let cloud_model = CloudModel::as_ref(ctx);
let mut documents = Vec::new();
for id in document_ids {
// Try to find the notebook by treating the ID as a SyncId string
let notebook = cloud_model
.get_all_active_notebooks()
.find(|nb| nb.id.uid() == id.to_string());
if let Some(notebook) = notebook {
documents.push(DocumentContext {
document_id: *id,
document_version: AIDocumentVersion::default(),
content: format!("# {}\n\n{}", notebook.model().title, notebook.model().data),
line_ranges: vec![],
});
}
}
ActionExecution::Sync(ReadDocumentsResult::Success { documents }.into())
}
pub(super) fn execute_edit(
&mut self,
input: ExecuteActionInput,
ctx: &mut ModelContext<Self>,
) -> impl Into<AnyActionExecution> {
let ExecuteActionInput { action, .. } = input;
let AIAgentAction {
action: AIAgentActionType::EditDocuments(EditDocumentsRequest { diffs }),
..
} = action
else {
return ActionExecution::<EditDocumentsResult>::InvalidAction;
};
let mut updated_documents = Vec::new();
let mut errors = Vec::new();
for diff in diffs {
let cloud_model = CloudModel::as_ref(ctx);
let notebook_data = cloud_model
.get_all_active_notebooks()
.find(|nb| nb.id.uid() == diff.document_id.to_string())
.map(|nb| (nb.id, nb.model().data.clone()));
let Some((notebook_id, current_data)) = notebook_data else {
errors.push(format!("Notebook {} not found.", diff.document_id));
continue;
};
// Simple search/replace on the notebook data
if !current_data.contains(&diff.search) {
errors.push(format!(
"Could not find search text in notebook {}.",
diff.document_id
));
continue;
}
let new_data = current_data.replacen(&diff.search, &diff.replace, 1);
let new_data_arc = std::sync::Arc::new(new_data.clone());
UpdateManager::handle(ctx).update(ctx, |update_manager, ctx| {
update_manager.update_notebook_data(new_data_arc, notebook_id, ctx);
});
updated_documents.push(DocumentContext {
document_id: diff.document_id,
document_version: AIDocumentVersion::default(),
content: new_data,
line_ranges: vec![],
});
}
if !errors.is_empty() {
return ActionExecution::Sync(EditDocumentsResult::Error(errors.join("\n")).into());
}
ActionExecution::Sync(EditDocumentsResult::Success { updated_documents }.into())
}
pub(super) fn preprocess_action(
&mut self,
_input: PreprocessActionInput,
_ctx: &mut ModelContext<Self>,
) -> BoxFuture<'static, ()> {
futures::future::ready(()).boxed()
}
}
impl Entity for NotebookExecutor {
type Event = ();
}
+3 -6
View File
@@ -2226,11 +2226,9 @@ impl BlocklistAIController {
{
let history_model = BlocklistAIHistoryModel::as_ref(ctx);
if let Some(conversation) = history_model.conversation(&conversation_id) {
let has_optimistic_cli_subagent =
conversation.has_active_subagent();
let has_optimistic_cli_subagent = conversation.has_active_subagent();
if !has_optimistic_cli_subagent {
request_params.root_task_id =
Some(conversation.get_root_task_id().to_string());
request_params.root_task_id = Some(conversation.get_root_task_id().to_string());
}
}
}
@@ -2435,8 +2433,7 @@ impl BlocklistAIController {
{
let terminal_view_id = self.terminal_view_id;
history_model.update(ctx, |history_model, ctx| {
if let Some(conversation) =
history_model.conversation_mut(&conversation_id)
if let Some(conversation) = history_model.conversation_mut(&conversation_id)
{
conversation.force_cancel_all_streaming_exchanges(
terminal_view_id,