Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
71 changes: 43 additions & 28 deletions tools/wta/src/agent_hooks_installer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ fn install_for_claude(home: &Path) {
tracing::warn!(
target: "agent_hooks",
err = %e,
path = %settings_path.display(),
path = %crate::logging::redact_user_path(&settings_path),
"failed to strip legacy wta hooks from settings.json; non-fatal",
);
}
Expand Down Expand Up @@ -742,8 +742,8 @@ fn maybe_stage_bundle_for_codex(source: &Path) -> Option<PathBuf> {
Ok(()) => {
tracing::info!(
target: "agent_hooks",
source = %source.display(),
staged = %staged.display(),
source = %crate::logging::redact_user_path(&source),
staged = %crate::logging::redact_user_path(&staged),
"restaged codex bundle out of WindowsApps",
);
Some(staged)
Expand All @@ -752,8 +752,8 @@ fn maybe_stage_bundle_for_codex(source: &Path) -> Option<PathBuf> {
tracing::warn!(
target: "agent_hooks",
err = %e,
source = %source.display(),
staged = %staged.display(),
source = %crate::logging::redact_user_path(&source),
staged = %crate::logging::redact_user_path(&staged),
"failed to restage codex bundle out of WindowsApps; using original path",
);
None
Expand Down Expand Up @@ -803,7 +803,7 @@ fn install_for_copilot(home: &Path) {
tracing::warn!(
target: "copilot_hooks",
err = %e,
path = %settings_path.display(),
path = %crate::logging::redact_user_path(&settings_path),
"failed to clean up stale wt-local marketplace entry; non-fatal",
);
}
Expand Down Expand Up @@ -852,13 +852,13 @@ fn install_for_copilot(home: &Path) {
tracing::warn!(
target: "copilot_hooks",
err = %e,
path = %stale.display(),
path = %crate::logging::redact_user_path(&stale),
"failed to remove stale _direct folder; non-fatal",
);
} else {
tracing::info!(
target: "copilot_hooks",
path = %stale.display(),
path = %crate::logging::redact_user_path(&stale),
"removed stale _direct plugin folder",
);
}
Expand Down Expand Up @@ -2078,19 +2078,29 @@ fn run_plugin_cli_capture_with_env(
tracing::warn!(
target: "agent_hooks",
exe = exe,
status = ?output.status.code(),
"plugin CLI returned non-zero exit",
);
tracing::trace!(
target: "agent_hooks.content",
exe = exe,
args = ?args,
stdout = %stdout.trim(),
stderr = %stderr.trim(),
status = ?output.status.code(),
"plugin CLI returned non-zero exit",
"plugin CLI non-zero output",
);
} else {
tracing::info!(
target: "agent_hooks",
exe = exe,
"plugin CLI succeeded",
);
tracing::trace!(
target: "agent_hooks.content",
exe = exe,
args = ?args,
stdout = %stdout.trim(),
"plugin CLI succeeded",
"plugin CLI success output",
);
}
Ok(CliRunOutcome {
Expand Down Expand Up @@ -2148,9 +2158,14 @@ fn run_plugin_cli_with_env(
tracing::info!(
target: "agent_hooks",
exe = exe,
args = ?args,
"plugin CLI exited non-zero but matched idempotency substring; treating as success",
);
tracing::trace!(
target: "agent_hooks.content",
exe = exe,
args = ?args,
"plugin CLI idempotency-match args",
);
return Ok(());
}
return Err(std::io::Error::new(
Expand Down Expand Up @@ -2231,8 +2246,8 @@ fn maybe_stage_bundle_for_claude(source: &Path) -> Option<PathBuf> {
Ok(()) => {
tracing::info!(
target: "agent_hooks",
source = %source.display(),
staged = %staged.display(),
source = %crate::logging::redact_user_path(&source),
staged = %crate::logging::redact_user_path(&staged),
"staged claude bundle out of WindowsApps to sidestep Node.js cpSync EPERM",
);
Some(staged)
Expand All @@ -2241,8 +2256,8 @@ fn maybe_stage_bundle_for_claude(source: &Path) -> Option<PathBuf> {
tracing::warn!(
target: "agent_hooks",
err = %e,
source = %source.display(),
staged = %staged.display(),
source = %crate::logging::redact_user_path(&source),
staged = %crate::logging::redact_user_path(&staged),
"failed to stage claude bundle under LOCALAPPDATA; \
falling back to WindowsApps source (claude plugin install \
may fail with EPERM)",
Expand Down Expand Up @@ -2313,7 +2328,7 @@ fn cleanup_legacy_claude_hooks(settings_path: &Path) -> std::io::Result<()> {
tracing::warn!(
target: "agent_hooks",
err = %e,
path = %settings_path.display(),
path = %crate::logging::redact_user_path(&settings_path),
"settings.json malformed; leaving untouched",
);
return Ok(());
Expand Down Expand Up @@ -2362,7 +2377,7 @@ fn cleanup_legacy_claude_hooks(settings_path: &Path) -> std::io::Result<()> {
fs::write(settings_path, serialized)?;
tracing::info!(
target: "agent_hooks",
path = %settings_path.display(),
path = %crate::logging::redact_user_path(&settings_path),
"stripped legacy wta hooks block",
);
Ok(())
Expand Down Expand Up @@ -2446,7 +2461,7 @@ fn cleanup_stale_copilot_marketplace(
tracing::warn!(
target: "copilot_hooks",
err = %e,
path = %settings_path.display(),
path = %crate::logging::redact_user_path(&settings_path),
"settings.json malformed; leaving untouched",
);
return Ok(());
Expand Down Expand Up @@ -2500,9 +2515,9 @@ fn cleanup_stale_copilot_marketplace(
fs::write(settings_path, serialized)?;
tracing::info!(
target: "copilot_hooks",
path = %settings_path.display(),
old = %old_path,
new = %expected_str,
path = %crate::logging::redact_user_path(&settings_path),
old = %crate::logging::redact_user_path(&old_path),
new = %crate::logging::redact_user_path(&expected_str),
"rewrote stale wt-local marketplace path",
);
Ok(())
Expand Down Expand Up @@ -3181,7 +3196,7 @@ fn save_upgrade_state(path: &Path, state: &UpgradeState) {
tracing::warn!(
target: "agent_hooks",
err = %e,
path = %parent.display(),
path = %crate::logging::redact_user_path(&parent),
"failed to create upgrade-state parent dir",
);
return;
Expand All @@ -3198,7 +3213,7 @@ fn save_upgrade_state(path: &Path, state: &UpgradeState) {
tracing::warn!(
target: "agent_hooks",
err = %e,
path = %path.display(),
path = %crate::logging::redact_user_path(&path),
"failed to write upgrade-state file",
);
}
Expand Down Expand Up @@ -3232,7 +3247,7 @@ fn cleanup_stale_claude_marketplace(
tracing::warn!(
target: "agent_hooks",
err = %e,
path = %known_path.display(),
path = %crate::logging::redact_user_path(&known_path),
"known_marketplaces.json malformed; leaving untouched",
);
return Ok(());
Expand Down Expand Up @@ -3290,9 +3305,9 @@ fn cleanup_stale_claude_marketplace(
fs::write(known_path, serialized)?;
tracing::info!(
target: "agent_hooks",
path = %known_path.display(),
old = %old_path,
new = %expected_str,
path = %crate::logging::redact_user_path(&known_path),
old = %crate::logging::redact_user_path(&old_path),
new = %crate::logging::redact_user_path(&expected_str),
"rewrote stale wt-local marketplace path (claude)",
);
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion tools/wta/src/agent_pane_origin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub fn append_default(session_id: &str, pane_session_id: Option<&str>) {
tracing::warn!(
target: "agent_pane_origin",
session_id = %session_id,
path = %path.display(),
path = %crate::logging::redact_user_path(&path),
error = %err,
"failed to append origin record",
);
Expand Down
39 changes: 34 additions & 5 deletions tools/wta/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3131,9 +3131,14 @@ impl App {
tracing::warn!(
target: "agents_view",
key = %key,
cwd = %raw_cwd_string,
"dispatch_resume: stored cwd is no longer a valid directory; falling back to profile default",
);
tracing::trace!(
target: "agents_view.content",
key = %key,
cwd = %raw_cwd_string,
"dispatch_resume: invalid stored cwd",
);
}
let short_key: String = key.chars().take(8).collect();
let launch_commandline = format!(
Expand Down Expand Up @@ -3186,9 +3191,15 @@ impl App {
cli = %cli_id,
commandline = %commandline,
launch_commandline = %launch_commandline,
cwd = %cwd_string,
has_cwd = !cwd_string.is_empty(),
"dispatch_resume: new-tab scheduled",
);
tracing::trace!(
target: "agents_view.content",
key = %key,
cwd = %cwd_string,
"dispatch_resume: new-tab cwd",
);

#[cfg(test)]
{
Expand Down Expand Up @@ -3331,9 +3342,14 @@ impl App {
tracing::warn!(
target: "agents_view",
key = %key,
cwd = %raw_cwd_string,
"dispatch_resume_in_agent_pane: stored cwd is no longer a valid directory; omitting from resume_in_new_agent_tab event",
);
tracing::trace!(
target: "agents_view.content",
key = %key,
cwd = %raw_cwd_string,
"dispatch_resume_in_agent_pane: invalid stored cwd",
);
}
let cwd_string = valid_cwd.unwrap_or_default();

Expand All @@ -3359,9 +3375,15 @@ impl App {
tracing::info!(
target: "agents_view",
key = %s.key,
cwd = %cwd_string,
has_cwd = !cwd_string.is_empty(),
"dispatch_resume_in_agent_pane: resume_in_new_agent_tab event published",
);
tracing::trace!(
target: "agents_view.content",
key = %s.key,
cwd = %cwd_string,
"dispatch_resume_in_agent_pane: resume cwd",
);

#[cfg(test)]
{
Expand Down Expand Up @@ -5423,9 +5445,16 @@ impl App {
target: "acp_load_session",
tab_id,
session_id,
cwd = ?cwd,
has_cwd = cwd.is_some(),
"inbound load_session event from WT"
);
tracing::trace!(
target: "acp_load_session.content",
tab_id,
session_id,
cwd = ?cwd,
"inbound load_session cwd"
);
if tab_id.is_empty() || session_id.is_empty() {
tracing::warn!(
target: "acp_load_session",
Expand Down
61 changes: 61 additions & 0 deletions tools/wta/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,43 @@ pub(crate) fn default_filter_directive(debug_assertions: bool) -> &'static str {
}
}

/// Replace the current user's home / app-data prefixes in a path with stable
/// placeholders, so fixed tool / runtime paths can be logged at default levels
/// without leaking the Windows username.
///
/// Only the user-profile prefix is personal in these paths (e.g.
/// `C:\Users\<name>\AppData\Local\…\config.json`); the rest is a fixed,
/// non-personal tool layout that stays useful for diagnostics. Use this for
/// *known* tool/runtime paths only — for arbitrary paths the agent reads or
/// writes (which can reveal user file/folder names beyond the prefix), keep
/// the full path on a trace-only `*.content` target instead.
///
/// Prefixes are tried most-specific first (`LOCALAPPDATA` is itself under
/// `USERPROFILE`) and matched case-insensitively (Windows paths).
pub(crate) fn redact_user_path(path: impl AsRef<std::path::Path>) -> String {
let s = path.as_ref().to_string_lossy().into_owned();
for (var, placeholder) in [
("LOCALAPPDATA", "%LOCALAPPDATA%"),
("APPDATA", "%APPDATA%"),
("USERPROFILE", "%USERPROFILE%"),
] {
if let Some(prefix) = std::env::var_os(var) {
let prefix = prefix.to_string_lossy();
if prefix.is_empty() {
continue;
}
// `get(..len)` is None when `len` is not a char boundary, so this
// never panics on a non-ASCII username.
if let Some(head) = s.get(..prefix.len()) {
if head.eq_ignore_ascii_case(&prefix) {
return format!("{placeholder}{}", &s[prefix.len()..]);
}
}
Comment thread
vanzue marked this conversation as resolved.
Outdated
}
Comment thread
vanzue marked this conversation as resolved.
Outdated
}
s
}

/// Root of the WTA log tree: `<local_root>/logs` (or a temp-dir fallback).
fn logs_root() -> std::path::PathBuf {
crate::runtime_paths::intelligent_terminal_local_root()
Expand Down Expand Up @@ -266,6 +303,30 @@ mod tests {
assert_eq!(default_filter_directive(false), "info");
}

#[test]
fn redact_user_path_strips_localappdata_prefix() {
let local = std::env::var_os("LOCALAPPDATA");
if let Some(local) = local {
let local = local.to_string_lossy().into_owned();
let full = std::path::Path::new(&local)
.join("IntelligentTerminal")
.join("master-pipe.txt");
let redacted = redact_user_path(&full);
assert!(
redacted.starts_with("%LOCALAPPDATA%"),
"expected placeholder prefix, got: {redacted}",
);
assert!(!redacted.contains(&local), "raw prefix leaked: {redacted}");
assert!(redacted.ends_with("master-pipe.txt"));
}
}

#[test]
fn redact_user_path_leaves_unrelated_paths_untouched() {
let p = std::path::Path::new(r"D:\unrelated\proj\file.rs");
assert_eq!(redact_user_path(p), r"D:\unrelated\proj\file.rs");
}

#[test]
fn release_default_filter_enables_info() {
// The EnvFilter built from the release default must enable info (and
Expand Down
Loading
Loading