@ev-ai/agent-hook 1.3.4 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/COLLECTED-DATA.md CHANGED
@@ -40,7 +40,7 @@ Every event also carries: UTC timestamp, `installation_id` (per-machine under `~
40
40
  - `tool_name`, `skill_name`, `rule_name`, `command_name`
41
41
  - **`executable`** — first token of a shell or MCP spawn command when stdin has one (`tool_input.command` or Cursor top-level `command`). Payload `executable` never includes args or the rest of the line. The full Cursor `command` string and Claude `tool_input` object ride on `hook_input` unless content capture is off. Home-directory prefixes on `executable` are rewritten to `~`. Omitted when the tool has no command (Read / Write / Grep / Skill / MCP tool calls).
42
42
  - **Cursor `skill_name` (derived, not from a native Skill tool):** slash commands via `beforeSubmitPrompt` (`/my-skill` → `tool_name: Skill`, `skill_name: my-skill`; the prompt body is on `hook_input.prompt` unless content capture is off); Read of `**/skills/<id>/SKILL.md` on `preToolUse` / `postToolUse` / `beforeReadFile` (skill id only — file path is never POSTed); Shell whose command references a skill path on `beforeShellExecution` / `afterShellExecution` (id only; the shell command line is on `hook_input.command` unless content capture is off; argv0 may appear as `executable`). Silent auto-inject with no slash and no SKILL.md Read is not observable.
43
- - **Claude `skill_name` (derived):** slash commands via `UserPromptExpansion` (`command_name` → `tool_name: Skill`, `skill_name: <id>`; `prompt` is on `hook_input` unless content capture is off; `command_args` are never POSTed except under full stdin). Typing `/skillname` bypasses `PreToolUse(Skill)`; Read of `**/skills/<id>/SKILL.md` on Pre/Post tool hooks is also derived when present. Non-slash user prompts arrive on `UserPromptSubmit`.
43
+ - **Claude `skill_name` (derived):** slash commands via `UserPromptExpansion` (`command_name` → `tool_name: Skill`, `skill_name: <id>`; `prompt` is on `hook_input` unless content capture is off; `command_args` are never POSTed except under full stdin). Typing `/skillname` bypasses `PreToolUse(Skill)`; Read of `**/skills/<id>/SKILL.md` on Pre/Post tool hooks is also derived when present. Non-slash user prompts arrive on `UserPromptSubmit` only when prompt capture is on; with capture off that event is not sent. Slash-command rows from the same hook still send, without the prompt.
44
44
  - **Codex `skill_name` (derived):** slash / custom-prompt ids via `UserPromptSubmit` (`/my-skill` → `tool_name: Skill`, `skill_name: my-skill`; the prompt body is on `hook_input.prompt` unless content capture is off); Bash/Shell whose `tool_input.command` references `.claude/.cursor/.agents/skills/<id>/SKILL.md` or `.codex/commands/<id>.md` (id only; `tool_input` is on `hook_input` unless content capture is off; argv0 may appear as `executable`); Read-like tools of those paths when present. Silent auto-inject with no slash and no skill-file touch is not observable.
45
45
  - **Cursor `rule_name` (derived):** always-apply rules via `beforeSubmitPrompt` `attachments[]` (`type: "rule"` → `tool_name: Rule`; `file_path` / `attachments` are never POSTed; prompt text is on `hook_input.prompt` unless content capture is off). When the attachment resolves to a local rules file, `rule_name` is the path-derived id after symlink canonicalization (extension stripped): Cursor's `.cursor/rules/claude/_global/finops.mdc` shim names as `_global/finops` because that path realpaths to `.claude/rules/_global/finops.mdc`. Glob / agent-requestable / @-mentioned rules are not on submit stdin — only a later Read is observable. Root memory files Cursor attaches as rules (`CLAUDE.md`, `AGENTS.md`) emit `rule_name` without a `component_reference`.
46
46
  - **Claude `rule_name` (derived):** Claude Code has no submit-time attachment list, so a rule is only observable as a Read / Shell that touches a rules file. Read of `**/.cursor/rules/**`, `**/.claude/rules/**`, or `**/.codex/rules/**` on any Pre/Post tool hook emits `rule_name` (id only — the file path is never POSTed), and on any runner that path is realpath-canonicalized before naming, so a tool event and an attachment event on the same file agree. A Shell command that references a rules path yields the id from the stdin spelling without canonicalization. Auto-attached rules that are never read are not observable.
@@ -79,7 +79,7 @@ Username and hostname *are* sent (per-member usage for the installing org). The
79
79
 
80
80
  - Chat transcripts (the JSONL file), session titles, and keystrokes
81
81
  - Tool error messages and stderr (a failure **class** is sent, never the message). Tool **result bodies** (`tool_response`) and **arguments** (`tool_input` / Cursor `command`) **are** sent unless `EV_AI_HOOK_CAPTURE_CONTENT=off`
82
- - User **prompts**, tool args, tool results and the last assistant message — dropped unless `EV_AI_HOOK_CAPTURE_PROMPT=on`, the org config `collect_prompt`, or full stdin capture
82
+ - User **prompts**, tool args, tool results and the last assistant message — dropped unless `EV_AI_HOOK_CAPTURE_PROMPT=on`, the org config `collect_prompt`, or full stdin capture. With prompt capture off, a plain prompt submit sends no event at all; slash-command and rule rows from that hook still send, without the prompt.
83
83
  - Cursor top-level `content` / `edits` / `file_path` from `afterFileEdit` / `beforeReadFile` stdin — those stay dropped. **Claude nests the same data inside `tool_input`** (Write `content`, Edit `old_string` / `new_string`), and `tool_response` carries read / command output, so on Claude those bodies **are** sent unless `EV_AI_HOOK_CAPTURE_CONTENT=off`
84
84
  - Contents of skills, rules, commands, memory, or settings files **outside** the user-spec path (user-scope name+size listings still omit bodies). User-spec file-backed components **do** send capped `text` (once per content hash; may be deferred under the byte budget); `description` is derived server-side
85
85
  - Hook **prompt** strings (only `action_kind` + `action_hash` for `prompt`-type actions). Hook **command** strings (`command` / `bash` / `powershell`) *are* sent, sanitized: home directory rewritten to `~`, URLs/tokens/secret-shaped args redacted, capped at 300 chars
package/README.md CHANGED
@@ -8,7 +8,7 @@ This is an **opt-in, first-party observability agent** that an ev-ai customer in
8
8
 
9
9
  - **Data goes to the customer's own collector.** The destination is the org's ev-ai endpoint (default `https://hooks.ev-ai.ai/{orgToken}/hook`, or a self-hosted collector via `--url`). It is not a third party from the org's perspective — the customer is the data controller.
10
10
  - **It is consent-based and removable.** Nothing installs silently: the operator enables the ev-ai plugins (or runs file-wiring `install`), and uninstall / plugin disable removes ev-ai wiring. Non-managed hooks can be removed locally by the developer at any time.
11
- - **Prompt collection is opt-in, and it covers every content body.** User prompts, tool args, tool results and the last assistant message (`hook_input.prompt`, `tool_input`, `tool_response`, `last_assistant_message`, Cursor `command`) are dropped by default and sent together (size-capped) with `EV_AI_HOOK_CAPTURE_PROMPT=on` or the org config `collect_prompt`. Policy rules still match them locally either way.
11
+ - **Prompt collection is opt-in, and it covers every content body.** User prompts, tool args, tool results and the last assistant message (`hook_input.prompt`, `tool_input`, `tool_response`, `last_assistant_message`, Cursor `command`) are dropped by default and sent together (size-capped) with `EV_AI_HOOK_CAPTURE_PROMPT=on` or the org config `collect_prompt`. With prompt capture off, a plain prompt submit sends no event at all (Claude/Codex `UserPromptSubmit`, Cursor `beforeSubmitPrompt` with no slash command or rule attachment). Slash-command and rule rows from those hooks still send, without the prompt. Policy rules still match them locally either way.
12
12
  - **Content kill switch.** `EV_AI_HOOK_CAPTURE_CONTENT=off` or `capture_hook_input: "allowlist"` drops all of them even when prompt collection is on. On Claude that includes file bodies written or read through a tool, which arrive nested in `tool_input` / `tool_response`. Session titles, chat transcripts, keystrokes, and runner error prose stay dropped. `env_vars` is a fixed allowlist of non-secret CI/runtime markers; API keys and proxy vars are never read. See [Privacy defaults](#privacy-defaults).
13
13
  - **What it is not:** it is not spyware, not a keylogger, and not a data broker. It does not collect keystrokes, does not scrape the filesystem, and does not exfiltrate to any endpoint the operator did not configure. It forwards the bodies of prompts and tool calls only when prompt collection is turned on.
14
14
 
@@ -106,7 +106,7 @@ Keep Claude and Cursor both wired. Cursor also loads `.claude/settings.json` as
106
106
 
107
107
  Stacked configs for the **same** runner (user-level `~/.cursor/hooks.json` plus project `.cursor/hooks.json`, or plugins enabled alongside file wiring) also fire twice. The hook collapses those to one collect + POST via a short-lived cross-process claim under `~/.ev-ai/agent-hook/claims/` (keyed by platform, session, event, turn / `tool_use_id`, and a local hash of call-identifying stdin when `tool_use_id` is absent — so distinct Cursor shell/read/edit calls in one turn are not treated as duplicates). Weak payloads without a strong id fail open (both may post). Disable by setting `EVENT_CLAIM_DEDUP_ENABLED` to `false` in source — needed if two configs for the same runner intentionally target **different** collector URLs (the claim key does not include the URL, so only one would deliver).
108
108
 
109
- **Concurrency cap.** At most `max_concurrent_hooks` hook processes (set in `~/.ev-ai/config.json`; default 4 on Windows, 10 elsewhere) collect and POST at the same time. Each one holds a slot file under `~/.ev-ai/agent-hook/slots/`. An event that arrives while every slot is held is dropped: the runner still gets its normal ack and nothing is posted. Session start and end events are never capped. A value that is not a positive integer falls back to the default, and any slot-file error fails open (the event is collected).
109
+ **Concurrency cap.** At most `max_concurrent_hooks` hook processes (set in `~/.ev-ai/config.json`; default 4 on Windows, 10 elsewhere) collect and POST at the same time. Each one holds a slot file under `~/.ev-ai/agent-hook/slots/`. An event that arrives while every slot is held is dropped: the runner still gets its normal ack and nothing is posted. The launcher checks the slots before it starts the hook, so a dropped event does not start a hook process at all. Session start and end events are never capped. A value that is not a positive integer falls back to the default, and any slot-file error fails open (the event is collected).
110
110
 
111
111
  Wired command shape (auth is in the committed `collector_url`). The per-event command is **pinned to the installer's version** (not `@latest`):
112
112
 
@@ -204,7 +204,7 @@ Debug envelope logging: `EV_AI_HOOK_DEBUG=1` (redacted summary). Full envelope:
204
204
 
205
205
  Full field-by-field inventory of what is POSTed: [COLLECTED-DATA.md](./COLLECTED-DATA.md).
206
206
 
207
- - **`hook_input`**: metadata (session/tool ids, status, …). The content fields — `prompt`, `tool_input`, `tool_response`, `last_assistant_message`, and Cursor `command` — are opt-in together: `EV_AI_HOOK_CAPTURE_PROMPT=on` (also accepts `1` / `true`) or the org config `collect_prompt`. Kill switch: `EV_AI_HOOK_CAPTURE_CONTENT=off` or untracked/tracked manifest `capture_hook_input: "allowlist"`. Session titles, transcript paths, runner error prose, and file/attachment paths stay dropped, as do the MCP spawn argv on `beforeMCPExecution` and the error prose `StopFailure` puts in `last_assistant_message`. Full stdin dump: untracked operator manifest `capture_hook_input: "full"`, `debug: true` on `~/.ev-ai/config.json`, or `EV_AI_HOOK_CAPTURE_FULL_INPUT=1` (still size-capped). `debug` does not change the collector URL. A **git-tracked** (committed) manifest cannot set `full` or `debug` — use the env var or an untracked operator / home overlay instead.
207
+ - **`hook_input`**: metadata (session/tool ids, status, …). The content fields — `prompt`, `tool_input`, `tool_response`, `last_assistant_message`, and Cursor `command` — are opt-in together: `EV_AI_HOOK_CAPTURE_PROMPT=on` (also accepts `1` / `true`) or the org config `collect_prompt`. With prompt capture off, a plain prompt submit sends no event; slash-command and rule rows from that hook still send, without the prompt. Kill switch: `EV_AI_HOOK_CAPTURE_CONTENT=off` or untracked/tracked manifest `capture_hook_input: "allowlist"`. Session titles, transcript paths, runner error prose, and file/attachment paths stay dropped, as do the MCP spawn argv on `beforeMCPExecution` and the error prose `StopFailure` puts in `last_assistant_message`. Full stdin dump: untracked operator manifest `capture_hook_input: "full"`, `debug: true` on `~/.ev-ai/config.json`, or `EV_AI_HOOK_CAPTURE_FULL_INPUT=1` (still size-capped). `debug` does not change the collector URL. A **git-tracked** (committed) manifest cannot set `full` or `debug` — use the env var or an untracked operator / home overlay instead.
208
208
  - **`env_vars`**: fixed allowlist only (CI markers, `GITHUB_*` context, vendor base-URL overrides). Gateway URL values (`*_BASE_URL`, etc.) are sent with host/path and non-sensitive query keys for proxy-substitution detection; embedded URL credentials (`user:pass@`) and credential-shaped query params (`key`, `api-key`, `token`, `sig`, …) are redacted. API keys and HTTP proxy vars are never read.
209
209
  - **Transport**: collector URL must be `https` (or `http://localhost` / `127.0.0.1`, which is allowed only so a collector can be exercised on the same machine). Cleartext remote URLs are blocked (fail-open, no POST).
210
210
  - **User spec / project directories.** Accepting home-directory inventory (`~/.claude`, `~/.cursor`, `~/.codex`) does not by itself describe the project walks. Those also inventory **gitignored** agent config in the working tree (`settings.local.json`, `CLAUDE.local.md`, uncommitted edits) and **every directory a session opens** — including a personal clone on a work machine (cwd walk always; repo walk when that clone has an origin remote) and a covering parent above the git root (for example `~/dev/CLAUDE.md` sitting above both work and personal clones). Disable the project walks with `EV_AI_HOOK_CWD_SPEC=off`. Disable home spec, project spec, and operator identity together with `EV_AI_HOOK_USER_SPEC=off`.