claude-agent-sdk 1.2.1 → 1.3.0

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.
@@ -868,227 +868,55 @@ module ClaudeAgentSDK
868
868
  @callback_request_signals.delete(request_id)
869
869
  end
870
870
 
871
- def parse_hook_input(input_data) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength -- one branch per hook event type
871
+ # The typed input built for each hook event the SDK models; any other
872
+ # event becomes an UnknownHookInput.
873
+ HOOK_INPUT_CLASSES = {
874
+ 'PreToolUse' => PreToolUseHookInput,
875
+ 'PostToolUse' => PostToolUseHookInput,
876
+ 'PostToolUseFailure' => PostToolUseFailureHookInput,
877
+ 'UserPromptSubmit' => UserPromptSubmitHookInput,
878
+ 'Stop' => StopHookInput,
879
+ 'SubagentStop' => SubagentStopHookInput,
880
+ 'Notification' => NotificationHookInput,
881
+ 'SubagentStart' => SubagentStartHookInput,
882
+ 'PermissionRequest' => PermissionRequestHookInput,
883
+ 'PreCompact' => PreCompactHookInput,
884
+ 'SessionStart' => SessionStartHookInput,
885
+ 'SessionEnd' => SessionEndHookInput,
886
+ 'Setup' => SetupHookInput,
887
+ 'TeammateIdle' => TeammateIdleHookInput,
888
+ 'TaskCompleted' => TaskCompletedHookInput,
889
+ 'ConfigChange' => ConfigChangeHookInput,
890
+ 'WorktreeCreate' => WorktreeCreateHookInput,
891
+ 'WorktreeRemove' => WorktreeRemoveHookInput,
892
+ 'StopFailure' => StopFailureHookInput,
893
+ 'PostCompact' => PostCompactHookInput,
894
+ 'PermissionDenied' => PermissionDeniedHookInput,
895
+ 'TaskCreated' => TaskCreatedHookInput,
896
+ 'Elicitation' => ElicitationHookInput,
897
+ 'ElicitationResult' => ElicitationResultHookInput,
898
+ 'InstructionsLoaded' => InstructionsLoadedHookInput,
899
+ 'CwdChanged' => CwdChangedHookInput,
900
+ 'FileChanged' => FileChangedHookInput,
901
+ 'PostToolBatch' => PostToolBatchHookInput,
902
+ 'UserPromptExpansion' => UserPromptExpansionHookInput,
903
+ 'PreModelSwitch' => PreModelSwitchHookInput,
904
+ 'PostModelSwitch' => PostModelSwitchHookInput,
905
+ 'DirectoryAdded' => DirectoryAddedHookInput,
906
+ 'MessageDisplay' => MessageDisplayHookInput
907
+ }.freeze
908
+ private_constant :HOOK_INPUT_CLASSES
909
+
910
+ # Every field of the payload that the class declares is assigned (the
911
+ # lenient parse path: other keys are skipped, not rejected), and the
912
+ # whole payload is kept as #raw_input, so a field the CLI added after this
913
+ # SDK version is still readable there.
914
+ def parse_hook_input(input_data)
915
+ input_data = {} unless input_data.is_a?(Hash)
872
916
  event_name = input_data[:hook_event_name] || input_data['hook_event_name']
873
- fetch = lambda do |key|
874
- if input_data.key?(key)
875
- input_data[key]
876
- elsif input_data.key?(key.to_s)
877
- input_data[key.to_s]
878
- end
879
- end
880
- base_args = {
881
- session_id: fetch.call(:session_id),
882
- transcript_path: fetch.call(:transcript_path),
883
- cwd: fetch.call(:cwd),
884
- permission_mode: fetch.call(:permission_mode)
885
- }
886
-
887
- # Subagent context fields shared by tool-lifecycle hooks
888
- subagent_args = {
889
- agent_id: fetch.call(:agent_id),
890
- agent_type: fetch.call(:agent_type)
891
- }
892
-
893
- case event_name
894
- when 'PreToolUse'
895
- PreToolUseHookInput.new(
896
- tool_name: fetch.call(:tool_name),
897
- tool_input: fetch.call(:tool_input),
898
- tool_use_id: fetch.call(:tool_use_id),
899
- **subagent_args, **base_args
900
- )
901
- when 'PostToolUse'
902
- PostToolUseHookInput.new(
903
- tool_name: fetch.call(:tool_name),
904
- tool_input: fetch.call(:tool_input),
905
- tool_response: fetch.call(:tool_response),
906
- tool_use_id: fetch.call(:tool_use_id),
907
- **subagent_args, **base_args
908
- )
909
- when 'PostToolUseFailure'
910
- PostToolUseFailureHookInput.new(
911
- tool_name: fetch.call(:tool_name),
912
- tool_input: fetch.call(:tool_input),
913
- tool_use_id: fetch.call(:tool_use_id),
914
- error: fetch.call(:error),
915
- is_interrupt: fetch.call(:is_interrupt),
916
- **subagent_args, **base_args
917
- )
918
- when 'UserPromptSubmit'
919
- UserPromptSubmitHookInput.new(
920
- prompt: fetch.call(:prompt),
921
- **base_args
922
- )
923
- when 'Stop'
924
- StopHookInput.new(
925
- stop_hook_active: fetch.call(:stop_hook_active),
926
- last_assistant_message: fetch.call(:last_assistant_message),
927
- background_tasks: fetch.call(:background_tasks),
928
- session_crons: fetch.call(:session_crons),
929
- **base_args
930
- )
931
- when 'SubagentStop'
932
- SubagentStopHookInput.new(
933
- stop_hook_active: fetch.call(:stop_hook_active),
934
- agent_id: fetch.call(:agent_id),
935
- agent_transcript_path: fetch.call(:agent_transcript_path),
936
- agent_type: fetch.call(:agent_type),
937
- last_assistant_message: fetch.call(:last_assistant_message),
938
- background_tasks: fetch.call(:background_tasks),
939
- session_crons: fetch.call(:session_crons),
940
- **base_args
941
- )
942
- when 'Notification'
943
- NotificationHookInput.new(
944
- message: fetch.call(:message),
945
- title: fetch.call(:title),
946
- notification_type: fetch.call(:notification_type),
947
- **base_args
948
- )
949
- when 'SubagentStart'
950
- SubagentStartHookInput.new(
951
- agent_id: fetch.call(:agent_id),
952
- agent_type: fetch.call(:agent_type),
953
- **base_args
954
- )
955
- when 'PermissionRequest'
956
- PermissionRequestHookInput.new(
957
- tool_name: fetch.call(:tool_name),
958
- tool_input: fetch.call(:tool_input),
959
- permission_suggestions: fetch.call(:permission_suggestions),
960
- **subagent_args, **base_args
961
- )
962
- when 'PreCompact'
963
- PreCompactHookInput.new(
964
- trigger: fetch.call(:trigger),
965
- custom_instructions: fetch.call(:custom_instructions),
966
- **base_args
967
- )
968
- when 'SessionStart'
969
- SessionStartHookInput.new(
970
- source: fetch.call(:source),
971
- agent_type: fetch.call(:agent_type),
972
- model: fetch.call(:model),
973
- **base_args
974
- )
975
- when 'SessionEnd'
976
- SessionEndHookInput.new(
977
- reason: fetch.call(:reason),
978
- **base_args
979
- )
980
- when 'Setup'
981
- SetupHookInput.new(
982
- trigger: fetch.call(:trigger),
983
- **base_args
984
- )
985
- when 'TeammateIdle'
986
- TeammateIdleHookInput.new(
987
- teammate_name: fetch.call(:teammate_name),
988
- team_name: fetch.call(:team_name),
989
- **base_args
990
- )
991
- when 'TaskCompleted'
992
- TaskCompletedHookInput.new(
993
- task_id: fetch.call(:task_id),
994
- task_subject: fetch.call(:task_subject),
995
- task_description: fetch.call(:task_description),
996
- teammate_name: fetch.call(:teammate_name),
997
- team_name: fetch.call(:team_name),
998
- **base_args
999
- )
1000
- when 'ConfigChange'
1001
- ConfigChangeHookInput.new(
1002
- source: fetch.call(:source),
1003
- file_path: fetch.call(:file_path),
1004
- **base_args
1005
- )
1006
- when 'WorktreeCreate'
1007
- WorktreeCreateHookInput.new(
1008
- name: fetch.call(:name),
1009
- **base_args
1010
- )
1011
- when 'WorktreeRemove'
1012
- WorktreeRemoveHookInput.new(
1013
- worktree_path: fetch.call(:worktree_path),
1014
- **base_args
1015
- )
1016
- when 'StopFailure'
1017
- StopFailureHookInput.new(
1018
- error: fetch.call(:error),
1019
- error_details: fetch.call(:error_details),
1020
- last_assistant_message: fetch.call(:last_assistant_message),
1021
- **base_args
1022
- )
1023
- when 'PostCompact'
1024
- PostCompactHookInput.new(
1025
- trigger: fetch.call(:trigger),
1026
- compact_summary: fetch.call(:compact_summary),
1027
- **base_args
1028
- )
1029
- when 'PermissionDenied'
1030
- PermissionDeniedHookInput.new(
1031
- tool_name: fetch.call(:tool_name),
1032
- tool_input: fetch.call(:tool_input),
1033
- tool_use_id: fetch.call(:tool_use_id),
1034
- reason: fetch.call(:reason),
1035
- **subagent_args, **base_args
1036
- )
1037
- when 'TaskCreated'
1038
- TaskCreatedHookInput.new(
1039
- task_id: fetch.call(:task_id),
1040
- task_subject: fetch.call(:task_subject),
1041
- task_description: fetch.call(:task_description),
1042
- teammate_name: fetch.call(:teammate_name),
1043
- team_name: fetch.call(:team_name),
1044
- **base_args
1045
- )
1046
- when 'Elicitation'
1047
- ElicitationHookInput.new(
1048
- mcp_server_name: fetch.call(:mcp_server_name),
1049
- message: fetch.call(:message),
1050
- mode: fetch.call(:mode),
1051
- url: fetch.call(:url),
1052
- elicitation_id: fetch.call(:elicitation_id),
1053
- requested_schema: fetch.call(:requested_schema),
1054
- **base_args
1055
- )
1056
- when 'ElicitationResult'
1057
- ElicitationResultHookInput.new(
1058
- mcp_server_name: fetch.call(:mcp_server_name),
1059
- elicitation_id: fetch.call(:elicitation_id),
1060
- mode: fetch.call(:mode),
1061
- action: fetch.call(:action),
1062
- content: fetch.call(:content),
1063
- **base_args
1064
- )
1065
- when 'InstructionsLoaded'
1066
- InstructionsLoadedHookInput.new(
1067
- file_path: fetch.call(:file_path),
1068
- memory_type: fetch.call(:memory_type),
1069
- load_reason: fetch.call(:load_reason),
1070
- globs: fetch.call(:globs),
1071
- trigger_file_path: fetch.call(:trigger_file_path),
1072
- **base_args
1073
- )
1074
- when 'CwdChanged'
1075
- CwdChangedHookInput.new(
1076
- old_cwd: fetch.call(:old_cwd),
1077
- new_cwd: fetch.call(:new_cwd),
1078
- **base_args
1079
- )
1080
- when 'FileChanged'
1081
- FileChangedHookInput.new(
1082
- file_path: fetch.call(:file_path),
1083
- event: fetch.call(:event),
1084
- **base_args
1085
- )
1086
- else
1087
- # Unknown event: preserve the wire event name and full raw payload
1088
- # rather than dropping event-specific fields (Python passes the raw
1089
- # dict through, so nothing is lost there).
1090
- UnknownHookInput.new(hook_event_name: event_name, raw_input: input_data, **base_args)
1091
- end
917
+ hook_input = HOOK_INPUT_CLASSES.fetch(event_name, UnknownHookInput).from_hash(input_data)
918
+ hook_input.raw_input = input_data
919
+ hook_input
1092
920
  end
1093
921
 
1094
922
  def handle_mcp_message(request_data)