1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//! Cursor hook I/O protocol boundary (GH-823).
//!
//! This module owns the shared Cursor stdin/payload contract for the
//! `context`, `observe`, and `summarize` hook commands. Command entrypoints
//! only dispatch and adapt; every Cursor payload is read through the bounded
//! byte reader, sanitized (PII such as `user_email` is dropped at the outer
//! boundary and never copied into canonical events), and validated fail-closed
//! against the payload shapes observed on Cursor 3.12.17 (PR #914 evidence).
//!
//! Frozen decisions (issue #823 SP823-T2 approval record, 2026-07-24):
//! - `CURSOR_HOOK_STDIN_MAX_BYTES = 1_048_576`, independent from
//! `CURSOR_TOOL_FIELD_MAX_BYTES`.
//! - Cursor 3.12.17 sessionStart injection stays disabled; GH-823 v1 adds no
//! postToolUse context command/renderer/install entry.
//! - `session_id == conversation_id` equality is required on every event that
//! carries both; subagent identities stay distinct.
//! - Generic success requires a non-empty string `tool_use_id`; it is the
//! canonical per-call event/upsert identity.
//! - B-016 MCP ownership is generic: `beforeMCPExecution` and
//! `afterMCPExecution` are both unregistered/unsupported.
//! - All unobserved paths (Task success, Write/Edit/Delete, failed Shell,
//! multi-root, Windows/UNC, stop `status:"error"`) stay fail-closed.
/// Whole-payload stdin bound applied before any `String` allocation or serde
/// parse on every Cursor hook entrypoint. Human-frozen (SP823-T2 item 1).
pub const CURSOR_HOOK_STDIN_MAX_BYTES: usize = 1_048_576;
/// Per-field bound for Cursor tool payload representations (raw generic
/// strings measured as exact UTF-8 bytes; object representations measured on
/// their canonical serialization). Independent from
/// `CURSOR_HOOK_STDIN_MAX_BYTES` per the SP823-T2 approval record; the
/// numeric value below is the implementation proposal pending explicit human
/// confirmation on issue #823.
pub const CURSOR_TOOL_FIELD_MAX_BYTES: usize = 262_144;
/// Canonical host value recorded in the database for Cursor-origin events
/// (B-011). Must match `crate::identity::InstallHost::Cursor.as_db_value()`.
pub const CURSOR_HOST: &str = "cursor";
/// Existing `captured_events.event_type` text discriminator for the observed
/// failed-tool path (B-007). No migration, table, column, or index is added.
pub const CURSOR_TOOL_FAILURE_EVENT_TYPE: &str = "cursor_tool_failure";
/// Maps a capture summary event type to the `captured_events.event_type`
/// discriminator: the Cursor failed-tool value is preserved verbatim (B-007,
/// including spill replay); everything else keeps the historical
/// `tool_result` value used by Claude/Codex captures.
pub
/// Correlation id attached to Cursor parse/limit errors so failures can be
/// traced in logs without ever echoing raw payload bytes.
pub