@qwen-code/qwen-code 0.15.11 → 0.15.12-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.
package/README.md CHANGED
@@ -428,12 +428,13 @@ and adjust it to the context length configured on your local server.
428
428
 
429
429
  ## Usage
430
430
 
431
- As an open-source terminal agent, you can use Qwen Code in four primary ways:
431
+ As an open-source terminal agent, you can use Qwen Code in five primary ways:
432
432
 
433
433
  1. Interactive mode (terminal UI)
434
434
  2. Headless mode (scripts, CI)
435
435
  3. IDE integration (VS Code, Zed)
436
436
  4. SDKs (TypeScript, Python, Java)
437
+ 5. Daemon mode — `qwen serve` exposes ACP over HTTP+SSE so multiple clients share one agent (experimental)
437
438
 
438
439
  #### Interactive mode
439
440
 
@@ -461,6 +462,20 @@ Use Qwen Code inside your editor (VS Code, Zed, and JetBrains IDEs):
461
462
  - [Use in Zed](https://qwenlm.github.io/qwen-code-docs/en/users/integration-zed/)
462
463
  - [Use in JetBrains IDEs](https://qwenlm.github.io/qwen-code-docs/en/users/integration-jetbrains/)
463
464
 
465
+ #### Daemon mode (`qwen serve`, experimental)
466
+
467
+ ```bash
468
+ cd your-project/
469
+ qwen serve
470
+ # → qwen serve listening on http://127.0.0.1:4170 (mode=http-bridge)
471
+ ```
472
+
473
+ Run Qwen Code as a local HTTP daemon so IDE plugins, web UIs, CI scripts and custom CLIs all share **one** agent session over HTTP+SSE — instead of each spawning their own subprocess. Loopback bind has no auth by default (set `QWEN_SERVER_TOKEN` to enable bearer auth even on loopback); remote binds (`--hostname 0.0.0.0`) **require** a token — boot refuses without one. See:
474
+
475
+ - [Daemon mode user guide](https://qwenlm.github.io/qwen-code-docs/en/users/qwen-serve)
476
+ - [HTTP protocol reference](https://qwenlm.github.io/qwen-code-docs/en/developers/qwen-serve-protocol)
477
+ - [DaemonClient TypeScript quickstart](https://qwenlm.github.io/qwen-code-docs/en/developers/examples/daemon-client-quickstart)
478
+
464
479
  #### SDKs
465
480
 
466
481
  Build on top of Qwen Code with the available SDKs:
@@ -13,7 +13,8 @@ export default {
13
13
  'integration-vscode': 'Visual Studio Code',
14
14
  'integration-zed': 'Zed IDE',
15
15
  'integration-jetbrains': 'JetBrains IDEs',
16
- 'integration-github-action': 'Github Actions',
16
+ 'integration-github-action': 'GitHub Actions',
17
+ 'qwen-serve': 'Daemon mode (qwen serve)',
17
18
  'Code with Qwen Code': {
18
19
  type: 'separator',
19
20
  title: 'Code with Qwen Code', // Title is optional
@@ -583,11 +583,16 @@ For authentication-related variables (like `OPENAI_*`) and the recommended `.qwe
583
583
  | `SEATBELT_PROFILE` | (macOS specific) Switches the Seatbelt (`sandbox-exec`) profile on macOS. | `permissive-open`: (Default) Restricts writes to the project folder (and a few other folders, see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) but allows other operations. `strict`: Uses a strict profile that declines operations by default. `<profile_name>`: Uses a custom profile. To define a custom profile, create a file named `sandbox-macos-<profile_name>.sb` in your project's `.qwen/` directory (e.g., `my-project/.qwen/sandbox-macos-custom.sb`). |
584
584
  | `DEBUG` or `DEBUG_MODE` | (often used by underlying libraries or the CLI itself) Set to `true` or `1` to enable verbose debug logging, which can be helpful for troubleshooting. | **Note:** These variables are automatically excluded from project `.env` files by default to prevent interference with the CLI behavior. Use `.qwen/.env` files if you need to set these for Qwen Code specifically. |
585
585
  | `NO_COLOR` | Set to any value to disable all color output in the CLI. | |
586
+ | `FORCE_HYPERLINK` | Override the OSC 8 clickable-link detection in the markdown renderer. Set to `1` (or any non-zero value, or empty string) to force-enable, `0` to force-disable. Honors `NO_COLOR` / `QWEN_DISABLE_HYPERLINKS` opt-outs above it. | Use this to opt into OSC 8 inside `tmux` / GNU `screen` (auto-detection refuses by default because the host terminal's capabilities are hidden behind the multiplexer). Requires `set -g allow-passthrough on` on tmux 3.3+. Also enables Hyper, which isn't auto-detected. |
587
+ | `QWEN_DISABLE_HYPERLINKS` | Set to `1` to hard-disable OSC 8 clickable hyperlinks in the markdown renderer even on terminals that auto-detect as capable. | Useful when a terminal advertises support but breaks on long URLs, or when piping output through an intermediary that mangles escape sequences. The renderer falls back to plain `label (url)` rendering. |
586
588
  | `CLI_TITLE` | Set to a string to customize the title of the CLI. | |
587
589
  | `CODE_ASSIST_ENDPOINT` | Specifies the endpoint for the code assist server. | This is useful for development and testing. |
588
590
  | `QWEN_CODE_MAX_OUTPUT_TOKENS` | Overrides the default maximum output tokens per response. When not set, Qwen Code uses an adaptive strategy: starts with 8K tokens and automatically retries with 64K if the response is truncated. Set this to a specific value (e.g., `16000`) to use a fixed limit instead. | Takes precedence over the capped default (8K) but is overridden by `samplingParams.max_tokens` in settings. Disables automatic escalation when set. Example: `export QWEN_CODE_MAX_OUTPUT_TOKENS=16000` |
589
591
  | `QWEN_CODE_UNATTENDED_RETRY` | Set to `true` or `1` to enable persistent retry mode. When enabled, transient API capacity errors (HTTP 429 Rate Limit and 529 Overloaded) are retried indefinitely with exponential backoff (capped at 5 minutes per retry) and heartbeat keepalives every 30 seconds on stderr. | Designed for CI/CD pipelines and background automation where long-running tasks should survive temporary API outages. Must be set explicitly — `CI=true` alone does **not** activate this mode. See [Headless Mode](../features/headless#persistent-retry-mode) for details. Example: `export QWEN_CODE_UNATTENDED_RETRY=1` |
590
- | `QWEN_CODE_PROFILE_STARTUP` | Set to `1` to enable startup performance profiling. Writes a JSON timing report to `~/.qwen/startup-perf/` with per-phase durations. | Only active inside the sandbox child process. Zero overhead when not set. Example: `export QWEN_CODE_PROFILE_STARTUP=1` |
592
+ | `QWEN_CODE_PROFILE_STARTUP` | Set to `1` to enable startup performance profiling. Writes a JSON timing report to `~/.qwen/startup-perf/` with per-phase durations. | Only active inside the sandbox child process (or with `QWEN_CODE_PROFILE_STARTUP_OUTER=1`). Zero overhead when not set. Example: `export QWEN_CODE_PROFILE_STARTUP=1` |
593
+ | `QWEN_CODE_PROFILE_STARTUP_OUTER` | Set to `1` together with `QWEN_CODE_PROFILE_STARTUP=1` to also collect a startup profile in the outer (pre-sandbox) process. Outer-process reports get an `outer-` filename prefix to keep them distinct from the sandbox child's report. | Off by default — only the sandbox child collects, to avoid duplicate reports. Useful for local development where the cli isn't relaunched into a sandbox. |
594
+ | `QWEN_CODE_PROFILE_STARTUP_NO_HEAP` | Set to `1` together with `QWEN_CODE_PROFILE_STARTUP=1` to skip the per-checkpoint `process.memoryUsage()` snapshots. Useful when measuring the profiler's own Heisenberg overhead. | Off by default. Heap snapshots cost ~50 µs each (well below 1% of total startup) so most users should leave this alone. |
595
+ | `QWEN_CODE_LEGACY_MCP_BLOCKING` | Set to `1` to restore the pre-progressive-MCP behavior where `Config.initialize()` waits synchronously for every configured MCP server's discover handshake before returning. | Off by default. Modern qwen-code lets MCP servers come online in the background while the UI is already interactive; the model sees each batch of new tools within ~16 ms of the server settling. This flag is kept as a rollback escape hatch for ≥ 1 release. Example: `export QWEN_CODE_LEGACY_MCP_BLOCKING=1` |
591
596
 
592
597
  When both user-level `.env` files define the same variable, the Qwen-specific
593
598
  file wins: `<QWEN_HOME>/.env` (or `~/.qwen/.env` when `QWEN_HOME` is unset) is
@@ -45,12 +45,11 @@ Commands for adjusting interface appearance and work environment.
45
45
 
46
46
  Commands specifically for controlling interface and output language.
47
47
 
48
- | Command | Description | Usage Examples |
49
- | --------------------- | --------------------------------------------------------------------------- | -------------------------- |
50
- | `/language` | View or change language settings | `/language` |
51
- | → `ui [language]` | Set UI interface language | `/language ui zh-CN` |
52
- | → `output [language]` | Set LLM output language | `/language output Chinese` |
53
- | → `translate on/off` | Toggle AI translation for dynamic slash command descriptions (default: off) | `/language translate on` |
48
+ | Command | Description | Usage Examples |
49
+ | --------------------- | -------------------------------- | -------------------------- |
50
+ | `/language` | View or change language settings | `/language` |
51
+ | → `ui [language]` | Set UI interface language | `/language ui zh-CN` |
52
+ | → `output [language]` | Set LLM output language | `/language output Chinese` |
54
53
 
55
54
  - Available built-in UI languages: `zh-CN` (Simplified Chinese), `en-US` (English), `ru-RU` (Russian), `de-DE` (German), `ja-JP` (Japanese), `pt-BR` (Portuguese - Brazil), `fr-FR` (French), `ca-ES` (Catalan)
56
55
  - Output language examples: `Chinese`, `English`, `Japanese`, etc.
@@ -30,13 +30,14 @@ Hooks are user-defined scripts or programs that are automatically executed by Qw
30
30
 
31
31
  ## Hook Types
32
32
 
33
- Qwen Code supports three hook executor types:
33
+ Qwen Code supports four hook executor types:
34
34
 
35
35
  | Type | Description |
36
36
  | :--------- | :--------------------------------------------------------------------------------------------- |
37
37
  | `command` | Execute a shell command. Receives JSON via `stdin`, returns results via `stdout`. |
38
38
  | `http` | Send JSON as a `POST` request body to a specified URL. Returns results via HTTP response body. |
39
39
  | `function` | Directly call a registered JavaScript function (session-level hooks only). |
40
+ | `prompt` | Use an LLM to evaluate hook input and return a decision. |
40
41
 
41
42
  ### Command Hooks
42
43
 
@@ -134,6 +135,102 @@ Function hooks directly call registered JavaScript/TypeScript functions. They ar
134
135
 
135
136
  **Note**: For most use cases, use **command hooks** or **HTTP hooks** instead, which can be configured in settings files.
136
137
 
138
+ ### Prompt Hooks
139
+
140
+ Prompt hooks use an LLM to evaluate hook input and return a decision. This is useful for making intelligent decisions based on context, such as determining whether to allow or block an operation.
141
+
142
+ **How it works:**
143
+
144
+ 1. The hook input JSON is injected into your prompt using the `$ARGUMENTS` placeholder
145
+ 2. The prompt is sent to an LLM (default: your current model)
146
+ 3. The LLM returns a JSON response with the decision
147
+ 4. Qwen Code processes the decision and continues or blocks execution accordingly
148
+
149
+ **Configuration:**
150
+
151
+ | Field | Type | Required | Description |
152
+ | :-------------- | :--------- | :------- | :-------------------------------------------------- |
153
+ | `type` | `"prompt"` | Yes | Hook type |
154
+ | `prompt` | `string` | Yes | Prompt sent to LLM. Use `$ARGUMENTS` for hook input |
155
+ | `model` | `string` | No | Model to use (defaults to your current model) |
156
+ | `timeout` | `number` | No | Timeout in seconds, default 30 |
157
+ | `name` | `string` | No | Hook name (for logging) |
158
+ | `description` | `string` | No | Hook description |
159
+ | `statusMessage` | `string` | No | Status message displayed during execution |
160
+
161
+ **Response Format:**
162
+
163
+ The LLM must return JSON with the following structure:
164
+
165
+ ```json
166
+ {
167
+ "ok": true,
168
+ "reason": "Explanation of the decision",
169
+ "additionalContext": "Optional context to inject into the conversation"
170
+ }
171
+ ```
172
+
173
+ | Field | Description |
174
+ | :------------------ | :------------------------------------------------------------------------- |
175
+ | `ok` | `true` to allow/continue, `false` to block/stop |
176
+ | `reason` | Required when `ok` is `false`. Shown to the model to explain the block |
177
+ | `additionalContext` | Optional. Additional context to inject into the conversation when allowing |
178
+
179
+ **Supported Events:**
180
+
181
+ Prompt hooks can be used with most hook events, including:
182
+
183
+ - `PreToolUse` - Evaluate whether to allow a tool call
184
+ - `PostToolUse` - Evaluate tool results and potentially inject context
185
+ - `Stop` - Determine whether to continue or stop
186
+ - `SubagentStop` - Evaluate subagent results
187
+ - `UserPromptSubmit` - Evaluate or enrich user prompts
188
+
189
+ **Example: Stop Hook**
190
+
191
+ ```json
192
+ {
193
+ "hooks": {
194
+ "Stop": [
195
+ {
196
+ "hooks": [
197
+ {
198
+ "type": "prompt",
199
+ "prompt": "You are evaluating whether Qwen Code should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
200
+ "timeout": 30
201
+ }
202
+ ]
203
+ }
204
+ ]
205
+ }
206
+ }
207
+ ```
208
+
209
+ When `ok` is `false`, Qwen Code will continue working and use the `reason` as context for the next response.
210
+
211
+ **Example: PreToolUse Hook**
212
+
213
+ ```json
214
+ {
215
+ "hooks": {
216
+ "PreToolUse": [
217
+ {
218
+ "matcher": "Bash",
219
+ "hooks": [
220
+ {
221
+ "type": "prompt",
222
+ "prompt": "Evaluate this tool call for security concerns. Tool input: $ARGUMENTS\n\nCheck for:\n- Dangerous commands (rm -rf, curl | sh, etc.)\n- Unauthorized access attempts\n- Data exfiltration patterns\n\nRespond with {\"ok\": true} if safe, or {\"ok\": false, \"reason\": \"concern\"} if blocked.",
223
+ "model": "sonnet",
224
+ "timeout": 30,
225
+ "name": "security-evaluator"
226
+ }
227
+ ]
228
+ }
229
+ ]
230
+ }
231
+ }
232
+ ```
233
+
137
234
  ## Hook Events
138
235
 
139
236
  Hooks fire at specific points during a Qwen Code session. Different events support different matchers to filter trigger conditions.
@@ -152,6 +249,8 @@ Hooks fire at specific points during a Qwen Code session. Different events suppo
152
249
  | `PreCompact` | Before conversation compaction | Trigger (`manual`, `auto`) |
153
250
  | `Notification` | When notifications are sent | Type (`permission_prompt`, `idle_prompt`, `auth_success`) |
154
251
  | `PermissionRequest` | When permission dialog is shown | Tool name |
252
+ | `TodoCreated` | When a new todo item is created | None (always fires) |
253
+ | `TodoCompleted` | When a todo item is marked as completed | None (always fires) |
155
254
 
156
255
  ### Matcher Patterns
157
256
 
@@ -165,6 +264,7 @@ Hooks fire at specific points during a Qwen Code session. Different events suppo
165
264
  | Session Events | `SessionEnd` | ✅ Regex | Reason: `clear`, `logout`, `prompt_input_exit`, etc. |
166
265
  | Notification Events | `Notification` | ✅ Exact match | Type: `permission_prompt`, `idle_prompt`, `auth_success` |
167
266
  | Compact Events | `PreCompact` | ✅ Exact match | Trigger: `manual`, `auto` |
267
+ | Todo Events | `TodoCreated`, `TodoCompleted` | ❌ No | N/A |
168
268
  | Prompt Events | `UserPromptSubmit` | ❌ No | N/A |
169
269
  | Stop Events | `Stop` | ❌ No | N/A |
170
270
 
@@ -754,6 +854,204 @@ Hook output supports three categories of fields:
754
854
  }
755
855
  ```
756
856
 
857
+ #### TodoCreated
858
+
859
+ **Purpose**: Executed when a new todo item is created via the `todo_write` tool. Allows validation, logging, or blocking of todo creation.
860
+
861
+ Todo hooks run in two phases:
862
+
863
+ - `validation`: runs before persistence. Use this phase for validation only; returning `block` or `deny` prevents the write.
864
+ - `postWrite`: runs after persistence. Use this phase for side effects such as logging or syncing; `block` or `deny` is ignored in this phase.
865
+
866
+ **Event-specific fields**:
867
+
868
+ ```json
869
+ {
870
+ "todo_id": "unique identifier for the todo item",
871
+ "todo_content": "content/description of the todo item",
872
+ "todo_status": "pending | in_progress | completed",
873
+ "all_todos": "array of all todo items in the current list",
874
+ "phase": "validation | postWrite"
875
+ }
876
+ ```
877
+
878
+ **Output Options**:
879
+
880
+ - `decision`: "allow", "block", or "deny"
881
+ - `reason`: human-readable explanation for the decision (required when blocking)
882
+
883
+ **Blocking Behavior**:
884
+
885
+ During the `validation` phase, when `decision` is `block` or `deny` (exit code 2), todo creation is prevented. The todo list remains unchanged, and the reason is provided as feedback to the model.
886
+
887
+ During the `postWrite` phase, the todo has already been persisted. Hooks may still return output, but `block` / `deny` does not undo the write and should not be used for validation.
888
+
889
+ **Example Output (Allow)**:
890
+
891
+ ```json
892
+ {
893
+ "decision": "allow",
894
+ "reason": "Todo content validated successfully"
895
+ }
896
+ ```
897
+
898
+ **Example Output (Block)**:
899
+
900
+ ```json
901
+ {
902
+ "decision": "block",
903
+ "reason": "Todo content too short. Minimum 5 characters required."
904
+ }
905
+ ```
906
+
907
+ **Example Hook Script**:
908
+
909
+ ```bash
910
+ #!/bin/bash
911
+ # ~/.qwen/hooks/todo-validator.sh
912
+ # Validates todo content before creation
913
+
914
+ INPUT=$(cat)
915
+ CONTENT=$(echo "$INPUT" | jq -r '.todo_content')
916
+
917
+ # Check minimum length
918
+ if [ ${#CONTENT} -lt 5 ]; then
919
+ echo '{"decision": "block", "reason": "Todo content must be at least 5 characters"}'
920
+ exit 2
921
+ fi
922
+
923
+ # Block test-related todos
924
+ if [[ "$CONTENT" =~ "test" ]]; then
925
+ echo '{"decision": "block", "reason": "Test todos are not allowed in production"}'
926
+ exit 2
927
+ fi
928
+
929
+ echo '{"decision": "allow"}'
930
+ exit 0
931
+ ```
932
+
933
+ **Example Configuration**:
934
+
935
+ ```json
936
+ {
937
+ "hooks": {
938
+ "TodoCreated": [
939
+ {
940
+ "hooks": [
941
+ {
942
+ "type": "command",
943
+ "command": "$HOME/.qwen/hooks/todo-validator.sh",
944
+ "name": "todo-validator",
945
+ "timeout": 5000
946
+ }
947
+ ]
948
+ }
949
+ ]
950
+ }
951
+ }
952
+ ```
953
+
954
+ #### TodoCompleted
955
+
956
+ **Purpose**: Executed when a todo item is marked as completed. Allows validation, logging, or blocking of todo completion.
957
+
958
+ Todo hooks run in two phases:
959
+
960
+ - `validation`: runs before persistence. Use this phase for validation only; returning `block` or `deny` prevents the write.
961
+ - `postWrite`: runs after persistence. Use this phase for side effects such as logging or syncing; `block` or `deny` is ignored in this phase.
962
+
963
+ **Event-specific fields**:
964
+
965
+ ```json
966
+ {
967
+ "todo_id": "unique identifier for the todo item",
968
+ "todo_content": "content/description of the todo item",
969
+ "previous_status": "pending | in_progress (status before completion)",
970
+ "all_todos": "array of all todo items in the current list",
971
+ "phase": "validation | postWrite"
972
+ }
973
+ ```
974
+
975
+ **Output Options**:
976
+
977
+ - `decision`: "allow", "block", or "deny"
978
+ - `reason`: human-readable explanation for the decision (required when blocking)
979
+
980
+ **Blocking Behavior**:
981
+
982
+ During the `validation` phase, when `decision` is `block` or `deny` (exit code 2), todo completion is prevented. The todo item remains in its previous status, and the reason is provided as feedback to the model.
983
+
984
+ During the `postWrite` phase, the todo has already been persisted. Hooks may still return output, but `block` / `deny` does not undo the write and should not be used for validation.
985
+
986
+ **Example Output (Allow)**:
987
+
988
+ ```json
989
+ {
990
+ "decision": "allow",
991
+ "reason": "Todo completion approved"
992
+ }
993
+ ```
994
+
995
+ **Example Output (Block)**:
996
+
997
+ ```json
998
+ {
999
+ "decision": "block",
1000
+ "reason": "Cannot complete this todo until dependent tasks are finished."
1001
+ }
1002
+ ```
1003
+
1004
+ **Example Hook Script**:
1005
+
1006
+ ```bash
1007
+ #!/bin/bash
1008
+ # ~/.qwen/hooks/todo-completion-validator.sh
1009
+ # Validates todo completion conditions
1010
+
1011
+ INPUT=$(cat)
1012
+ TODO_ID=$(echo "$INPUT" | jq -r '.todo_id')
1013
+ ALL_TODOS=$(echo "$INPUT" | jq -r '.all_todos')
1014
+
1015
+ # Check if there are incomplete dependent todos (example logic)
1016
+ INCOMPLETE_COUNT=$(echo "$ALL_TODOS" | jq '[.[] | select(.status != "completed")] | length')
1017
+
1018
+ if [ "$INCOMPLETE_COUNT" -gt 5 ]; then
1019
+ echo '{"decision": "block", "reason": "Too many incomplete todos. Complete other tasks first."}'
1020
+ exit 2
1021
+ fi
1022
+
1023
+ echo '{"decision": "allow"}'
1024
+ exit 0
1025
+ ```
1026
+
1027
+ **Example Configuration**:
1028
+
1029
+ ```json
1030
+ {
1031
+ "hooks": {
1032
+ "TodoCompleted": [
1033
+ {
1034
+ "hooks": [
1035
+ {
1036
+ "type": "command",
1037
+ "command": "$HOME/.qwen/hooks/todo-completion-validator.sh",
1038
+ "name": "completion-validator",
1039
+ "timeout": 5000
1040
+ }
1041
+ ]
1042
+ }
1043
+ ]
1044
+ }
1045
+ }
1046
+ ```
1047
+
1048
+ **Use Cases**:
1049
+
1050
+ - **Logging**: Track todo creation and completion for audit or analytics
1051
+ - **Validation**: Enforce content quality standards (minimum length, required keywords)
1052
+ - **Workflow Control**: Block completion until prerequisites are met
1053
+ - **Integration**: Sync todos with external task management systems (Jira, Trello, etc.)
1054
+
757
1055
  ## Hook Configuration
758
1056
 
759
1057
  Hooks are configured in Qwen Code settings, typically in `.qwen/settings.json` or user configuration files:
@@ -55,22 +55,6 @@ Detection priority:
55
55
  3. System locale via JavaScript Intl API
56
56
  4. Default: English
57
57
 
58
- ### Dynamic Command Translation
59
-
60
- Dynamic slash command descriptions from skills, extensions, file commands, and
61
- MCP prompts can be translated with AI. This is **off by default** to avoid
62
- unexpected model calls, latency, and token usage.
63
-
64
- ```bash
65
- /language translate status # Show current status
66
- /language translate on # Enable AI translation for dynamic descriptions
67
- /language translate off # Disable AI translation
68
- ```
69
-
70
- Use `/language translate cache refresh` to re-translate cached dynamic
71
- descriptions after enabling translation, or `/language translate cache clear` to
72
- remove cached translations.
73
-
74
58
  ## LLM Output Language
75
59
 
76
60
  The LLM output language controls what language the AI assistant responds in, regardless of what language you type your questions in.
@@ -145,6 +129,29 @@ User directory takes precedence over built-in translations.
145
129
  > Contributions are welcome! If you’d like to improve built-in translations or add new languages.
146
130
  > For a concrete example, see [PR #1238: feat(i18n): add Russian language support](https://github.com/QwenLM/qwen-code/pull/1238).
147
131
 
132
+ ### Maintaining `zh-TW` (Traditional Chinese for Taiwan)
133
+
134
+ `zh-TW` is **not** an automatic OpenCC s2t conversion of `zh.js` — it is a hand-maintained Taiwan-vocabulary translation. When adding or updating keys, please follow the conventions below.
135
+
136
+ The "CI enforced?" column indicates whether `npm run check-i18n` will fail the build on a violation. Rows marked **No** are style guidance enforced by review only — typically because the offending form has a legitimate non-UI meaning (`文件` can mean "document", `打開` is colloquially fine in Taiwan).
137
+
138
+ | Avoid | Use instead | CI enforced? | Reason |
139
+ | --------------------- | --------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
140
+ | 文件 (file) | 檔案 | No | Taiwan term for filesystem files (but `文件` can legitimately mean "document") |
141
+ | 服務器 / 服务器 | 伺服器 | Yes | Taiwan term for "server" |
142
+ | 菜單 / 菜单 | 選單 | Yes | Taiwan term for "menu" |
143
+ | 鏈接 / 链接 | 連結 | Yes | Taiwan term for "link" (bare `鏈` is fine — e.g. 區塊鏈) |
144
+ | 打開 | 開啟 | No | Taiwan-preferred verb for "open" (UI); `打開` is colloquially common |
145
+ | 爲 / 啓 / 曆史 / 鏈接 | 為 / 啟 / 歷史 / 連結 | Yes | Variant Traditional forms from raw OpenCC s2t. Note: `曆` is context-dependent and correct in calendar terms (日曆, 農曆, 西曆); CI only flags the bigram `曆史`, not bare `曆`. |
146
+
147
+ If you are not a Traditional Chinese speaker and need to bootstrap a value, **do not paste raw OpenCC `s2t` output**: the default s2t profile emits variant Traditional characters (e.g. 爲, 啓) that Taiwan does not use, and never rewrites Mainland-Chinese vocabulary (服務器, 菜單). Prefer `s2twp.json` (Simplified → Taiwan with phrase mapping) as a starting point and then ask a Taiwan-Chinese speaker to review.
148
+
149
+ The `check-i18n` script (run in CI via `npm run check-i18n`) will fail the build if any of the CI-enforced substrings above end up in a `zh-TW` value. See `scripts/check-i18n.ts → ZH_TW_FORBIDDEN_PATTERNS` for the full list. If a translation legitimately needs to contain a CI-forbidden substring, add its key to `ZH_TW_ALLOWED_EXCEPTIONS` in the same file with a brief justification.
150
+
151
+ > [!note]
152
+ >
153
+ > The check uses plain substring matching, which does not understand Chinese word boundaries. A bigram pattern can therefore false-positive across compound-word boundaries — for example, `區塊鏈接口` (= `區塊鏈` + `接口`) contains the substring `鏈接` even though neither word is incorrect. If you hit a surprising CI failure of this kind, add the translation key to `ZH_TW_ALLOWED_EXCEPTIONS` rather than removing the pattern.
154
+
148
155
  ### Language Pack Format
149
156
 
150
157
  ```javascript
@@ -147,6 +147,62 @@ CLI:
147
147
  qwen mcp add --transport sse sseServer http://localhost:8080/sse --timeout 30000
148
148
  ```
149
149
 
150
+ ## Progressive availability and discovery timeouts
151
+
152
+ Qwen Code discovers MCP servers in the background after the UI is already
153
+ interactive. You see the cli's first prompt within a few hundred
154
+ milliseconds even when one of your MCP servers takes several seconds
155
+ (or never responds), and the model's tool list updates within roughly
156
+ one frame (~16 ms) of each server completing its discover handshake.
157
+
158
+ - **Interactive mode**: the UI appears immediately; an MCP status pill in
159
+ the bottom-right shows `N/M MCP servers ready` while discovery is in
160
+ flight. Sending a prompt before MCP finishes simply means the model
161
+ sees the tools that are ready _at that moment_; subsequent prompts see
162
+ more tools as servers come online.
163
+ - **Non-interactive mode** (`--prompt`, stream-json, ACP): the cli still
164
+ waits for MCP discovery to settle before sending the first prompt, so
165
+ scripted / piped invocations see the same complete tool set the
166
+ legacy synchronous behavior produced.
167
+
168
+ ### Per-server `discoveryTimeoutMs`
169
+
170
+ Each MCP server gets a discovery-only timeout that caps how long the
171
+ initial handshake (`connect` + `tools/list` + `prompts/list` +
172
+ `resources/list`) is allowed to take. Defaults:
173
+
174
+ - **stdio servers**: 30 s
175
+ - **remote HTTP / SSE servers**: 5 s (network risk is higher)
176
+
177
+ Override per server when needed:
178
+
179
+ ```jsonc
180
+ {
181
+ "mcpServers": {
182
+ "slow-stdio": {
183
+ "command": "node",
184
+ "args": ["./slow-server.js"],
185
+ "discoveryTimeoutMs": 60000,
186
+ },
187
+ "flaky-remote": {
188
+ "httpUrl": "https://example.com/mcp",
189
+ "discoveryTimeoutMs": 10000,
190
+ },
191
+ },
192
+ }
193
+ ```
194
+
195
+ The existing `timeout` field is **tool-call** timeout (used for each
196
+ `tools/call` request, default 10 minutes) and is unaffected by
197
+ `discoveryTimeoutMs` — a long-running tool invocation is not a startup
198
+ pathology.
199
+
200
+ ### Rolling back progressive MCP
201
+
202
+ If you need the old synchronous behavior (cli waits for every MCP server
203
+ before showing any UI), set `QWEN_CODE_LEGACY_MCP_BLOCKING=1` in your
204
+ environment. This is kept as an escape hatch for at least one release.
205
+
150
206
  ## Safety and control
151
207
 
152
208
  ### Trust (skip confirmations)