Skip to content

Host Detection

The bridge resolves the host in this order:

  1. Explicit host option to run(hooks, { host }).
  2. --host <id> flag in process.argv (or argv option). The install command writes this into every generated config.
  3. Confidence-scored heuristic over stdin markers.

If all three fall through without a result, run() writes a stderr error and exits 1.

Stdin scoring

Used only when no explicit host is provided.

ts
const scores: Record<THostId, number> = { claude: 0, cursor: 0, codex: 0 };

if (typeof raw.cursor_version === "string")                                            scores.cursor += 1.0;
if (typeof raw.conversation_id === "string" && typeof raw.generation_id === "string") scores.cursor += 0.6;
if (typeof raw.hook_event_name === "string" && /^[a-z]/.test(raw.hook_event_name))     scores.cursor += 0.4;

if (typeof raw.turn_id === "string")                                                   scores.codex += 1.0;
if (typeof raw.tool_use_id === "string")                                               scores.codex += 0.6;
if ("last_assistant_message" in raw)                                                   scores.codex += 0.6;
if (typeof raw.model === "string" && eventNameIsPascalCase)                            scores.codex += 0.6;

if (eventNameIsPascalCase && scores.codex === 0)                                       scores.claude += 0.3;
if (typeof raw.tool_input === "object" && raw.tool_input !== null)                     scores.claude += 0.2;
if (typeof raw.transcript_path === "string")                                           scores.claude += 0.2;

Threshold: 0.5. Below that, detection returns null.

The tool_use_id, last_assistant_message, and model + PascalCase fingerprints exist so Codex events that lack turn_id (notably SessionStart) still score above the threshold. Without them, a Codex SessionStart payload would tie-break to Claude via the PascalCase + transcript_path rule and parse against the wrong adapter.

Marker reference

MarkerHost
cursor_version: stringCursor
conversation_id + generation_id stringsCursor
camelCase hook_event_nameCursor
turn_id: stringCodex
tool_use_id: stringCodex
last_assistant_message presentCodex
model: string + PascalCase hook_event_nameCodex
PascalCase hook_event_name without Codex markersClaude
tool_input objectClaude
transcript_path: stringClaude

Failure mode

text
[agent-hooks-bridge] could not detect host from stdin (scores: {"claude":0.2,"cursor":0,"codex":0}); pass --host or { host } explicitly

stdout is empty. Exit code is 1. The host treats this as a non-blocking error.

When detection runs in practice

For installs generated by bunx @pivanov/agent-hooks-bridge install, every command includes --host <host>, so detection is not on the path. Detection only runs when:

  • A hook is wired by hand without the --host flag.
  • A custom transport calls run() without options.

Debug telemetry

Set AGENT_HOOKS_BRIDGE_DEBUG=1 (or any truthy value: true, yes, on) to log the resolved host and detection scores to stderr on every event:

text
[agent-hooks-bridge] debug: host=claude via=argv
[agent-hooks-bridge] debug: parsed event=PreToolUse tool=Bash
text
[agent-hooks-bridge] debug: host=codex via=detection scores={"claude":0.4,"cursor":0,"codex":2.2}
[agent-hooks-bridge] debug: parsed event=PostToolUse tool=Edit

via is one of option (passed to run({ host })), argv (resolved from --host in argv), or detection (stdin scoring; the scores object is included). Falsy values disable the logging: empty string, 0, false, no, off.

Not affiliated with Anthropic, Anysphere, or OpenAI. Supported by LogicStar AI.