Skip to content

Commit 3d82354

Browse files
vorporealoz-agent
andauthored
Add basic tracing support for cloud agents. (warpdotdev#12363)
## Description Adds opt-in OpenTelemetry tracing for cloud-agent runs so we can inspect end-to-end execution when debugging latency and failures. When `WARP_CLOUD_AGENT_OTLP_ENDPOINT` is configured, the native app installs an OTLP/HTTP trace exporter; otherwise tracing remains a no-op. Cloud-agent spans are explicitly marked, and filtering at the exporter boundary prevents unrelated application spans from being sent. This also adds spans and events across the cloud-agent lifecycle, server APIs, and driver setup, and propagates the current span through asynchronous tasks and streams. The tracing provider is retained through the standard application shutdown path, with a best-effort active-span registry that ends still-reachable spans before shutting down the provider so long-lived asynchronous work is less likely to be lost. ## Linked Issue None. ## Testing - [x] Manually tested locally with `./script/run -- agent run --prompt "hello world"`. - [x] Ran `./script/format`. - [x] Ran `cargo check -p warp --features gui`. - [x] Ran `cargo doc -p warp --features gui --no-deps`. No automated tests were added; this is opt-in observability plumbing that was validated through the cloud-agent command path and focused compilation/documentation checks. > [!NOTE] > note from dstern: i ended up working with an agent to do an end-to-end test of the fully-authenticated flow with a local cloud agent running via a self-hosted worker host; everything appears to work. ## Agent Mode - [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode CHANGELOG-NONE Co-Authored-By: Oz <oz-agent@warp.dev>
1 parent b34f4ec commit 3d82354

24 files changed

Lines changed: 929 additions & 60 deletions

File tree

Cargo.lock

Lines changed: 109 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,15 @@ notify-debouncer-full = { git = "https://github.com/warpdotdev/notify", rev = "9
198198
nom = "7.1.1"
199199
num-traits = "0.2"
200200
oauth2 = { version = "5.0.0", default-features = false }
201+
opentelemetry = { version = "0.32.0", default-features = false, features = ["trace"] }
202+
opentelemetry-otlp = { version = "0.32.0", default-features = false, features = [
203+
"gzip-http",
204+
"http-proto",
205+
"reqwest-blocking-client",
206+
"trace",
207+
"zstd-http",
208+
] }
209+
opentelemetry_sdk = { version = "0.32.1", default-features = false, features = ["trace"] }
201210
openh264 = "0.8"
202211
static_assertions = "1.1.0"
203212
url = "2.5.4"
@@ -277,6 +286,12 @@ toml_edit = "0.25.5"
277286
tower = "0.5.2"
278287
tower-http = "0.6.6"
279288
tracing = "0.1.40"
289+
tracing-futures = "0.2.5"
290+
tracing-opentelemetry = { version = "0.33.0", default-features = false }
291+
tracing-subscriber = { version = "0.3.22", default-features = false, features = [
292+
"registry",
293+
"std",
294+
] }
280295
arborium = { version = "2", default-features = false, features = [
281296
"lang-rust",
282297
"lang-go",

app/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ tikv-jemallocator = { version = "0.6", optional = true, features = [
213213
toml = "0.8.13"
214214
toml_edit.workspace = true
215215
tracing.workspace = true
216+
tracing-futures = { workspace = true, features = ["futures-03"] }
216217
ui_components.workspace = true
217218
unicase = "2.7.0"
218219
unicode-general-category.workspace = true
@@ -316,8 +317,13 @@ http_server.workspace = true
316317
hyper.workspace = true
317318
libsqlite3-sys = { version = "0.33.0", features = ["bundled"] }
318319
mio = { version = "1.1.1", features = ["os-poll", "os-ext"] }
320+
opentelemetry.workspace = true
321+
opentelemetry-otlp.workspace = true
322+
opentelemetry_sdk.workspace = true
319323
tokio.workspace = true
320324
tokio-util.workspace = true
325+
tracing-opentelemetry.workspace = true
326+
tracing-subscriber.workspace = true
321327

322328
# AWS SDK (loading credentials for BYO LLM)
323329
aws-config = { version = "1.8.16", features = ["credentials-login"] }

app/src/ai/agent_sdk/driver.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,12 @@ impl From<PrepareEnvironmentError> for AgentDriverError {
546546
}
547547

548548
impl AgentDriver {
549+
#[tracing::instrument(name = "AgentDriver::new", skip_all, err, fields(
550+
tags.cloud_agent = true,
551+
task_id = ?options.task_id,
552+
parent_run_id = ?options.parent_run_id,
553+
is_sandbox = tracing::field::Empty,
554+
))]
549555
pub fn new(
550556
options: AgentDriverOptions,
551557
ctx: &mut ModelContext<Self>,
@@ -626,6 +632,7 @@ impl AgentDriver {
626632
// so they allow root execution with permissive flags.
627633
if warp_isolation_platform::detect().is_some() {
628634
env_vars.insert(OsString::from("IS_SANDBOX"), OsString::from("1"));
635+
tracing::Span::current().record("is_sandbox", true);
629636
}
630637

631638
let resolved_env_vars = Arc::new(env_vars);
@@ -1782,6 +1789,7 @@ impl AgentDriver {
17821789
/// Driving the agent mostly requires main-thread UI framework updates, but using `async` and
17831790
/// a `ModelSpawner` lets us express the high-level process linearly rather than in a
17841791
/// series of callbacks and state machine updates.
1792+
#[tracing::instrument(name = "AgentDriver::run_internal", skip_all, err, fields(tags.cloud_agent = true))]
17851793
async fn run_internal(
17861794
task: Task,
17871795
foreground: ModelSpawner<Self>,
@@ -3278,6 +3286,11 @@ impl AgentDriver {
32783286
) {
32793287
match event {
32803288
TerminalDriverEvent::SlowBootstrap => {
3289+
tracing::event!(
3290+
tracing::Level::WARN,
3291+
tags.cloud_agent = true,
3292+
"slow bootstrap"
3293+
);
32813294
eprintln!(
32823295
"Warning: Terminal session is slow to bootstrap. See https://docs.warp.dev/support-and-community/troubleshooting-and-support/known-issues#shells to troubleshoot."
32833296
);
@@ -3286,6 +3299,12 @@ impl AgentDriver {
32863299
session_id,
32873300
join_url,
32883301
} => {
3302+
tracing::event!(
3303+
tracing::Level::INFO,
3304+
tags.cloud_agent = true,
3305+
session_id = %*session_id,
3306+
"shared session established",
3307+
);
32893308
write_session_joined(join_url, self.output_format);
32903309

32913310
// If running as part of a task, store the session-sharing link.
@@ -3369,6 +3388,7 @@ impl AgentDriver {
33693388
/// Invoke the end-of-run snapshot upload pipeline if the feature flag is enabled and this
33703389
/// driver is associated with a cloud task. Errors are logged internally; this helper always
33713390
/// returns so cleanup can proceed.
3391+
#[tracing::instrument(skip_all, fields(tags.cloud_agent = true))]
33723392
async fn run_snapshot_upload(spawner: &ModelSpawner<Self>) {
33733393
if !FeatureFlag::OzHandoff.is_enabled() {
33743394
return;

app/src/ai/agent_sdk/driver/environment.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ pub(super) async fn clone_repos(
378378

379379
/// Clone a GitHub repository to `{working_dir}/{repo.repo}` if it does not already exist.
380380
/// This only performs the clone -- it does NOT register the repo with `DetectedRepositories`.
381+
#[tracing::instrument(skip_all, err, fields(tags.cloud_agent = true, repo = %repo))]
381382
pub(super) async fn clone_repo(
382383
repo: &GithubRepo,
383384
working_dir: &Path,
@@ -438,6 +439,7 @@ pub(super) async fn clone_repo(
438439

439440
/// Register a cloned GitHub repository with `DetectedRepositories` so that the
440441
/// skill watcher and other repo-aware subsystems can discover it.
442+
#[tracing::instrument(skip_all, err, fields(tags.cloud_agent = true, repo = %repo, is_sandbox = is_sandbox))]
441443
pub(super) async fn register_cloned_repo(
442444
repo: &GithubRepo,
443445
working_dir: &Path,
@@ -546,6 +548,7 @@ async fn subscribe_to_codebase_index_events(
546548
.map_err(|_| PrepareEnvironmentError::InvalidRuntimeState)
547549
}
548550

551+
#[tracing::instrument(skip_all, err, fields(tags.cloud_agent = true, repo = %repo_name))]
549552
async fn index_repo_codebase(
550553
repo_name: &str,
551554
working_dir: &Path,

app/src/ai/agent_sdk/driver/git_credentials.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ pub(crate) fn configure_git_identity(credentials: &[GitCredential]) {
248248
/// Returns `Ok(())` on success (including when the server returns no
249249
/// credentials). Returns `Err` when the workload-token issuance or the server
250250
/// API call fails — these are transient failures worth retrying.
251+
#[tracing::instrument(name = "git_credentials::try_refresh", skip_all, err, fields(
252+
tags.cloud_agent = true,
253+
task_id,
254+
))]
251255
async fn try_refresh(task_id: &str, ai_client: &Arc<dyn AIClient>) -> Result<()> {
252256
let workload_token =
253257
warp_isolation_platform::issue_workload_token(Some(Duration::from_secs(5 * 60)))

app/src/ai/agent_sdk/driver/terminal.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ use std::task::{Context, Poll};
88
use std::time::Duration;
99

1010
use futures::channel::oneshot;
11+
use futures::TryFutureExt as _;
1112
use session_sharing_protocol::common::{Role, SessionId};
1213
use session_sharing_protocol::sharer::SessionRetentionReason;
14+
use tracing::Instrument as _;
1315
use warp_cli::share::{ShareAccessLevel, ShareRequest, ShareSubject};
1416
use warp_completer::completer::CommandOutput;
1517
use warp_core::command::ExitCode;
@@ -547,11 +549,17 @@ impl TerminalDriver {
547549
session_bootstrapped
548550
.wait()
549551
.with_timeout(TERMINAL_SESSION_BOOTSTRAP_TIMEOUT)
550-
.await
551-
.map_err(|_| {
552+
.map_err(|err| {
552553
log::error!("Timed out waiting for session bootstrap");
554+
tracing::error!(error = %err);
555+
553556
AgentDriverError::BootstrapFailed
554557
})
558+
.instrument(tracing::info_span!(
559+
"wait_for_session_bootstrapped",
560+
tags.cloud_agent = true
561+
))
562+
.await
555563
}
556564
}
557565

0 commit comments

Comments
 (0)