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
@@ -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 = ();
}