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}