Skip to main content

agentwatch_core/detection/
grouping.rs

1// ── Session grouping ─────────────────────────────────────────────────────────
2// Generates session keys for clustering related processes on the backend.
3// Replaces the React-side HOST_TO_AGENT fallback with a vendor-agnostic
4// session linker (Phase 5 of the pair-review plan).
5
6/// Map of host-app display names → primary agent IDs they host.
7/// Used to normalize session keys so subagents/helpers share the same key
8/// as their parent main session.
9fn resolve_parent_agent<'a>(agent_id: &'a str, host_app: Option<&str>) -> &'a str {
10    match host_app {
11        Some("Claude Code")      => "claude-code",
12        Some("Codex App")        => "codex-app",
13        Some("Cursor")           => "cursor-agent",
14        Some("VS Code")          => "vscode-agent",
15        Some("VS Code / Cursor") => "vscode-agent",
16        Some("Windsurf")         => "windsurf",
17        _                        => agent_id,
18    }
19}
20
21/// Generate a session key for a process.
22///
23/// Processes with a `host_app` are normalized to their parent agent's ID
24/// so they cluster together. E.g. a claude-subagent working in `/proj`
25/// gets key `"claude-code:/proj"`, matching its parent main session.
26///
27/// Falls back to `agent_id:pid:<parent_pid>` when cwd is unavailable.
28pub fn generate_session_key(
29    agent_id:   &str,
30    cwd:        &str,
31    parent_pid: Option<u32>,
32    host_app:   Option<&str>,
33) -> String {
34    let resolved = resolve_parent_agent(agent_id, host_app);
35    if cwd.is_empty() {
36        format!("{}:pid:{}", resolved, parent_pid.unwrap_or(0))
37    } else {
38        format!("{}:{}", resolved, cwd)
39    }
40}