@qwen-code/qwen-code 0.14.5 → 0.15.0-preview.1

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.
@@ -1,4 +1,4 @@
1
- # Qwen Code Hooks Documentation
1
+ # Qwen Code Hooks
2
2
 
3
3
  ## Overview
4
4
 
@@ -28,48 +28,204 @@ Hooks are user-defined scripts or programs that are automatically executed by Qw
28
28
  - Integrate with external systems and services
29
29
  - Modify tool inputs or responses programmatically
30
30
 
31
- ## Hook Architecture
31
+ ## Hook Types
32
32
 
33
- The Qwen Code hook system consists of several key components:
33
+ Qwen Code supports three hook executor types:
34
34
 
35
- 1. **Hook Registry**: Stores and manages all configured hooks
36
- 2. **Hook Planner**: Determines which hooks should run for each event
37
- 3. **Hook Runner**: Executes individual hooks with proper context
38
- 4. **Hook Aggregator**: Combines results from multiple hooks
39
- 5. **Hook Event Handler**: Coordinates the firing of hooks for events
35
+ | Type | Description |
36
+ | :--------- | :--------------------------------------------------------------------------------------------- |
37
+ | `command` | Execute a shell command. Receives JSON via `stdin`, returns results via `stdout`. |
38
+ | `http` | Send JSON as a `POST` request body to a specified URL. Returns results via HTTP response body. |
39
+ | `function` | Directly call a registered JavaScript function (session-level hooks only). |
40
+
41
+ ### Command Hooks
42
+
43
+ Command hooks execute commands via child processes. Input JSON is passed through stdin, and output is returned via stdout.
44
+
45
+ **Configuration:**
46
+
47
+ | Field | Type | Required | Description |
48
+ | :-------------- | :----------------------- | :------- | :------------------------------------------ |
49
+ | `type` | `"command"` | Yes | Hook type |
50
+ | `command` | `string` | Yes | Command to execute |
51
+ | `name` | `string` | No | Hook name (for logging) |
52
+ | `description` | `string` | No | Hook description |
53
+ | `timeout` | `number` | No | Timeout in milliseconds, default 60000 |
54
+ | `async` | `boolean` | No | Whether to run asynchronously in background |
55
+ | `env` | `Record<string, string>` | No | Environment variables |
56
+ | `shell` | `"bash" \| "powershell"` | No | Shell to use |
57
+ | `statusMessage` | `string` | No | Status message displayed during execution |
58
+
59
+ **Example:**
60
+
61
+ ```json
62
+ {
63
+ "hooks": {
64
+ "PreToolUse": [
65
+ {
66
+ "matcher": "WriteFile",
67
+ "hooks": [
68
+ {
69
+ "type": "command",
70
+ "command": "$QWEN_PROJECT_DIR/.qwen/hooks/security-check.sh",
71
+ "name": "security-check",
72
+ "timeout": 10000
73
+ }
74
+ ]
75
+ }
76
+ ]
77
+ }
78
+ }
79
+ ```
80
+
81
+ ### HTTP Hooks
82
+
83
+ HTTP hooks send hook input as POST requests to specified URLs. They support URL whitelists, DNS-level SSRF protection, environment variable interpolation, and other security features.
84
+
85
+ **Configuration:**
86
+
87
+ | Field | Type | Required | Description |
88
+ | :--------------- | :----------------------- | :------- | :-------------------------------------------------------- |
89
+ | `type` | `"http"` | Yes | Hook type |
90
+ | `url` | `string` | Yes | Target URL |
91
+ | `headers` | `Record<string, string>` | No | Request headers (supports env var interpolation) |
92
+ | `allowedEnvVars` | `string[]` | No | Whitelist of environment variables allowed in URL/headers |
93
+ | `timeout` | `number` | No | Timeout in seconds, default 600 |
94
+ | `name` | `string` | No | Hook name (for logging) |
95
+ | `statusMessage` | `string` | No | Status message displayed during execution |
96
+ | `once` | `boolean` | No | Execute only once per event per session (HTTP hooks only) |
97
+
98
+ **Security Features:**
99
+
100
+ - **URL Whitelist**: Configure allowed URL patterns via `allowedUrls`
101
+ - **SSRF Protection**: Blocks private IPs (10.x.x.x, 172.16-31.x.x, 192.168.x.x, etc.) but allows loopback addresses (127.0.0.1, ::1)
102
+ - **DNS Validation**: Validates domain resolution before requests to prevent DNS rebinding attacks
103
+ - **Environment Variable Interpolation**: `${VAR}` syntax, only allows variables in `allowedEnvVars` whitelist
104
+
105
+ **Example:**
106
+
107
+ ```json
108
+ {
109
+ "hooks": {
110
+ "PreToolUse": [
111
+ {
112
+ "matcher": "*",
113
+ "hooks": [
114
+ {
115
+ "type": "http",
116
+ "url": "http://127.0.0.1:8080/hooks/pre-tool-use",
117
+ "headers": {
118
+ "Authorization": "Bearer ${HOOK_API_KEY}"
119
+ },
120
+ "allowedEnvVars": ["HOOK_API_KEY"],
121
+ "timeout": 10,
122
+ "name": "remote-security-check"
123
+ }
124
+ ]
125
+ }
126
+ ]
127
+ }
128
+ }
129
+ ```
130
+
131
+ ### Function Hooks
132
+
133
+ Function hooks directly call registered JavaScript/TypeScript functions. They are used internally by the Skill system and are not currently exposed as a public API for end users.
134
+
135
+ **Note**: For most use cases, use **command hooks** or **HTTP hooks** instead, which can be configured in settings files.
40
136
 
41
137
  ## Hook Events
42
138
 
43
- Hooks fire at specific points during a Qwen Code session. When an event fires and a matcher matches, Qwen Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. Your handler can inspect the input, take action, and optionally return a decision. Some events fire once per session, while others fire repeatedly inside the agentic loop.
44
-
45
- <div align="center">
46
- <img src="https://img.alicdn.com/imgextra/i4/O1CN01sYWUTh1RDJl7Lz2ne_!!6000000002077-2-tps-812-1212.png" alt="Hook Lifecycle Diagram" width="400"/>
47
- </div>
48
-
49
- The following table lists all available hook events in Qwen Code:
50
-
51
- | Event Name | Description | Use Case |
52
- | -------------------- | ------------------------------------------- | ----------------------------------------------- |
53
- | `PreToolUse` | Fired before tool execution | Permission checking, input validation, logging |
54
- | `PostToolUse` | Fired after successful tool execution | Logging, output processing, monitoring |
55
- | `PostToolUseFailure` | Fired when tool execution fails | Error handling, alerting, remediation |
56
- | `Notification` | Fired when notifications are sent | Notification customization, logging |
57
- | `UserPromptSubmit` | Fired when user submits a prompt | Input processing, validation, context injection |
58
- | `SessionStart` | Fired when a new session starts | Initialization, context setup |
59
- | `Stop` | Fired before Qwen concludes its response | Finalization, cleanup |
60
- | `StopFailure` | Fired when turn ends due to API error | Error logging, alerting, rate limit handling |
61
- | `SubagentStart` | Fired when a subagent starts | Subagent initialization |
62
- | `SubagentStop` | Fired when a subagent stops | Subagent finalization |
63
- | `PreCompact` | Fired before conversation compaction | Pre-compaction processing |
64
- | `PostCompact` | Fired after conversation compaction | Summary archiving, usage statistics |
65
- | `SessionEnd` | Fired when a session ends | Cleanup, reporting |
66
- | `PermissionRequest` | Fired when permission dialogs are displayed | Permission automation, policy enforcement |
139
+ Hooks fire at specific points during a Qwen Code session. Different events support different matchers to filter trigger conditions.
140
+
141
+ | Event | Triggered When | Matcher Target |
142
+ | :------------------- | :---------------------------------------- | :-------------------------------------------------------- |
143
+ | `PreToolUse` | Before tool execution | Tool name (`WriteFile`, `ReadFile`, `Bash`, etc.) |
144
+ | `PostToolUse` | After successful tool execution | Tool name |
145
+ | `PostToolUseFailure` | After tool execution fails | Tool name |
146
+ | `UserPromptSubmit` | After user submits prompt | None (always fires) |
147
+ | `SessionStart` | When session starts or resumes | Source (`startup`, `resume`, `clear`, `compact`) |
148
+ | `SessionEnd` | When session ends | Reason (`clear`, `logout`, `prompt_input_exit`, etc.) |
149
+ | `Stop` | When Claude prepares to conclude response | None (always fires) |
150
+ | `SubagentStart` | When subagent starts | Agent type (`Bash`, `Explorer`, `Plan`, etc.) |
151
+ | `SubagentStop` | When subagent stops | Agent type |
152
+ | `PreCompact` | Before conversation compaction | Trigger (`manual`, `auto`) |
153
+ | `Notification` | When notifications are sent | Type (`permission_prompt`, `idle_prompt`, `auth_success`) |
154
+ | `PermissionRequest` | When permission dialog is shown | Tool name |
155
+
156
+ ### Matcher Patterns
157
+
158
+ `matcher` is a regular expression used to filter trigger conditions.
159
+
160
+ | Event Type | Events | Matcher Support | Matcher Target |
161
+ | :------------------ | :--------------------------------------------------------------------- | :-------------- | :------------------------------------------------------- |
162
+ | Tool Events | `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | Regex | Tool name: `WriteFile`, `ReadFile`, `Bash`, etc. |
163
+ | Subagent Events | `SubagentStart`, `SubagentStop` | ✅ Regex | Agent type: `Bash`, `Explorer`, etc. |
164
+ | Session Events | `SessionStart` | ✅ Regex | Source: `startup`, `resume`, `clear`, `compact` |
165
+ | Session Events | `SessionEnd` | ✅ Regex | Reason: `clear`, `logout`, `prompt_input_exit`, etc. |
166
+ | Notification Events | `Notification` | ✅ Exact match | Type: `permission_prompt`, `idle_prompt`, `auth_success` |
167
+ | Compact Events | `PreCompact` | ✅ Exact match | Trigger: `manual`, `auto` |
168
+ | Prompt Events | `UserPromptSubmit` | ❌ No | N/A |
169
+ | Stop Events | `Stop` | ❌ No | N/A |
170
+
171
+ **Matcher Syntax:**
172
+
173
+ - Empty string `""` or `"*"` matches all events of that type
174
+ - Standard regex syntax supported (e.g., `^Bash$`, `Read.*`, `(WriteFile|Edit)`)
175
+
176
+ **Examples:**
177
+
178
+ ```json
179
+ {
180
+ "hooks": {
181
+ "PreToolUse": [
182
+ {
183
+ "matcher": "^Bash$",
184
+ "hooks": [
185
+ {
186
+ "type": "command",
187
+ "command": "echo 'bash check' >> /tmp/hooks.log"
188
+ }
189
+ ]
190
+ },
191
+ {
192
+ "matcher": "Write.*",
193
+ "hooks": [
194
+ {
195
+ "type": "command",
196
+ "command": "echo 'write check' >> /tmp/hooks.log"
197
+ }
198
+ ]
199
+ },
200
+ {
201
+ "matcher": "*",
202
+ "hooks": [
203
+ { "type": "command", "command": "echo 'all tools' >> /tmp/hooks.log" }
204
+ ]
205
+ }
206
+ ],
207
+ "SubagentStart": [
208
+ {
209
+ "matcher": "^(Bash|Explorer)$",
210
+ "hooks": [
211
+ {
212
+ "type": "command",
213
+ "command": "echo 'subagent check' >> /tmp/hooks.log"
214
+ }
215
+ ]
216
+ }
217
+ ]
218
+ }
219
+ }
220
+ ```
67
221
 
68
222
  ## Input/Output Rules
69
223
 
70
224
  ### Hook Input Structure
71
225
 
72
- All hooks receive standardized input in JSON format through stdin. Common fields included in every hook event:
226
+ All hooks receive standardized input in JSON format through stdin (command) or POST body (http).
227
+
228
+ **Common Fields:**
73
229
 
74
230
  ```json
75
231
  {
@@ -81,7 +237,39 @@ All hooks receive standardized input in JSON format through stdin. Common fields
81
237
  }
82
238
  ```
83
239
 
84
- Event-specific fields are added based on the hook type. Below are the event-specific fields for each hook event:
240
+ Event-specific fields are added based on the hook type. When running in a subagent, `agent_id` and `agent_type` are additionally included.
241
+
242
+ ### Hook Output Structure
243
+
244
+ Hook output is returned via `stdout` (command) or HTTP response body (http) as JSON.
245
+
246
+ **Exit Code Behavior (Command Hooks):**
247
+
248
+ | Exit Code | Behavior |
249
+ | :-------- | :------------------------------------------------------------------------------------ |
250
+ | `0` | Success. Parse JSON in `stdout` to control behavior. |
251
+ | `2` | **Blocking error**. Ignores `stdout`, passes `stderr` as error feedback to the model. |
252
+ | Other | Non-blocking error. `stderr` only shown in debug mode, execution continues. |
253
+
254
+ **Output Structure:**
255
+
256
+ Hook output supports three categories of fields:
257
+
258
+ 1. **Common Fields**: `continue`, `stopReason`, `suppressOutput`, `systemMessage`
259
+ 2. **Top-level Decision**: `decision`, `reason` (used by some events)
260
+ 3. **Event-specific Control**: `hookSpecificOutput` (must include `hookEventName`)
261
+
262
+ ```json
263
+ {
264
+ "continue": true,
265
+ "decision": "allow",
266
+ "reason": "Operation approved",
267
+ "hookSpecificOutput": {
268
+ "hookEventName": "PreToolUse",
269
+ "additionalContext": "Additional context information"
270
+ }
271
+ }
272
+ ```
85
273
 
86
274
  ### Individual Hook Event Details
87
275
 
@@ -115,11 +303,8 @@ Event-specific fields are added based on the hook type. Below are the event-spec
115
303
  {
116
304
  "hookSpecificOutput": {
117
305
  "hookEventName": "PreToolUse",
118
- "permissionDecision": "allow",
119
- "permissionDecisionReason": "My reason here",
120
- "updatedInput": {
121
- "field_to_modify": "new value"
122
- },
306
+ "permissionDecision": "deny",
307
+ "permissionDecisionReason": "Security policy blocks database writes",
123
308
  "additionalContext": "Current environment: production. Proceed with caution."
124
309
  }
125
310
  }
@@ -578,12 +763,12 @@ Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` o
578
763
  "hooks": {
579
764
  "PreToolUse": [
580
765
  {
581
- "matcher": "^bash$", // Regex to match tool names
582
- "sequential": false, // Whether to run hooks sequentially
766
+ "matcher": "^Bash$",
767
+ "sequential": false,
583
768
  "hooks": [
584
769
  {
585
770
  "type": "command",
586
- "command": "/path/to/script.sh",
771
+ "command": "/path/to/security-check.sh",
587
772
  "name": "security-check",
588
773
  "description": "Run security checks before tool execution",
589
774
  "timeout": 30000
@@ -606,69 +791,58 @@ Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` o
606
791
  }
607
792
  ```
608
793
 
609
- ### Matcher Patterns
794
+ ## Hook Execution
610
795
 
611
- Matchers allow filtering hooks based on context. Not all hook events support matchers:
796
+ ### Parallel vs Sequential Execution
797
+
798
+ - By default, hooks execute in parallel for better performance
799
+ - Use `sequential: true` in hook definition to enforce order-dependent execution
800
+ - Sequential hooks can modify input for subsequent hooks in the chain
612
801
 
613
- | Event Type | Events | Matcher Support | Matcher Target (Values) |
614
- | ------------------- | ---------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------- |
615
- | Tool Events | `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest` | ✅ Yes (regex) | Tool name: `bash`, `read_file`, `write_file`, `edit`, `glob`, `grep_search`, etc. |
616
- | Subagent Events | `SubagentStart`, `SubagentStop` | ✅ Yes (regex) | Agent type: `Bash`, `Explorer`, etc. |
617
- | Session Events | `SessionStart` | ✅ Yes (regex) | Source: `startup`, `resume`, `clear`, `compact` |
618
- | Session Events | `SessionEnd` | ✅ Yes (regex) | Reason: `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` |
619
- | Notification Events | `Notification` | ✅ Yes (exact) | Type: `permission_prompt`, `idle_prompt`, `auth_success` |
620
- | Compact Events | `PreCompact` | ✅ Yes (exact) | Trigger: `manual`, `auto` |
621
- | Prompt Events | `UserPromptSubmit` | ❌ No | N/A |
622
- | Stop Events | `Stop` | ❌ No | N/A |
802
+ ### Async Hooks
623
803
 
624
- **Matcher Syntax**:
804
+ Only `command` type supports asynchronous execution. Setting `"async": true` runs the hook in the background without blocking the main flow.
625
805
 
626
- - Regex pattern matched against the target field
627
- - Empty string `""` or `"*"` matches all events of that type
628
- - Standard regex syntax supported (e.g., `^bash$`, `read.*`, `(bash|run_shell_command)`)
806
+ **Features:**
629
807
 
630
- **Examples**:
808
+ - Cannot return decision control (operation has already occurred)
809
+ - Results are injected in the next conversation turn via `systemMessage` or `additionalContext`
810
+ - Suitable for auditing, logging, background testing, etc.
811
+
812
+ **Example:**
631
813
 
632
814
  ```json
633
815
  {
634
816
  "hooks": {
635
- "PreToolUse": [
636
- {
637
- "matcher": "^bash$", // Only match bash tool
638
- "hooks": [...]
639
- },
640
- {
641
- "matcher": "read.*", // Match read_file, read_multiple_files, etc.
642
- "hooks": [...]
643
- },
644
- {
645
- "matcher": "", // Match all tools (same as "*" or omitting matcher)
646
- "hooks": [...]
647
- }
648
- ],
649
- "SubagentStart": [
817
+ "PostToolUse": [
650
818
  {
651
- "matcher": "^(Bash|Explorer)$", // Only match Bash and Explorer agents
652
- "hooks": [...]
653
- }
654
- ],
655
- "SessionStart": [
656
- {
657
- "matcher": "^(startup|resume)$", // Only match startup and resume sources
658
- "hooks": [...]
819
+ "matcher": "WriteFile|Edit",
820
+ "hooks": [
821
+ {
822
+ "type": "command",
823
+ "command": "$QWEN_PROJECT_DIR/.qwen/hooks/run-tests-async.sh",
824
+ "async": true,
825
+ "timeout": 300000
826
+ }
827
+ ]
659
828
  }
660
829
  ]
661
830
  }
662
831
  }
663
832
  ```
664
833
 
665
- ## Hook Execution
666
-
667
- ### Parallel vs Sequential Execution
668
-
669
- - By default, hooks execute in parallel for better performance
670
- - Use `sequential: true` in hook definition to enforce order-dependent execution
671
- - Sequential hooks can modify input for subsequent hooks in the chain
834
+ ```bash
835
+ #!/bin/bash
836
+ INPUT=$(cat)
837
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
838
+ if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then exit 0; fi
839
+ RESULT=$(npm test 2>&1)
840
+ if [ $? -eq 0 ]; then
841
+ echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}"
842
+ else
843
+ echo "{\"systemMessage\": \"Tests failed: $RESULT\"}"
844
+ fi
845
+ ```
672
846
 
673
847
  ### Security Model
674
848
 
@@ -676,32 +850,6 @@ Matchers allow filtering hooks based on context. Not all hook events support mat
676
850
  - Project-level hooks require trusted folder status
677
851
  - Timeouts prevent hanging hooks (default: 60 seconds)
678
852
 
679
- ### Exit Codes
680
-
681
- Hook scripts communicate their result through exit codes:
682
-
683
- | Exit Code | Meaning | Behavior |
684
- | --------- | ------------------ | ----------------------------------------------- |
685
- | `0` | Success | stdout/stderr not shown |
686
- | `2` | Blocking error | Show stderr to model and block tool call |
687
- | Other | Non-blocking error | Show stderr to user only but continue tool call |
688
-
689
- **Examples**:
690
-
691
- ```bash
692
- #!/bin/bash
693
-
694
- # Success (exit 0 is default, can be omitted)
695
- echo '{"decision": "allow"}'
696
- exit 0
697
-
698
- # Blocking error - prevents operation
699
- echo "Dangerous operation blocked by security policy" >&2
700
- exit 2
701
- ```
702
-
703
- > **Note**: If no exit code is specified, the script defaults to `0` (success).
704
-
705
853
  ## Best Practices
706
854
 
707
855
  ### Example 1: Security Validation Hook
@@ -723,24 +871,20 @@ TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input')
723
871
  # Check for potentially dangerous operations
724
872
  if echo "$TOOL_INPUT" | grep -qiE "(rm.*-rf|mv.*\/|chmod.*777)"; then
725
873
  echo '{
726
- "decision": "deny",
727
- "reason": "Potentially dangerous operation detected",
728
874
  "hookSpecificOutput": {
729
875
  "hookEventName": "PreToolUse",
730
876
  "permissionDecision": "deny",
731
- "permissionDecisionReason": "Dangerous command blocked by security policy"
877
+ "permissionDecisionReason": "Security policy blocks dangerous command"
732
878
  }
733
879
  }'
734
880
  exit 2 # Blocking error
735
881
  fi
736
882
 
737
- # Allow the operation with a log
883
+ # Log the operation
738
884
  echo "INFO: Tool $TOOL_NAME executed safely at $(date)" >> /var/log/qwen-security.log
739
885
 
740
886
  # Allow with additional context
741
887
  echo '{
742
- "decision": "allow",
743
- "reason": "Operation approved by security checker",
744
888
  "hookSpecificOutput": {
745
889
  "hookEventName": "PreToolUse",
746
890
  "permissionDecision": "allow",
@@ -773,7 +917,36 @@ Configure in `.qwen/settings.json`:
773
917
  }
774
918
  ```
775
919
 
776
- ### Example 2: User Prompt Validation Hook
920
+ ### Example 2: HTTP Audit Hook
921
+
922
+ A PostToolUse HTTP hook that sends all tool execution records to a remote audit service:
923
+
924
+ ```json
925
+ {
926
+ "hooks": {
927
+ "PostToolUse": [
928
+ {
929
+ "matcher": "*",
930
+ "hooks": [
931
+ {
932
+ "type": "http",
933
+ "url": "https://audit.example.com/api/tool-execution",
934
+ "headers": {
935
+ "Authorization": "Bearer ${AUDIT_API_TOKEN}",
936
+ "Content-Type": "application/json"
937
+ },
938
+ "allowedEnvVars": ["AUDIT_API_TOKEN"],
939
+ "timeout": 10,
940
+ "name": "audit-logger"
941
+ }
942
+ ]
943
+ }
944
+ ]
945
+ }
946
+ }
947
+ ```
948
+
949
+ ### Example 3: User Prompt Validation Hook
777
950
 
778
951
  A UserPromptSubmit hook that validates user prompts for sensitive information and provides context for long prompts:
779
952
 
@@ -831,3 +1004,5 @@ exit(0)
831
1004
  - Verify hook script permissions and executability
832
1005
  - Ensure proper JSON formatting in hook outputs
833
1006
  - Use specific matcher patterns to avoid unintended hook execution
1007
+ - Use `--debug` mode to see detailed hook matching and execution information
1008
+ - Temporarily disable all hooks: add `"disableAllHooks": true` in settings