Skip to content

feat(hooks): error event — fires when a turn exits unrecoverably#241

Merged
emal-avala merged 1 commit intomainfrom
feat/hook-error-event
Apr 24, 2026
Merged

feat(hooks): error event — fires when a turn exits unrecoverably#241
emal-avala merged 1 commit intomainfrom
feat/hook-error-event

Conversation

@emal-avala
Copy link
Copy Markdown
Member

Summary

Adds HookEvent::Error, a lifecycle event the turn driver fires when an LLM call can't be recovered via retry or reactive compaction. Fills a real gap: today there's no way for a hook to catch turn failures without tailing stderr or polling session files.

Hook context

{
  "session_id": "",
  "turn": 3,
  "stage": "llm_call_failed",
  "message": "<error text>"
}

The stage field is a coarse tag ("llm_call_failed" today) so future fire-sites — tool-dispatch failures, permission denies — can reuse the same hook surface with a different tag. Hooks can filter on stage if they care about one category.

Example use

[[hooks]]
event  = "error"
action = { type = "http", url = "https://pager.example.com/alert", method = "POST" }

Integration

  • Schema: new HookEvent::Error variant with rustdoc.
  • Dispatcher: run_hooks test confirms event matching.
  • QueryEngine::fire_error_hooks(stage, message) helper plus one fire-site at the only return Err(...) path in run_turn_with_sink.
  • /hooks events catalog, format_hook_event, and parse_hook_event all teach the new name.

Tests

  • Serde roundtrip: HookEvent::Error"error"
  • Dispatcher: a registered Error hook actually fires when the event is dispatched
  • CLI invariant tests continue to pass (hook_event_catalog_has_unique_names, parse_hook_event_covers_every_variant_in_catalog)

Full hook + schema suites pass. Clippy clean under -D warnings.

Test plan

  • cargo test -p agent-code-lib --lib hooks::
  • cargo test -p agent-code-lib --lib hook_event_serde
  • cargo test -p agent-code --bin agent parse_hook_event
  • cargo clippy --workspace --tests --no-deps -- -D warnings
  • cargo fmt --all --check

Adds `HookEvent::Error`, a lifecycle event that fires from the turn
driver when an LLM call can't be recovered via retry or reactive
compaction. Context:

    {
      "session_id": "...",
      "turn":       3,
      "stage":      "llm_call_failed",
      "message":    "<error text>"
    }

Use case: pager integrations, audit logs, and failover automation
want a single place to react to turn failures without tailing stderr
or polling session files.

The `stage` field is a coarse tag ("llm_call_failed" today) so
future fire-sites (tool dispatch failures, permission denies, etc.)
can reuse the same hook surface with a different tag — hooks can
filter on stage if they care about one category.

Integration points updated:
- schema: new variant with rustdoc
- dispatcher: run_hooks test proves matching
- QueryEngine: fire_error_hooks helper + one fire-site at the only
  existing `return Err(...)` path in run_turn_with_sink
- /hooks events: catalog, format_hook_event, parse_hook_event all
  teach the new name

Tests:
- serde roundtrip: `HookEvent::Error` ↔ `"error"`
- dispatcher: run_hooks fires a hook registered for `Error`
- CLI catalog invariants continue to pass: unique names,
  `parse_hook_event_covers_every_variant_in_catalog`
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@emal-avala emal-avala merged commit 77552c9 into main Apr 24, 2026
14 checks passed
@emal-avala emal-avala deleted the feat/hook-error-event branch April 24, 2026 04:34
@emal-avala emal-avala mentioned this pull request Apr 24, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant