Checking in progress, though not fully working as expected

This commit is contained in:
Ryan Ward
2026-07-27 11:41:20 -05:00
parent 856a6cd6f3
commit 309000a303
51 changed files with 74 additions and 3315 deletions
-2
View File
@@ -9,7 +9,6 @@ license.workspace = true
jemalloc = []
local_fs = []
test-util = []
crash_reporting = ["dep:sentry"]
[dependencies]
async-channel.workspace = true
@@ -34,7 +33,6 @@ rayon.workspace = true
ignore = "0.4.23"
line-span = "0.1.5"
log.workspace = true
sentry = { workspace = true, optional = true }
shellexpand.workspace = true
strum.workspace = true
strum_macros.workspace = true
-3
View File
@@ -6,7 +6,6 @@ publish.workspace = true
license.workspace = true
[features]
crash_reporting = ["dep:sentry", "dep:sentry-log"]
integration_tests = []
local_fs = []
release_bundle = []
@@ -35,8 +34,6 @@ serde.workspace = true
settings.workspace = true
serde_json.workspace = true
serde_with.workspace = true
sentry = { workspace = true, optional = true }
sentry-log = { workspace = true, optional = true }
strum.workspace = true
strum_macros.workspace = true
sysinfo.workspace = true
-8
View File
@@ -21,8 +21,6 @@ pub struct ChannelConfig {
pub telemetry_config: Option<TelemetryConfig>,
/// Configuration for autoupdate functionality.
pub autoupdate_config: Option<AutoupdateConfig>,
/// Configuration for crash reporting.
pub crash_reporting_config: Option<CrashReportingConfig>,
/// Configuration for statically-bundled MCP OAuth credentials.
pub mcp_static_config: Option<McpStaticConfig>,
}
@@ -130,12 +128,6 @@ pub struct AutoupdateConfig {
pub show_autoupdate_menu_items: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct CrashReportingConfig {
/// The URL/DSN for sending error logs and crash reports to Sentry.
pub sentry_url: Cow<'static, str>,
}
/// Configuration for statically-bundled MCP OAuth credentials.
///
/// These are credentials for OAuth providers where dynamic client registration
-18
View File
@@ -48,7 +48,6 @@ impl ChannelState {
oz_config: OzConfig::production(),
telemetry_config: None,
autoupdate_config: None,
crash_reporting_config: None,
mcp_static_config: None,
},
}
@@ -196,13 +195,6 @@ impl ChannelState {
CHANNEL_STATE.lock().config.telemetry_config.is_some()
}
/// Returns whether this build has a crash reporting config and can therefore
/// ship crash reports. Builds like OpenWarp intentionally ship with
/// `crash_reporting_config: None`, in which case UI that controls crash
/// reporting should be hidden since the toggle has no effect.
pub fn is_crash_reporting_available() -> bool {
CHANNEL_STATE.lock().config.crash_reporting_config.is_some()
}
pub fn releases_base_url() -> Cow<'static, str> {
CHANNEL_STATE
@@ -346,16 +338,6 @@ impl ChannelState {
option_env!("GIT_RELEASE_TAG")
}
pub fn sentry_url() -> Cow<'static, str> {
CHANNEL_STATE
.lock()
.config
.crash_reporting_config
.as_ref()
.map(|crc| crc.sentry_url.clone())
.unwrap_or_default()
}
pub fn show_autoupdate_menu_items() -> bool {
CHANNEL_STATE
.lock()
-13
View File
@@ -103,25 +103,12 @@ macro_rules! report_if_error {
}
pub use report_if_error;
/// Returns whether or not a log entry with the given metadata should be
/// ignored by Sentry.
#[cfg(feature = "crash_reporting")]
pub fn should_ignore_log_for_sentry(md: &log::Metadata) -> bool {
// Filter out any Error-level log entries generated by report_error!().
// report_error!() utilizes capture_anyhow() to report structured errors
// instead of simple string error messages, and we don't want to _also_
// report the Error-level log line to Sentry.
md.target() == LOG_TARGET && md.level() == log::Level::Error
}
pub trait ErrorExt: RegisteredError + std::error::Error {
/// Returns whether or not an error is something that is actionable by our
/// engineering team.
fn is_actionable(&self) -> bool;
fn report_error(&self) {
#[cfg(feature = "crash_reporting")]
sentry::capture_error(self);
}
}
-2
View File
@@ -25,7 +25,5 @@ impl AnyhowErrorExt for anyhow::Error {
}
fn report_error(&self) {
#[cfg(feature = "crash_reporting")]
sentry::integrations::anyhow::capture_anyhow(self);
}
}
-6
View File
@@ -7,11 +7,8 @@ pub use overrides::{get_overrides, set_overrides};
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Sequence)]
pub enum FeatureFlag {
Changelog,
CocoaSentry,
CrashReporting,
DebugMode,
Autoupdate,
LogExpensiveFramesInSentry,
WithSandboxTelemetry,
RecordAppActiveEvents,
@@ -934,7 +931,6 @@ pub const LOCAL_FLAGS: &[FeatureFlag] = &[FeatureFlag::LocalClaudeCodexChildHarn
/// Features enabled for the development team. The expectation is that, over
/// time, these will move on to PREVIEW_FLAGS before being launched.
pub const DOGFOOD_FLAGS: &[FeatureFlag] = &[
FeatureFlag::LogExpensiveFramesInSentry,
FeatureFlag::ToggleBootstrapBlock,
FeatureFlag::CreatingSharedSessions,
FeatureFlag::RemoveAutosuggestionDuringTabCompletions,
@@ -979,7 +975,6 @@ pub const DOGFOOD_FLAGS: &[FeatureFlag] = &[
FeatureFlag::PinnedTabs,
FeatureFlag::BackgroundComputerUse,
FeatureFlag::ContextWindowUsageBreakdown,
FeatureFlag::CloudRunners,
FeatureFlag::WaitForEventsParentRegistration,
];
@@ -997,7 +992,6 @@ pub const PREVIEW_FLAGS: &[FeatureFlag] = &[
pub const RELEASE_FLAGS: &[FeatureFlag] = &[
FeatureFlag::Autoupdate,
FeatureFlag::Changelog,
FeatureFlag::CrashReporting,
// Marked text is currently only supported on MacOS.
#[cfg(target_os = "macos")]
FeatureFlag::ImeMarkedText,
-4
View File
@@ -18,9 +18,6 @@ dirs.workspace = true
log-panics = { version = "2.1.0", features = ["with-backtrace"] }
zip = "2.1"
# Optional crash reporting integration.
sentry = { workspace = true, optional = true }
sentry-log = { workspace = true, optional = true }
[target.'cfg(target_family = "wasm")'.dependencies]
console_error_panic_hook = { version = "0.1.6" }
@@ -33,5 +30,4 @@ galaxy_web_event_bus.workspace = true
tempfile.workspace = true
[features]
crash_reporting = ["dep:sentry", "dep:sentry-log", "galaxy_core/crash_reporting"]
agent_mode_evals = []
-36
View File
@@ -475,32 +475,6 @@ fn temp_log_file_path(log_directory: impl AsRef<Path>) -> PathBuf {
.join(format!("{channel_logfile_name}.{TEMP_LOG_FILE_SUFFIX}"))
}
#[cfg(feature = "crash_reporting")]
fn sentry_log_filter(md: &log::Metadata) -> sentry_log::LogFilter {
if galaxy_core::errors::should_ignore_log_for_sentry(md) {
return sentry_log::LogFilter::Ignore;
}
match md.target() {
// Ignore any log lines that come from the `log_panics` crate.
"panic" => sentry_log::LogFilter::Ignore,
// Filter out spammy INFO-level log lines from wgpu.
t if t.starts_with("wgpu_core") || t.starts_with("wgpu_hal") => {
sentry_log::LogFilter::Ignore
}
// Filter out the "redraw_frame" logging from breadcrumbs.
"galaxyui_core::core::redraw_frame" => sentry_log::LogFilter::Ignore,
// Filter out logs from the crash-reporting implementation, in case it logs
// anything in the process of forwarding logs to Sentry.
t if t.starts_with("galaxy::crash_reporting::") => sentry_log::LogFilter::Ignore,
_ => sentry_log::default_filter(md),
}
}
fn init_internal(
is_from_crash_recovery_process: bool,
is_cli: bool,
@@ -610,16 +584,6 @@ fn init_internal(
base_logger.format(format_for_terminal_output);
}
#[cfg(feature = "crash_reporting")]
{
let base_logger = base_logger.build();
log::set_max_level(base_logger.filter());
let logger = sentry_log::SentryLogger::with_dest(base_logger).filter(sentry_log_filter);
log::set_boxed_logger(Box::new(logger))
.expect("Should not have already initialized a logger");
}
#[cfg(not(feature = "crash_reporting"))]
base_logger.init();
// If we're logging to a file, initialize the `log_panics` crate, which
@@ -53,7 +53,6 @@ pub fn main() -> Result<()> {
workload_audience_url: None,
},
telemetry_config: None,
crash_reporting_config: None,
autoupdate_config: None,
mcp_static_config: None,
},
@@ -546,7 +546,7 @@ pub fn test_restore_snapshot_with_settings_page() -> Builder {
TestStep::new("Verify settings pane restoration")
.add_assertion(assert_pane_title(0, 1, "Settings"))
.add_assertion(move |app, window_id| {
// Verify the settings view exists and is on the Referrals page.
// Verify the settings view exists and is on the About page.
let settings_views: Vec<ViewHandle<SettingsView>> = app
.views_of_type(window_id)
.expect("Settings view must exist");
@@ -556,7 +556,7 @@ pub fn test_restore_snapshot_with_settings_page() -> Builder {
settings_view.read(app, |view, _| {
async_assert_eq!(
view.current_settings_section(),
SettingsSection::Referrals
SettingsSection::About
)
})
}),
-14
View File
@@ -1,14 +0,0 @@
[package]
name = "managed_secrets_wasm"
version = "0.1.0"
authors.workspace = true
license.workspace = true
publish.workspace = true
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
galaxy_managed_secrets.workspace = true
wasm-bindgen.workspace = true
-118
View File
@@ -1,118 +0,0 @@
use galaxy_managed_secrets::{ManagedSecretValue, UploadKey, init_envelope};
use wasm_bindgen::prelude::*;
/// Called once when the WASM module is instantiated.
#[wasm_bindgen(start)]
pub fn start() {
init_envelope();
}
/// Helper: import keyset and encrypt a secret value.
fn do_encrypt(
public_key_base64: &str,
actor_uid: &str,
secret_name: &str,
secret: &ManagedSecretValue,
) -> Result<String, JsValue> {
let upload_key = UploadKey::import_public_keyset(public_key_base64)
.map_err(|e| JsValue::from_str(&format!("failed to import public key: {e}")))?;
upload_key
.encrypt_secret(actor_uid, secret_name, secret)
.map_err(|e| JsValue::from_str(&format!("encryption failed: {e}")))
}
/// Encrypt a raw secret value.
#[wasm_bindgen]
pub fn encrypt_raw_secret(
public_key_base64: &str,
actor_uid: &str,
secret_name: &str,
secret_value: &str,
) -> Result<String, JsValue> {
do_encrypt(
public_key_base64,
actor_uid,
secret_name,
&ManagedSecretValue::raw_value(secret_value),
)
}
/// Encrypt an Anthropic API key secret.
#[wasm_bindgen]
pub fn encrypt_anthropic_api_key_secret(
public_key_base64: &str,
actor_uid: &str,
secret_name: &str,
api_key: &str,
) -> Result<String, JsValue> {
do_encrypt(
public_key_base64,
actor_uid,
secret_name,
&ManagedSecretValue::anthropic_api_key(api_key),
)
}
/// Encrypt an Anthropic Bedrock API key secret.
#[wasm_bindgen]
pub fn encrypt_anthropic_bedrock_api_key_secret(
public_key_base64: &str,
actor_uid: &str,
secret_name: &str,
aws_bearer_token_bedrock: &str,
aws_region: &str,
) -> Result<String, JsValue> {
do_encrypt(
public_key_base64,
actor_uid,
secret_name,
&ManagedSecretValue::anthropic_bedrock_api_key(aws_bearer_token_bedrock, aws_region),
)
}
/// Encrypt an Anthropic Bedrock access key secret.
///
/// `aws_session_token` is optional and may be `None` for persistent IAM credentials
/// that do not require a session token.
#[wasm_bindgen]
pub fn encrypt_anthropic_bedrock_access_key_secret(
public_key_base64: &str,
actor_uid: &str,
secret_name: &str,
aws_access_key_id: &str,
aws_secret_access_key: &str,
aws_session_token: Option<String>,
aws_region: &str,
) -> Result<String, JsValue> {
do_encrypt(
public_key_base64,
actor_uid,
secret_name,
&ManagedSecretValue::anthropic_bedrock_access_key(
aws_access_key_id,
aws_secret_access_key,
aws_session_token,
aws_region,
),
)
}
/// Encrypt an OpenAI API key secret.
///
/// `base_url` is optional; when `None`, the harness uses the provider's default endpoint.
#[wasm_bindgen]
pub fn encrypt_openai_api_key_secret(
public_key_base64: &str,
actor_uid: &str,
secret_name: &str,
api_key: &str,
base_url: Option<String>,
) -> Result<String, JsValue> {
do_encrypt(
public_key_base64,
actor_uid,
secret_name,
&ManagedSecretValue::openai_api_key(api_key, base_url),
)
}
-24
View File
@@ -1,24 +0,0 @@
[package]
name = "warp_multi_agent_client"
version = "0.1.0"
edition = "2024"
authors.workspace = true
publish.workspace = true
license.workspace = true
[features]
agent_mode_evals = ["warp_server_client/agent_mode_evals"]
[dependencies]
anyhow.workspace = true
base64.workspace = true
cfg-if.workspace = true
futures.workspace = true
prost.workspace = true
reqwest-eventsource.workspace = true
thiserror.workspace = true
tracing.workspace = true
tracing-futures.workspace = true
warp_core.workspace = true
warp_multi_agent_api.workspace = true
warp_server_client.workspace = true
-170
View File
@@ -1,170 +0,0 @@
use base64::Engine as _;
use base64::prelude::BASE64_URL_SAFE;
use futures::StreamExt as _;
use prost::Message as _;
use tracing_futures::Instrument as _;
use warp_core::channel::ChannelState;
#[cfg(feature = "agent_mode_evals")]
use warp_server_client::base_client::EVAL_USER_ID_HEADER;
use warp_server_client::base_client::{AmbientHeaderPolicy, BaseClient};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Failed to authenticate multi-agent request")]
Authentication(#[source] anyhow::Error),
#[error("Failed to resolve ambient headers for multi-agent request")]
AmbientHeaders(#[source] anyhow::Error),
#[error("Failed to decode base64 multi-agent response event")]
Base64Decode(#[source] base64::DecodeError),
#[error("Failed to decode protobuf multi-agent response event")]
ProtobufDecode(#[source] prost::DecodeError),
#[error("Multi-agent eventsource stream failed: {0:?}")]
EventSource(Box<reqwest_eventsource::Error>),
}
cfg_if::cfg_if! {
if #[cfg(target_family = "wasm")] {
/// A multi-agent response event stream without an unnecessary `Send` bound on WASM.
pub type OutputStream = futures::stream::LocalBoxStream<
'static,
Result<warp_multi_agent_api::ResponseEvent, Error>,
>;
} else {
/// A multi-agent response event stream that can be sent between native threads.
pub type OutputStream = futures::stream::BoxStream<
'static,
Result<warp_multi_agent_api::ResponseEvent, Error>,
>;
}
}
/// Opens a decoded multi-agent response event stream.
pub async fn generate_multi_agent_output(
client: &BaseClient,
request: &warp_multi_agent_api::Request,
) -> Result<OutputStream, Error> {
let auth_token = client
.get_or_refresh_access_token()
.await
.map_err(Error::Authentication)?;
let is_passive = is_passive_suggestion_request(request);
let url = endpoint_url(is_passive);
let mut request_builder = client
.http_client()
.post(url)
.proto(request)
.prevent_sleep("Agent Mode request in-progress");
if let Some(token) = auth_token.as_bearer_token() {
request_builder = request_builder.bearer_auth(token);
}
for (name, value) in client
.ambient_headers(ambient_policy(is_passive))
.await
.map_err(Error::AmbientHeaders)?
{
request_builder = request_builder.header(name, value);
}
#[cfg(feature = "agent_mode_evals")]
if let Some(eval_user_id) = client.eval_user_id() {
request_builder = request_builder.header(EVAL_USER_ID_HEADER, eval_user_id.to_string());
}
let raw_stream = client.wrap_eventsource_with_iap_detection(request_builder.eventsource());
let output_stream = raw_stream.filter_map(|event| async {
match event {
Ok(reqwest_eventsource::Event::Message(message_event)) => {
Some(decode_response_event(&message_event.data))
}
Ok(reqwest_eventsource::Event::Open) => None,
Err(error) => Some(Err(Error::EventSource(Box::new(error)))),
}
});
// Once we get the init event, add some identifiers to the trace span.
let output_stream = output_stream.inspect(|event| {
if let Ok(event) = &event {
match &event.r#type {
Some(warp_multi_agent_api::response_event::Type::Init(init)) => {
tracing::info!("StreamInit");
tracing::Span::current().record("conversation_id", &init.conversation_id);
tracing::Span::current().record("request_id", &init.request_id);
tracing::Span::current().record("run_id", &init.run_id);
}
Some(warp_multi_agent_api::response_event::Type::Finished(_finished)) => {
tracing::info!("StreamFinished");
}
_ => {}
}
}
});
// Wrap the output stream with a trace span.
let output_stream = output_stream.instrument(tracing::info_span!(
"generate_multi_agent_output",
tags.cloud_agent = true,
conversation_id = tracing::field::Empty,
request_id = tracing::field::Empty,
run_id = tracing::field::Empty,
));
cfg_if::cfg_if! {
if #[cfg(target_family = "wasm")] {
Ok(output_stream.boxed_local())
} else {
Ok(output_stream.boxed())
}
}
}
fn is_passive_suggestion_request(request: &warp_multi_agent_api::Request) -> bool {
request.input.as_ref().is_some_and(|input| {
matches!(
input.r#type,
Some(warp_multi_agent_api::request::input::Type::GeneratePassiveSuggestions(_))
)
})
}
fn endpoint_url(is_passive: bool) -> String {
format!(
"{}/{}/{}",
ChannelState::server_root_url(),
if cfg!(feature = "agent_mode_evals") {
"agent-mode-evals"
} else {
"ai"
},
if is_passive {
"passive-suggestions"
} else {
"multi-agent"
}
)
}
fn ambient_policy(is_passive: bool) -> AmbientHeaderPolicy {
if is_passive {
// Passive suggestions read from the main conversation, but cannot modify it.
AmbientHeaderPolicy::omit_all()
} else {
AmbientHeaderPolicy::workload_only()
}
}
fn decode_response_event(data: &str) -> Result<warp_multi_agent_api::ResponseEvent, Error> {
let decoded_data = BASE64_URL_SAFE
.decode(data.trim_matches('"'))
.map_err(Error::Base64Decode)?;
warp_multi_agent_api::ResponseEvent::decode(decoded_data.as_slice())
.map_err(Error::ProtobufDecode)
}
#[cfg(test)]
#[path = "lib_tests.rs"]
mod tests;
@@ -1,77 +0,0 @@
use base64::Engine as _;
use base64::prelude::BASE64_URL_SAFE;
use prost::Message as _;
use warp_server_client::base_client::AmbientHeaderPolicy;
use super::{
Error, ambient_policy, decode_response_event, endpoint_url, is_passive_suggestion_request,
};
#[test]
fn detects_passive_suggestion_requests() {
let regular = warp_multi_agent_api::Request::default();
let passive = warp_multi_agent_api::Request {
input: Some(warp_multi_agent_api::request::Input {
r#type: Some(
warp_multi_agent_api::request::input::Type::GeneratePassiveSuggestions(
Default::default(),
),
),
..Default::default()
}),
..Default::default()
};
assert!(!is_passive_suggestion_request(&regular));
assert!(is_passive_suggestion_request(&passive));
}
#[test]
fn routes_regular_and_passive_requests_to_distinct_endpoints() {
let prefix = if cfg!(feature = "agent_mode_evals") {
"agent-mode-evals"
} else {
"ai"
};
assert!(endpoint_url(false).ends_with(&format!("/{prefix}/multi-agent")));
assert!(endpoint_url(true).ends_with(&format!("/{prefix}/passive-suggestions")));
}
#[test]
fn selects_endpoint_specific_ambient_header_policies() {
assert_eq!(ambient_policy(false), AmbientHeaderPolicy::workload_only());
assert_eq!(ambient_policy(true), AmbientHeaderPolicy::omit_all());
}
#[test]
fn decodes_quoted_base64_protobuf_response_event() {
let expected = warp_multi_agent_api::ResponseEvent::default();
let encoded = BASE64_URL_SAFE.encode(expected.encode_to_vec());
let decoded = decode_response_event(&format!("\"{encoded}\"")).unwrap();
assert_eq!(decoded, expected);
}
#[test]
fn distinguishes_base64_and_protobuf_decode_errors() {
assert!(matches!(
decode_response_event("%"),
Err(Error::Base64Decode(_))
));
let invalid_protobuf = BASE64_URL_SAFE.encode([0xff]);
assert!(matches!(
decode_response_event(&invalid_protobuf),
Err(Error::ProtobufDecode(_))
));
}
#[cfg(not(target_family = "wasm"))]
#[test]
fn native_output_stream_is_send() {
fn assert_send<T: Send>() {}
assert_send::<super::OutputStream>();
}
-1
View File
@@ -19,7 +19,6 @@ fn main() -> Result<()> {
server_config: WarpServerConfig::production(),
oz_config: OzConfig::production(),
telemetry_config: None,
crash_reporting_config: None,
autoupdate_config: None,
mcp_static_config: None,
},