kaizen-cli 0.1.46

Distributable agent observability: real-time-tailable sessions, agile-style retros, and repo-level improvement (Cursor, Claude Code, Codex). SQLite, redact before any sync you enable.
Documentation
use crate::core::event::{Event, EventKind};
use crate::visualization::TraceDetail;
use serde_json::{Value, json};

const MAX_COMMAND_CHARS: usize = 600;

pub(super) fn prepare(detail: &mut TraceDetail) {
    detail.prompt = prompt_from_events(&detail.events)
        .or_else(|| prompt_from_trace(&detail.session.trace_path));
    detail.events.iter_mut().for_each(compact_event);
}

fn compact_event(event: &mut Event) {
    event.payload = event_summary(event)
        .map(|summary| json!({"summary": summary}))
        .unwrap_or(Value::Null);
}

fn event_summary(event: &Event) -> Option<String> {
    (event.kind == EventKind::ToolCall)
        .then(|| payload_summary(&event.payload))
        .flatten()
        .map(|value| compact_text(&value, MAX_COMMAND_CHARS))
}

fn payload_summary(payload: &Value) -> Option<String> {
    pointer_text(
        payload,
        &["/tool_input/command", "/input/command", "/command", "/cmd"],
    )
    .or_else(|| argument_summary(payload))
    .or_else(|| pointer_text(payload, &["/tool_input/file_path", "/input/path", "/path"]))
}

fn argument_summary(payload: &Value) -> Option<String> {
    let raw = pointer_text(payload, &["/arguments", "/function/arguments"])?;
    let parsed: Value = serde_json::from_str(&raw).ok()?;
    pointer_text(&parsed, &["/cmd", "/command", "/path", "/file_path"])
}

fn pointer_text(value: &Value, pointers: &[&str]) -> Option<String> {
    pointers
        .iter()
        .find_map(|pointer| value.pointer(pointer)?.as_str().map(ToOwned::to_owned))
}

fn prompt_from_events(events: &[Event]) -> Option<String> {
    events
        .iter()
        .rev()
        .find_map(|event| crate::core::prompt_text::from_value(&event.payload))
}

fn prompt_from_trace(raw: &str) -> Option<String> {
    super::prompt_cache::from_trace(raw)
}

pub(super) fn prompt_from_line(line: &str) -> Option<String> {
    let value: Value = serde_json::from_str(line).ok()?;
    value
        .get("payload")
        .and_then(crate::core::prompt_text::from_value)
        .or_else(|| crate::core::prompt_text::from_value(&value))
}

fn compact_text(value: &str, limit: usize) -> String {
    let normalized = value.split_whitespace().collect::<Vec<_>>().join(" ");
    let truncated = normalized.chars().take(limit).collect::<String>();
    if normalized.chars().count() > limit {
        format!("{truncated}...")
    } else {
        truncated
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::OsString;
    use std::path::PathBuf;

    #[test]
    fn trusted_trace_returns_latest_user_prompt() {
        let _guard = crate::core::paths::test_lock::global().lock().unwrap();
        let (temp, path, _home) = trace_fixture();
        let line = json!({"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":"<environment_context>hidden</environment_context>"},{"type":"input_text","text":"Show commands"}]}});
        let filler = format!(
            "{}\n",
            json!({"type":"event_msg","payload":{"type":"noise"}})
        )
        .repeat(10_000);
        std::fs::write(&path, format!("{line}\n{filler}")).unwrap();
        assert_eq!(
            prompt_from_trace(path.to_str().unwrap()).as_deref(),
            Some("Show commands")
        );
        drop(temp);
    }

    #[test]
    fn codex_arguments_expose_command_only() {
        let payload = json!({"arguments":"{\"cmd\":\"rg parser src\",\"max_output_tokens\":1000}"});
        assert_eq!(payload_summary(&payload).as_deref(), Some("rg parser src"));
    }

    #[test]
    fn objective_wrapper_returns_user_objective() {
        let wrapped =
            "<codex_internal_context><objective>Fix identity</objective></codex_internal_context>";
        assert_eq!(
            crate::core::prompt_text::from_value(&json!({"prompt": wrapped})).as_deref(),
            Some("Fix identity")
        );
    }

    #[test]
    fn goal_update_wrapper_returns_user_objective() {
        let wrapped = "<codex_internal_context><untrusted_objective>Show prompt</untrusted_objective>Budget: hidden</codex_internal_context>";
        assert_eq!(
            crate::core::prompt_text::from_value(&json!({"prompt": wrapped})).as_deref(),
            Some("Show prompt")
        );
    }

    fn trace_fixture() -> (tempfile::TempDir, PathBuf, HomeGuard) {
        let temp = tempfile::tempdir().unwrap();
        let path = temp.path().join(".codex/sessions/session.jsonl");
        std::fs::create_dir_all(path.parent().unwrap()).unwrap();
        let home = HomeGuard::set(temp.path().into());
        (temp, path, home)
    }

    struct HomeGuard(Option<OsString>);

    impl HomeGuard {
        fn set(value: OsString) -> Self {
            let old = std::env::var_os("HOME");
            unsafe { std::env::set_var("HOME", value) };
            Self(old)
        }
    }

    impl Drop for HomeGuard {
        fn drop(&mut self) {
            match self.0.take() {
                Some(value) => unsafe { std::env::set_var("HOME", value) },
                None => unsafe { std::env::remove_var("HOME") },
            }
        }
    }
}