Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to this project will be documented in this file.

## [Unreleased]

### Removed

- **Perf telemetry**: Remove the obsolete `perf` Cargo feature, `--enable-perf`, `--perf-log`, and `--perf-append` flags, and high-frequency telemetry sidecars. Runtime diagnostics remain available through the existing logging flags and presets. Doctor reports and debug bundles no longer advertise perf paths; the debug-bundle manifest schema is now `claude-rs-debug-bundle/v2`.

## [0.14.9] - 2026-10-01 [Changes][v0.14.9]

### Features
Expand Down
3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ uuid = { version = "1.23.2", features = ["v4"] }
which = "8.0.2"
zip = { version = "8.6.0", default-features = false, features = ["deflate", "time"] }

[features]
perf = []

[profile.dev]
debug = "line-tables-only"
incremental = false
Expand Down
1 change: 0 additions & 1 deletion agent-sdk/src/bridge/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const LOG_TARGETS = {
APP_NETWORK: "app.network",
APP_INPUT: "app.input",
APP_PASTE: "app.paste",
APP_PERF: "app.perf",
APP_RENDER: "app.render",
APP_SESSION: "app.session",
APP_TOOL: "app.tool",
Expand Down
2 changes: 1 addition & 1 deletion docs/src/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Important Rust areas:

| Area | Responsibility |
| --- | --- |
| `src/main.rs` | Process entrypoint, runtime setup, logging/perf setup, and exit behavior. |
| `src/main.rs` | Process entrypoint, runtime setup, logging setup, and exit behavior. |
| `src/lib.rs` | CLI arguments, subcommands, and diagnostics presets. |
| `src/agent/` | Bridge process resolution, NDJSON client, wire types, and bridge error handling. |
| `src/app/` | App state, lifecycle, sessions, config, permissions, input, slash commands, plugins, MCP, usage, and trust. |
Expand Down
28 changes: 4 additions & 24 deletions docs/src/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Available presets:
| --- | --- |
| `runtime` | Debugging general app, bridge, session, tool, permission, network, and update flow. |
| `session` | Debugging session startup, permission, and command flow. |
| `render` | Debugging rendering, cache, input, paste, and perf-adjacent UI behavior. |
| `render` | Debugging rendering, cache, input, and paste behavior. |
| `bridge` | Debugging Agent SDK bridge lifecycle, protocol, SDK, permission, and MCP behavior. |
| `full` | Capturing the broadest diagnostic trace. |

Expand Down Expand Up @@ -123,7 +123,7 @@ claude-rs logs --path
claude-rs logs --latest
```

`claude-rs logs` prints the runtime log directory, legacy log path, perf log directory, latest discovered log, and common follow-up commands. `--path` prints only the default runtime log directory for scripts. `--latest` prints only the latest runtime log path, falling back to the legacy shared log when no timestamped runtime log exists.
`claude-rs logs` prints the runtime log directory, legacy log path, latest discovered log, and common follow-up commands. `--path` prints only the default runtime log directory for scripts. `--latest` prints only the latest runtime log path, falling back to the legacy shared log when no timestamped runtime log exists.

To inspect recent log output safely:

Expand All @@ -143,6 +143,8 @@ claude-rs logs --bundle --yes

Without `--yes`, an interactive terminal is prompted before the bundle is written. Use `--output <PATH>` to choose the ZIP path.

The manifest uses schema `claude-rs-debug-bundle/v2`. Diagnostics paths describe the runtime log directory and legacy log path.

The bundle includes:

- `manifest.json`
Expand Down Expand Up @@ -185,28 +187,6 @@ CLAUDE_RS_AGENT_BRIDGE_RUNTIME=/path/to/bun

Release npm installs use the root package launcher to start the native platform binary and point it at the bundled bridge script. The native binary resolves the private `claude-rs-bridge-bun` executable from the installed platform package. In `doctor --json`, the runtime checks are reported as `bridge_runtime`, `bridge_runtime_version`, and `bridge_script`.

## Perf Telemetry

Perf telemetry is a separate JSON-lines sidecar intended for high-frequency render and layout samples. It requires a binary built with the `perf` feature.

From source:

```bash
cargo run --features perf -- --enable-perf
cargo run --features perf -- --perf-log claude-rs-perf.log
```

For an already-built perf-enabled binary:

```bash
claude-rs --enable-perf
claude-rs --perf-log claude-rs-perf.log
```

If the binary was not built with `--features perf`, perf flags are rejected at startup.

When `--perf-log` is omitted, default perf telemetry uses timestamped JSON-lines files under the sibling `logs/perf/` directory.

## Useful Issue Reports

Include:
Expand Down
5 changes: 1 addition & 4 deletions docs/src/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,8 @@ The installed `claude-rs --help` command exposes these options:
| `--log-file <PATH>` | Write tracing diagnostics to a specific file. |
| `--log-filter <FILTER>` | Use explicit tracing filter directives. |
| `--log-append` | Append to the active log file instead of resetting it on startup. |
| `--enable-perf` | Enable perf telemetry when the binary was built with the `perf` feature. |
| `--perf-log <PATH>` | Write high-frequency perf telemetry to a specific JSON-lines file. |
| `--perf-append` | Append to the perf log instead of truncating it. |

See [Diagnostics](diagnostics.md) before enabling verbose logs or perf telemetry.
See [Diagnostics](diagnostics.md) before enabling verbose logs.

## Core UI

Expand Down
47 changes: 0 additions & 47 deletions src/app/connect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,50 +81,6 @@ pub fn create_app(cli: &Cli) -> App {

let (event_tx, event_rx) = mpsc::channel(CLIENT_EVENT_QUEUE_CAPACITY);
let (file_index_event_tx, file_index_event_rx) = super::file_index::event_channel();
let perf_path = match crate::logging::resolve_perf_path(cli) {
Ok(path) => path,
Err(err) => {
tracing::warn!(
target: crate::logging::targets::APP_PERF,
event_name = "perf_telemetry_unavailable",
message = "failed to resolve perf telemetry sidecar path",
outcome = "failure",
telemetry_channel = "perf_sidecar",
perf_schema = "claude-rs-perf/v1",
perf_append = cli.perf_append,
error = %err,
);
None
}
};
let perf = perf_path.as_deref().and_then(|path| {
let logger = crate::perf::PerfLogger::open(path, cli.perf_append);
if logger.is_some() {
tracing::info!(
target: crate::logging::targets::APP_PERF,
event_name = "perf_telemetry_enabled",
message = "perf telemetry sidecar enabled",
outcome = "success",
telemetry_channel = "perf_sidecar",
perf_schema = "claude-rs-perf/v1",
perf_log = %path.display(),
perf_append = cli.perf_append,
);
} else {
tracing::warn!(
target: crate::logging::targets::APP_PERF,
event_name = "perf_telemetry_unavailable",
message = "failed to enable perf telemetry sidecar",
outcome = "failure",
telemetry_channel = "perf_sidecar",
perf_schema = "claude-rs-perf/v1",
perf_log = %path.display(),
perf_append = cli.perf_append,
);
}
logger
});

let loaded_settings = match settings::load_global_settings(env!("CARGO_PKG_VERSION")) {
Ok(loaded) => loaded,
Err(err) => {
Expand Down Expand Up @@ -213,13 +169,10 @@ pub fn create_app(cli: &Cli) -> App {
usage: super::UsageState::default(),
mcp: super::McpState::default(),
notifications: super::notify::NotificationManager::new(),
perf,
render_cache_budget: RenderCacheBudget::default(),
history_retention: HistoryRetentionPolicy::default(),
history_retention_stats: HistoryRetentionStats::default(),
cache_metrics: CacheMetrics::default(),
fps_ema: None,
last_frame_at: None,
startup: StartupState::new(
cli.bridge_script.clone(),
match &cli.command {
Expand Down
3 changes: 0 additions & 3 deletions src/app/connect/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ fn create_app_defers_file_index_and_routes_untrusted_cwd_to_trust_surface() {
log_file: None,
log_filter: None,
log_append: false,
enable_perf: false,
perf_log: None,
perf_append: false,
};

let app = super::create_app(&cli);
Expand Down
16 changes: 2 additions & 14 deletions src/app/events/streaming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,24 +64,14 @@ pub(super) fn append_agent_stream_text(
blocks.push(new_text_block(chunk.to_owned(), source_message_uuid));
}

let split_count = split_tail_text_block(blocks);
if split_count > 0 {
crate::perf::mark_with("text_block_split_count", "count", split_count);
}

if let Some(MessageBlock::Text(block)) = blocks.last() {
crate::perf::mark_with("text_block_active_tail_bytes", "bytes", block.text.len());
}
let text_block_count = blocks.iter().filter(|b| matches!(b, MessageBlock::Text(..))).count();
crate::perf::mark_with("text_block_frozen_count", "count", text_block_count.saturating_sub(1));
split_tail_text_block(blocks);
}

fn new_text_block(text: String, source_message_uuid: Option<&str>) -> MessageBlock {
MessageBlock::Text(TextBlock::new(text).with_source_message_uuid(source_message_uuid))
}

fn split_tail_text_block(blocks: &mut Vec<MessageBlock>) -> usize {
let mut split_count = 0usize;
fn split_tail_text_block(blocks: &mut Vec<MessageBlock>) {
loop {
let Some(tail_idx) = blocks.len().checked_sub(1) else {
break;
Expand Down Expand Up @@ -115,9 +105,7 @@ fn split_tail_text_block(blocks: &mut Vec<MessageBlock>) -> usize {
.with_source_message_uuids(source_message_uuids.clone()),
);
blocks.insert(tail_idx, completed_text_block(completed, split, source_message_uuids));
split_count += 1;
}
split_count
}

fn completed_text_block(
Expand Down
2 changes: 0 additions & 2 deletions src/app/events/tool_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,6 @@ fn update_existing_tool_call(app: &mut App, mi: usize, bi: usize, tool_info: &To
if changed {
existing.invalidate_render_cache();
layout_dirty = true;
} else {
crate::perf::mark("tool_update_noop_skips");
}
}
if layout_dirty {
Expand Down
2 changes: 0 additions & 2 deletions src/app/events/tool_updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ fn apply_tool_call_update_to_indexed_block(
tc.invalidate_render_cache();
app.sync_render_cache_slot(mi, bi);
out.layout_dirty_idx = Some(mi);
} else {
crate::perf::mark("tool_update_noop_skips");
}
}

Expand Down
15 changes: 1 addition & 14 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,20 +308,7 @@ async fn run_tui_loop(
events::reconcile_terminal_size(app, width, height);
terminal_runtime.apply_surface_rebuilds(app)?;
if app.surface_dirty.active_surface_needs_draw(app.terminal_lifecycle) {
if let Some(ref mut perf) = app.perf {
perf.next_frame();
}
if app.perf.is_some() {
app.mark_frame_presented(Instant::now());
}
#[allow(clippy::drop_non_drop)]
{
let timer = app.perf.as_ref().map(|p| p.start("frame_total"));
let draw_timer = app.perf.as_ref().map(|p| p.start("frame::terminal_draw"));
terminal_runtime.draw_active_surface(app)?;
drop(draw_timer);
drop(timer);
}
terminal_runtime.draw_active_surface(app)?;
}

if app.shutdown_requested() {
Expand Down
11 changes: 0 additions & 11 deletions src/app/state/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,6 @@ pub struct App {

/// Central notification manager (bell + desktop toast when unfocused).
pub(crate) notifications: notify::NotificationManager,
/// Performance logger. Present only when built with `--features perf`.
/// Taken out (`Option::take`) during render, used, then put back to avoid
/// borrow conflicts with `&mut App`.
pub(crate) perf: Option<crate::perf::PerfLogger>,
/// Global in-memory budget for rendered block and message caches.
pub(crate) render_cache_budget: RenderCacheBudget,
/// Byte budget for source conversation history retained in memory.
Expand All @@ -109,10 +105,6 @@ pub struct App {
pub(crate) history_retention_stats: HistoryRetentionStats,
/// Cross-cutting cache metrics accumulator (enforcement counts, watermarks, rate limits).
pub(crate) cache_metrics: CacheMetrics,
/// Smoothed frames-per-second (EMA of presented frame cadence).
pub(crate) fps_ema: Option<f32>,
/// Timestamp of the previous presented frame.
pub(crate) last_frame_at: Option<Instant>,
/// Bootstrap sequencing state resolved from CLI flags at launch.
pub(crate) startup: StartupState,
/// Owned bridge-process task and its explicit shutdown signal.
Expand Down Expand Up @@ -274,13 +266,10 @@ impl App {
usage: UsageState::default(),
mcp: McpState::default(),
notifications: notify::NotificationManager::new(),
perf: None,
render_cache_budget: RenderCacheBudget::default(),
history_retention: HistoryRetentionPolicy::default(),
history_retention_stats: HistoryRetentionStats::default(),
cache_metrics: CacheMetrics::default(),
fps_ema: None,
last_frame_at: None,
startup: StartupState::default(),
bridge_task: None,
}
Expand Down
6 changes: 0 additions & 6 deletions src/app/state/history_retention.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,12 +581,6 @@ impl super::App {
stats.total_dropped_messages = self.history_retention_stats.total_dropped_messages;
stats.total_dropped_bytes = self.history_retention_stats.total_dropped_bytes;

crate::perf::mark_with("history::bytes_before", "bytes", stats.total_before_bytes);
crate::perf::mark_with("history::bytes_after", "bytes", stats.total_after_bytes);
crate::perf::mark_with("history::dropped_messages", "count", stats.dropped_messages);
crate::perf::mark_with("history::dropped_bytes", "bytes", stats.dropped_bytes);
crate::perf::mark_with("history::total_dropped", "count", stats.total_dropped_messages);

stats
}

Expand Down
21 changes: 0 additions & 21 deletions src/app/state/repaint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,6 @@ impl App {
}
}

/// Mark one presented frame at `now`, updating smoothed FPS.
pub fn mark_frame_presented(&mut self, now: Instant) {
let Some(prev) = self.last_frame_at.replace(now) else {
return;
};
let dt = now.saturating_duration_since(prev).as_secs_f32();
if dt <= f32::EPSILON {
return;
}
let fps = (1.0 / dt).clamp(0.0, 240.0);
self.fps_ema = Some(match self.fps_ema {
Some(current) => current * 0.9 + fps * 0.1,
None => fps,
});
}

#[must_use]
pub fn frame_fps(&self) -> Option<f32> {
self.fps_ema
}

pub fn invalidate_layout(&mut self, _level: LayoutInvalidation) {
self.chat_render.clear_measurements();
self.chat_render.invalidate_live_anchor();
Expand Down
2 changes: 0 additions & 2 deletions src/app/state/tool_call_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,7 @@ impl ToolCallInfo {

/// Invalidate cached rendered lines for this tool call.
pub fn invalidate_render_cache(&mut self) {
crate::perf::mark("tc_invalidations_requested");
self.cache.invalidate();
crate::perf::mark("tc_invalidations_applied");
}

pub fn set_raw_input(&mut self, raw_input: Option<serde_json::Value>) -> bool {
Expand Down
3 changes: 0 additions & 3 deletions src/cli/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,9 +824,6 @@ mod tests {
log_file: None,
log_filter: None,
log_append: false,
enable_perf: false,
perf_log: None,
perf_append: false,
}
}
}
8 changes: 1 addition & 7 deletions src/cli/doctor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ impl DoctorSection {
"config_settings" | "config_local_settings" | "config_preferences" | "config_paths" => {
Self::Configuration
}
"runtime_log_dir" | "legacy_log_path" | "perf_log_dir" => Self::Logs,
"runtime_log_dir" | "legacy_log_path" => Self::Logs,
"npm_root_package" | "npm_platform_package" => Self::Npm,
"claude_credentials" => Self::Credentials,
_ => Self::Overview,
Expand Down Expand Up @@ -560,12 +560,6 @@ fn log_path_checks() -> Vec<DoctorCheck> {
crate::logging::default_legacy_log_path(),
false,
),
result_path_check(
"perf_log_dir",
"Perf log directory",
crate::logging::default_perf_log_dir(),
false,
),
]
}

Expand Down
Loading
Loading