Suppresses hints that were already emitted for this session. The `(session_id, category)` pairs are persisted in `.tracedecay/tool_hints_seen.json` so each hint category surfaces at most once per Cursor session across short-lived hook processes. Hints are also suppressed entirely when the workspace has no tracedecay index (suggesting tracedecay tools there would be misleading). When no session id
(event_json: &str, hint_id: &str, hint: ToolHint)
| 400 | /// the hint is emitted as-is — dedupe is impossible but the hint is still |
| 401 | /// useful (fail-open). |
| 402 | fn deduped_cursor_hint(event_json: &str, hint_id: &str, hint: ToolHint) -> Option<ToolHint> { |
| 403 | let Ok(parsed) = serde_json::from_str::<Value>(event_json) else { |
| 404 | // The candidate was already recorded against `hint_id`; close its outcome |
| 405 | // with a terminal event instead of dropping it silently. |
| 406 | record_hint_analytics( |
| 407 | None, |
| 408 | "dropped_no_root", |
| 409 | HintAgent::Cursor, |
| 410 | None, |
| 411 | hint_id, |
| 412 | &hint, |
| 413 | ); |
| 414 | return None; |
| 415 | }; |
| 416 | let session_id = event_session_id(&parsed); |
| 417 | let Some(root) = cursor_project_root_candidate_from_parsed_event(&parsed) else { |
| 418 | record_hint_analytics( |
| 419 | None, |
| 420 | "dropped_no_root", |
| 421 | HintAgent::Cursor, |
| 422 | session_id.as_deref(), |
| 423 | hint_id, |
| 424 | &hint, |
| 425 | ); |
| 426 | return None; |
| 427 | }; |
| 428 | if !crate::tracedecay::TraceDecay::is_initialized(&root) { |
| 429 | record_hint_analytics( |
| 430 | Some(&root), |
| 431 | "suppressed_uninitialized", |
| 432 | HintAgent::Cursor, |
| 433 | session_id.as_deref(), |
| 434 | hint_id, |
| 435 | &hint, |
| 436 | ); |
| 437 | return None; |
| 438 | } |
| 439 | deduped_project_hint_with_id(Some(root), HintAgent::Cursor, session_id, hint_id, hint) |
| 440 | } |
| 441 | |
| 442 | pub fn cursor_project_root_from_event(event_json: &str) -> Option<PathBuf> { |
| 443 | let parsed: Value = serde_json::from_str(event_json).ok()?; |
no test coverage detected