Skip to content

Follow Python SDK #1331/#1332: hook types, raw hook input, init fields - #174

Merged
ya-luotao merged 2 commits into
mainfrom
feat/hook-types-python-parity
Oct 12, 2026
Merged

ya-luotao merged 2 commits into
mainfrom
feat/hook-types-python-parity

Conversation

@ya-luotao

@ya-luotao ya-luotao commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Follows the two unreleased Python SDK changes on main (anthropics/claude-agent-sdk-python#1331, #1332), and fixes the Ruby-side gap that made the first one matter: typed hook inputs used to drop every field the SDK did not list by hand.

Hook inputs

  • Query#parse_hook_input was a 27-branch case that copied a fixed field list per event, so any field a newer CLI added (prompt_id, effort, duration_ms, ...) never reached the callback. It is now an event → class table (HOOK_INPUT_CLASSES, like MessageParser::SYSTEM_MESSAGE_CLASSES) built with the lenient from_hash, and every hook input keeps the payload as #raw_input (previously UnknownHookInput only). #inspect shows raw_input on an UnknownHookInput only, as typed system messages leave out data: a typed input already prints those fields as attributes.
  • New fields: prompt_id, effort (all inputs); duration_ms (PostToolUse / PostToolUseFailure); session_title, source (UserPromptSubmit); session_title, seconds_since_last_response, prompt_cache_likely_expired, context_tokens, estimated_cache_write_usd (SessionStart); parent_file_path (InstructionsLoaded).
  • New typed inputs for PostToolBatch, UserPromptExpansion, PreModelSwitch, PostModelSwitch, DirectoryAdded, MessageDisplay; HOOK_EVENTS now matches the CLI's 33 events. The two model-switch inputs read every field of the CLI schema, cache_ttl and pricing included.
  • Fields beyond Python's list come from the hook input schemas in the Claude Code 2.1.273 and 2.1.295 binaries. mcp_server (new in 2.1.295, marked @internal) is left to raw_input.

Hook-specific outputs

  • Each *HookSpecificOutput class declares its fields with attr_accessor (so YARD documents them) and a small @api private HookSpecificOutputFields helper adds the event name, #initialize and a #to_h that takes wire keys from HookOutputKeys::HOOK_SPECIFIC, so the table is the single vocabulary instead of being kept in step with 12 hand-written #to_hs. A YARD macro documents the generated members.
  • Adds the 10 output classes from #1331 (PostToolBatch, UserPromptExpansion, Stop, SubagentStop, PreModelSwitch, PostModelSwitch, Elicitation, ElicitationResult, WorktreeCreate, MessageDisplay) and the new UserPromptSubmit / SessionStart fields. Hash outputs could already send these keys; the typed classes raised ArgumentError.

InitMessage

  • capabilities, plugin_errors, terminal_slash_commands, fast_mode_disabled_reason (#1332's SystemInitData).

Behavior changes (in CHANGELOG)

  • PermissionDeniedHookSpecificOutput#retry defaults to nil and is omitted (was false, always sent; the CLI reads both the same).
  • An output field set to false is now sent; only nil is omitted (most fields used to drop false too).

sig/, docs/hooks-and-permissions.md (new output-class table), docs/types.md and CHANGELOG are updated; README, the skill (both copies), the plugin manifest and the lifecycle example now say 33 hook events and list the 22 output classes.

Test plan

  • bundle exec rspec: 4314 examples, 0 failures (query_hook_input_spec now has rows for all 33 events, checks raw_input, and covers an unknown event)
  • bundle exec rubocop: no offenses
  • bundle exec rake rbs:validate rbs:test: 3903 examples, 0 failures
  • yard doc: every *HookSpecificOutput page lists its fields, #hook_event_name, #initialize and #to_h; no warning from types/hooks.rb
  • Real session against CLI 2.1.295: InitMessage#capabilities / #fast_mode_disabled_reason populated, UserPromptSubmitHookInput#prompt_id received, typed UserPromptSubmitHookSpecificOutput(session_title:) accepted. Tool-use and Stop hooks were not exercised live (the test account's API credit was exhausted).

https://ampcode.com/threads/T-01a127a8-ad00-706d-a216-afc5c6d86e27

ampagent and others added 2 commits October 10, 2026 21:33
- Build hook inputs from the whole payload through an event => class
  table instead of a per-event field list, and keep the payload as
  #raw_input on every hook input, so CLI fields the SDK does not model
  are no longer dropped.
- Add prompt_id/effort, duration_ms and the other input fields the CLI
  sends; typed inputs for PostToolBatch, UserPromptExpansion,
  PreModelSwitch, PostModelSwitch, DirectoryAdded and MessageDisplay.
- Declare the *HookSpecificOutput classes from HookOutputKeys and add
  the ten output classes and new fields of Python #1331.
- PermissionDeniedHookSpecificOutput#retry defaults to nil.
- InitMessage: capabilities, plugin_errors, terminal_slash_commands,
  fast_mode_disabled_reason (Python #1332).

Amp-Thread-ID: https://ampcode.com/threads/T-01a127a8-ad00-706d-a216-afc5c6d86e27
Co-authored-by: ya-luotao <luotao@hey.com>
…ields

- Declare each *HookSpecificOutput's fields with attr_accessor in the
  class body again; hook_specific_output only fixes the event and defines
  #initialize / #to_h (wire keys still from HookOutputKeys). With the
  fields behind the helper, YARD saw none of them and the API reference
  of all 22 classes was empty. A macro documents the generated members.
- Typed hook inputs leave raw_input out of #inspect, as typed system
  messages leave out data; an UnknownHookInput keeps it.
- PreModelSwitchHookInput / PostModelSwitchHookInput: cache_ttl and
  pricing, the two fields of the CLI 2.1.295 schema they did not read.
- README, the skill (both copies), the plugin manifest and the lifecycle
  example said 27 hook events and listed 12 output classes; now 33 and 22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZys11v9xdM7DXGx9JpC7y
@ya-luotao
ya-luotao merged commit 694c1a8 into main Oct 12, 2026
15 checks passed
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.

2 participants