Files
galaxy/app/src/ai/agent_sdk/driver/error_classification.rs
T
Ryan WardandClaude Opus 4.6 6f54e2cb30 v1.4.0: Auto-compact streaming, Bedrock summarization support, subagent orchestration, and Galaxy rebrand continuation
Major features:
- Auto-compact: triggers conversation summarization when context window >= 85%,
  compacts Bedrock message history to a summary pair, and tracks live context tokens
- Bedrock summarization: plumbs `is_summarization` flag through translator/client/response
  pipeline, handles SummarizeConversation input type, and marks `summarized` in metadata
- Session restore: rebuilds bedrock_message_history from persisted task messages via
  newly-public `convert_proto_message`, preventing empty history on reconnect
- Subagent orchestration: adds SubagentQuestion/Answer/CompletionSummary event types,
  parent-child question routing with depth limits, retry counting, and drain methods
- Summarization UI: inline SummarizationView in AI blocks with progress/finished states

Refactors:
- Rename WarpTheme → GalaxyTheme across ~100 files (rebrand continuation)
- Rename warp_home_config_dir → galaxy_home_config_dir and related path functions
- Predefined rules: replace "System Defined Rule #N" with descriptive names
  (e.g. "Correctness Over Speed", "Never Guess") and add lookup helpers
- Usage view: replace cumulative input/output token display with live context tokens,
  cache hit rate calculation, and separate cache read/write stats
- Telemetry: remove verbose doc comments, simplify trait definitions
- Facts view: simplify delete permission check (always allow local deletion)
- Remove warp_managed_paths_watcher.rs (dead code)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 11:59:37 -05:00

334 lines
14 KiB
Rust

use crate::ai::agent::RenderableAIError;
use crate::ai::blocklist::task_status_sync_model::classify_renderable_error;
use crate::server::server_api::ai::TaskStatusUpdate;
use galaxy_graphql::ai::{AgentTaskState, PlatformErrorCode};
use super::terminal::ShareSessionError;
use super::AgentDriverError;
/// Classify an `AgentDriverError` into a task state and a `TaskStatusUpdate`
/// suitable for reporting via `update_agent_task`.
pub fn classify_driver_error(error: &AgentDriverError) -> (AgentTaskState, TaskStatusUpdate) {
match error {
// --- Warp-side errors (task → ERROR) ---
AgentDriverError::TerminalUnavailable | AgentDriverError::InvalidRuntimeState => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
"An internal error occurred. Please try running your task again. If the issue persists, contact support.",
PlatformErrorCode::InternalError,
),
),
AgentDriverError::BootstrapFailed => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
"Terminal session failed to start. Please try running your task again.",
PlatformErrorCode::InternalError,
),
),
AgentDriverError::ShareSessionFailed { error: share_err } => {
let message = match share_err {
ShareSessionError::Internal(_) => {
"Failed to share agent session due to an internal error. Please try running your task again.".to_string()
}
ShareSessionError::Failed(reason) => {
// The reason string comes from the session-sharing layer and is aimed at
// interactive users (e.g. "try sharing again"). Provide a cloud-agent-
// appropriate message instead of wrapping it, which would produce
// repetitive "try again" text.
format!("Failed to share agent session: {reason}")
}
ShareSessionError::Disabled => {
"Session sharing is not enabled for your account. This is likely because \
an administrator has disabled session sharing for your team. Please \
verify that session sharing is enabled in your team settings, or try \
running without the --share flag."
.to_string()
}
ShareSessionError::Timeout => {
"Failed to share agent session: timed out waiting for the session sharing \
server to respond. Please check your network connection and try again."
.to_string()
}
ShareSessionError::Interrupted => {
"Session sharing was interrupted before it could complete. Please try running your task again.".to_string()
}
};
(
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
message,
match share_err {
ShareSessionError::Disabled => PlatformErrorCode::FeatureNotAvailable,
_ => PlatformErrorCode::InternalError,
},
),
)
}
AgentDriverError::WarpDriveSyncFailed => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
"Galaxy Drive failed to sync. Please check your network connection and try again.",
PlatformErrorCode::InternalError,
),
),
AgentDriverError::NotLoggedIn => {
let bin = galaxy_cli::binary_name().unwrap_or_else(|| "warp".to_string());
(
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
format!(
"Authentication required. Log in via '{bin} login', provide an API key via '--api-key', or set the GALAXY_API_KEY environment variable."
),
PlatformErrorCode::AuthenticationRequired,
),
)
}
AgentDriverError::CloudProviderSetupFailed(err) => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
format!("Error configuring cloud access: {err:#}"),
PlatformErrorCode::InternalError,
),
),
// --- User-side errors (task → FAILED) ---
AgentDriverError::MCPServerNotFound(uuid) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"MCP server {uuid} was not found. Verify the server exists in your Galaxy Drive and the UUID is correct."
),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::MCPStartupFailed => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
"One or more MCP servers failed to start. Check that your MCP server configuration is valid and the server process is runnable.",
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::MCPJsonParseError(msg) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!("Failed to parse MCP server JSON configuration: {msg}"),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::MCPMissingVariables => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
"MCP server configuration is missing required variables. Provide all required environment variables or template values.",
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::ProfileError(name) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Agent profile \"{name}\" not found. Check the profile ID and ensure it exists in your team's Galaxy Drive."
),
PlatformErrorCode::ResourceNotFound,
),
),
AgentDriverError::AIWorkflowNotFound(id) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Saved prompt not found for ID {id}. Verify the prompt exists in your Galaxy Drive."
),
PlatformErrorCode::ResourceNotFound,
),
),
AgentDriverError::EnvironmentNotFound(id) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Environment '{id}' not found. Verify the environment ID and ensure it exists in your team settings."
),
PlatformErrorCode::ResourceNotFound,
),
),
AgentDriverError::EnvironmentSetupFailed(msg) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Environment setup failed: {msg}. Check your repository URLs and setup commands."
),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::InvalidWorkingDirectory { path, .. } => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Working directory '{}' does not exist or is not a directory. Verify the path in your environment configuration.",
path.display()
),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
// --- Conversation errors ---
// Delegate to classify_renderable_error for proper ERROR vs FAILED
// distinction and PlatformErrorCode. This is a belt-and-suspenders
// fallback — TaskStatusSyncModel handles most conversation errors,
// but the driver catches them too if the conversation ends with an error.
AgentDriverError::ConversationError { error } => {
let (state, update) = classify_renderable_error(error);
(
state,
update.unwrap_or_else(|| {
TaskStatusUpdate::with_error_code(
error.to_string(),
PlatformErrorCode::InternalError,
)
}),
)
}
// --- Cancellation / Blocked (no error code) ---
AgentDriverError::ConversationCancelled { .. } => (
AgentTaskState::Cancelled,
TaskStatusUpdate::message("Task cancelled."),
),
AgentDriverError::ConversationBlocked { blocked_action } => (
AgentTaskState::Blocked,
TaskStatusUpdate::message(format!(
"The agent got stuck waiting for user confirmation on the action: {blocked_action}"
)),
),
// --- Setup errors ---
AgentDriverError::TeamMetadataRefreshTimeout => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
"Timed out refreshing team metadata. Please check your network connection and try again.",
PlatformErrorCode::InternalError,
),
),
AgentDriverError::SkillResolutionFailed(msg) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!("Skill resolution failed: {msg}"),
PlatformErrorCode::ResourceNotFound,
),
),
AgentDriverError::ConfigBuildFailed(err) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!("Failed to build agent configuration: {err}"),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::PromptResolutionFailed(err) => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
format!("Failed to resolve prompt for the run: {err}"),
PlatformErrorCode::InternalError,
),
),
AgentDriverError::SecretsFetchFailed(err) => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
format!("Failed to fetch task secrets: {err}"),
PlatformErrorCode::InternalError,
),
),
AgentDriverError::AwsBedrockCredentialsFailed(msg) => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!("Failed to initialize AWS Bedrock credentials: {msg}"),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::ConversationLoadFailed(msg) => (
AgentTaskState::Error,
TaskStatusUpdate::with_error_code(
format!("Failed to load conversation: {msg}"),
PlatformErrorCode::InternalError,
),
),
AgentDriverError::ConversationHarnessMismatch { conversation_id, expected, got } => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Conversation {conversation_id} was produced by the {expected} harness, but --harness {got} was requested. \
Re-run with --harness {expected} (or omit --harness) to continue this conversation."
),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::TaskHarnessMismatch { task_id, expected, got } => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Task {task_id} was created with the {expected} harness, but --harness {got} was requested. \
Re-run with --harness {expected} (or omit --harness) to continue this task."
),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::ConversationResumeStateMissing { harness, conversation_id } => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!(
"Conversation {conversation_id} has no stored transcript for the {harness} harness. \
The prior run may have crashed before saving any state."
),
PlatformErrorCode::ResourceNotFound,
),
),
AgentDriverError::HarnessCommandFailed { exit_code } => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!("Harness command exited with code {exit_code}"),
PlatformErrorCode::InternalError,
),
),
AgentDriverError::HarnessSetupFailed { harness, reason } => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!("Harness '{harness}' validation failed: {reason}"),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
AgentDriverError::HarnessConfigSetupFailed { harness, error } => (
AgentTaskState::Failed,
TaskStatusUpdate::with_error_code(
format!("Harness '{harness}' config setup failed: {error}"),
PlatformErrorCode::EnvironmentSetupFailed,
),
),
}
}
/// Returns true if an `AgentDriverError` represents a transient condition that
/// a subagent can recover from by retrying (e.g., network issues, rate limits,
/// server overload). Permanent errors (auth, config, cancelled) return false.
pub fn is_self_recoverable(error: &AgentDriverError) -> bool {
match error {
AgentDriverError::ConversationError { error: renderable } => {
matches!(
renderable,
RenderableAIError::ServerOverloaded
| RenderableAIError::Other {
will_attempt_resume: true,
..
}
)
}
AgentDriverError::WarpDriveSyncFailed
| AgentDriverError::TeamMetadataRefreshTimeout
| AgentDriverError::ShareSessionFailed {
error: ShareSessionError::Timeout,
..
} => true,
_ => false,
}
}
#[cfg(test)]
#[path = "error_classification_tests.rs"]
mod tests;