@qwen-code/qwen-code 0.15.12-preview.2 → 0.16.0-preview.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.
Files changed (79) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +30 -34
  2. package/bundled/qc-helper/docs/features/lsp.md +87 -10
  3. package/bundled/qc-helper/docs/qwen-serve.md +74 -24
  4. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +11 -11
  5. package/bundled/stuck/SKILL.md +124 -0
  6. package/chunks/{agent-UQY6A6OS.js → agent-ZNQPH67I.js} +15 -15
  7. package/chunks/{anthropicContentGenerator-4QE6LTVV.js → anthropicContentGenerator-ICBDZ6R2.js} +4 -4
  8. package/chunks/{askUserQuestion-QFSCBTUO.js → askUserQuestion-WQILGUSQ.js} +2 -2
  9. package/chunks/{ca-VQSV6JHA.js → ca-S3XJMT6P.js} +26 -0
  10. package/chunks/{chunk-SQNQIOD5.js → chunk-2B7UBDY5.js} +2 -2
  11. package/chunks/chunk-3MBY4GKN.js +350 -0
  12. package/chunks/{chunk-FSYKVGER.js → chunk-7QXHXMC6.js} +23 -7
  13. package/chunks/{chunk-PCL3EJGY.js → chunk-C3LHPHN2.js} +3924 -3683
  14. package/chunks/{chunk-UXW7MYAW.js → chunk-CW44BRRA.js} +1 -1
  15. package/chunks/{chunk-G27O2LD2.js → chunk-D5NTAHYL.js} +1 -1
  16. package/chunks/{chunk-CBVB66WY.js → chunk-EDYSNFEM.js} +1 -1
  17. package/chunks/{chunk-OCC4MZRS.js → chunk-F23NCRJ2.js} +1 -1
  18. package/chunks/{chunk-FYMSCRHM.js → chunk-FZIUV27X.js} +1 -1
  19. package/chunks/{chunk-SOIEFHIK.js → chunk-G7YTSRES.js} +1 -100
  20. package/chunks/{chunk-MXBWOU2L.js → chunk-JHMX4QTD.js} +15 -15
  21. package/chunks/{chunk-TPGOGCWM.js → chunk-JYQUJ5DS.js} +1 -1
  22. package/chunks/{chunk-FKVKVE6N.js → chunk-KXZ4TJB4.js} +1 -1
  23. package/chunks/{chunk-2WFU3IUH.js → chunk-MNPZ2WO6.js} +4864 -2303
  24. package/chunks/{chunk-BXNCPI75.js → chunk-NAID3ZWF.js} +2 -2
  25. package/chunks/{chunk-JMZQICAL.js → chunk-PPHYLJSS.js} +1 -1
  26. package/chunks/{chunk-CM2IESUE.js → chunk-PR4T27R7.js} +1 -1
  27. package/chunks/{chunk-CAWKL3UC.js → chunk-VTPOO6GV.js} +1 -1
  28. package/chunks/{chunk-GJXIKCKL.js → chunk-XP27SJMH.js} +76 -5
  29. package/chunks/{chunk-B7ZL7HUA.js → chunk-XVHR7ATJ.js} +1 -1
  30. package/chunks/{contextCommand-MQRG6RMG.js → contextCommand-IGBCEXI4.js} +16 -16
  31. package/chunks/{cron-create-WUTD5ZTH.js → cron-create-AVI3Q267.js} +2 -2
  32. package/chunks/{cron-delete-N3UQYCRA.js → cron-delete-ZCEGDXXV.js} +2 -2
  33. package/chunks/{cron-list-Z6RJJ4YH.js → cron-list-VN653OK5.js} +2 -2
  34. package/chunks/{de-M2IPQRBS.js → de-MNR4SMAI.js} +26 -0
  35. package/chunks/{edit-3KCBTA25.js → edit-74Q4AFHQ.js} +20 -16
  36. package/chunks/{en-N5GMPCVT.js → en-FIUWJSZR.js} +28 -0
  37. package/chunks/{enter-worktree-VWS5QZTU.js → enter-worktree-H72HXC7D.js} +15 -15
  38. package/chunks/{exit-worktree-RVXFWAPD.js → exit-worktree-FGIQO3S3.js} +15 -15
  39. package/chunks/{exitPlanMode-UL5DILDG.js → exitPlanMode-NBR2PK2D.js} +15 -15
  40. package/chunks/{fr-BTHRYEXO.js → fr-OFJFHLCR.js} +26 -0
  41. package/chunks/{geminiContentGenerator-O2OPGHJG.js → geminiContentGenerator-33RP4WKD.js} +3 -3
  42. package/chunks/{glob-57BSREPN.js → glob-WEE3CJL6.js} +15 -15
  43. package/chunks/{grep-XO5JOC7T.js → grep-DZKSBFZK.js} +15 -15
  44. package/chunks/{ja-D63TAEBO.js → ja-V6OQ6VL7.js} +26 -0
  45. package/chunks/{keychain-token-storage-DMFP5IJM.js → keychain-token-storage-335UOLJ6.js} +2 -2
  46. package/chunks/{ls-SUILOZZB.js → ls-6F3VSP6S.js} +3 -3
  47. package/chunks/{lsp-6TQBWVMZ.js → lsp-67Y7DJN5.js} +2 -2
  48. package/chunks/{monitor-BECPGO3K.js → monitor-EDZWEZVS.js} +33 -24
  49. package/chunks/{openaiContentGenerator-KEZQHIRM.js → openaiContentGenerator-5NQG3W64.js} +10 -10
  50. package/chunks/{pt-XUV7FSKC.js → pt-ZLE6SA4A.js} +26 -0
  51. package/chunks/{qwenContentGenerator-RPMRXTNH.js → qwenContentGenerator-4DPUUS6R.js} +17 -17
  52. package/chunks/{qwenOAuth2-JSQ7EPR3.js → qwenOAuth2-JE7H47TE.js} +3 -3
  53. package/chunks/{read-file-LGHEIQNH.js → read-file-CQOF7BQ2.js} +7 -7
  54. package/chunks/{ripGrep-6SFSXZ2G.js → ripGrep-KR5LKGTI.js} +15 -15
  55. package/chunks/{ru-7KHWMN3A.js → ru-A4OHIUNN.js} +26 -0
  56. package/chunks/{send-message-Q2JRAC3J.js → send-message-GB4AQZNC.js} +2 -2
  57. package/chunks/{serve-27O2AFE3.js → serve-GAD2PEST.js} +1299 -408
  58. package/chunks/{shell-J7K5KYCH.js → shell-E2HMCBGR.js} +15 -15
  59. package/chunks/{skill-2R7P4ATS.js → skill-KDZH6UZ6.js} +9 -9
  60. package/chunks/{src-CGEDVW67.js → src-LY4RU5AI.js} +96 -24
  61. package/chunks/{syntheticOutput-S4DRGMQM.js → syntheticOutput-HFL3DE7R.js} +3 -3
  62. package/chunks/{task-stop-7THHVAQS.js → task-stop-ZQF26RXS.js} +2 -2
  63. package/chunks/{todoWrite-WKUGUTPX.js → todoWrite-U4SC643O.js} +3 -3
  64. package/chunks/{tool-search-XOH3ZWVS.js → tool-search-U4XQVLFU.js} +7 -7
  65. package/chunks/{web-fetch-OZE6ZQUF.js → web-fetch-BRWZ4WSE.js} +4 -4
  66. package/chunks/{write-file-74NQ27Q2.js → write-file-NBLRMNGB.js} +20 -16
  67. package/chunks/{zh-TW-O36Q4V7E.js → zh-TW-552S24LR.js} +28 -0
  68. package/chunks/{zh-VGHU6XBB.js → zh-V32QONGV.js} +28 -0
  69. package/cli.js +8027 -5104
  70. package/locales/ca.js +40 -0
  71. package/locales/de.js +40 -0
  72. package/locales/en.js +43 -0
  73. package/locales/fr.js +41 -0
  74. package/locales/ja.js +39 -0
  75. package/locales/pt.js +39 -0
  76. package/locales/ru.js +39 -0
  77. package/locales/zh-TW.js +41 -0
  78. package/locales/zh.js +41 -0
  79. package/package.json +2 -2
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: stuck
3
+ description: Diagnose frozen, stuck, or slow Qwen Code sessions on this machine. Scans for problematic processes, high CPU/memory usage, hung subprocesses, and debug logs. Use /stuck or /stuck <PID> to focus on a specific process.
4
+ argument-hint: '[PID or symptom]'
5
+ allowedTools:
6
+ - run_shell_command
7
+ - read_file
8
+ ---
9
+
10
+ # /stuck — diagnose frozen/slow Qwen Code sessions
11
+
12
+ The user thinks another Qwen Code session on this machine is frozen, stuck, or very slow. Investigate and present a diagnostic report.
13
+
14
+ ## What to look for
15
+
16
+ Scan for other Qwen Code processes (excluding the current one — exclude the PID you see running this prompt). Since Qwen Code is a Node.js CLI (`#!/usr/bin/env node`), the process name (`comm` column) is always `node` (or `bun` if run with Bun). Identify Qwen Code sessions by looking at the `command` column for a script path inside a directory whose name starts with `qwen-code` (matches `qwen-code/`, `qwen-code-dev/`, worktree clones, etc.) — anchored to the start of the path or after `/` so unrelated names like `analyze-qwen-code/` don't false-match — or a bin invocation ending in `/qwen` (the global symlink). Avoid loose `qwen-code` substring matching: it false-positives on plugin brokers that merely pass a qwen-code path as `--cwd`.
17
+
18
+ Signs of a stuck session:
19
+
20
+ - **High CPU (>=90%) sustained** — likely an infinite loop. Sample twice, 1-2s apart, to confirm it's not a transient spike.
21
+ - **Process state `D` / `U` (uninterruptible sleep)** — often an I/O hang. Linux uses `D`, macOS/BSD uses `U`. The `state` column in `ps` output; first character matters (ignore modifiers like `+`, `s`, `<`).
22
+ - **Process state `T` (stopped)** — user probably hit Ctrl+Z by accident.
23
+ - **Process state `Z` (zombie)** — parent isn't reaping.
24
+ - **Very high RSS (>=4GB)** — possible memory leak making the session sluggish.
25
+ - **State `S` with low CPU** — the most common hang signature: a hung HTTPS request to the model API. Not a process-level red flag on its own, but combined with the user reporting "stuck", treat it as a strong signal to run the network check in step 3.
26
+ - **Stuck child process** — a hung `git`, `node`, or shell subprocess can freeze the parent. Check `pgrep -P <pid>` (then `ps -p` for state — see step 3) for each session.
27
+
28
+ ## Argument validation
29
+
30
+ If the user gave an argument, treat it as a PID **only if it consists entirely of digits 0-9**. Anything else — letters, whitespace, punctuation — fails the check, in which case treat it as a free-text symptom description (guidance for the report only, never substituted into shell commands). The strict digit-only whitelist is safer than enumerating shell metacharacters.
31
+
32
+ ## Investigation steps
33
+
34
+ **Preamble — resolve the runtime base directory.** Required for both paths below (sidecar enumeration in step 1, debug log lookup in step 3, and the PID fast path). The base directory is taken from (in priority order): `QWEN_RUNTIME_DIR` env var, the `advanced.runtimeOutputDir` setting, `QWEN_HOME` env var, and finally `~/.qwen`.
35
+
36
+ ```
37
+ RUNTIME_DIR="${QWEN_RUNTIME_DIR:-}"
38
+ [ -z "$RUNTIME_DIR" ] && command -v jq >/dev/null && RUNTIME_DIR=$(jq -r '.advanced.runtimeOutputDir // empty' "${QWEN_HOME:-$HOME/.qwen}/settings.json" 2>/dev/null)
39
+ # `advanced.runtimeOutputDir` may be `~/...` or relative; mirror Storage.resolvePath() before using in globs
40
+ [ -n "$RUNTIME_DIR" ] && RUNTIME_DIR="${RUNTIME_DIR/#\~/$HOME}"
41
+ [ -n "$RUNTIME_DIR" ] && case "$RUNTIME_DIR" in /*) ;; *) RUNTIME_DIR="$(cd "$RUNTIME_DIR" 2>/dev/null && pwd)" || RUNTIME_DIR="" ;; esac
42
+ RUNTIME_DIR="${RUNTIME_DIR:-${QWEN_HOME:-$HOME/.qwen}}"
43
+ ```
44
+
45
+ (If `jq` isn't installed, the settings layer is silently skipped — the env-var / default fallback covers the common case.)
46
+
47
+ **Fast path for targeted diagnosis** — if a digit-only PID argument was given, skip step 1 enumeration. Validate that the PID is a live current-user Qwen Code process before dumping any details:
48
+
49
+ ```
50
+ kill -0 <pid> 2>/dev/null || { echo "PID <pid> is dead, or owned by another user"; exit 0; }
51
+ ps -p <pid> -o command= -ww 2>/dev/null | grep -qE '((^|/)qwen-code[^ /]*/[^ ]*\.(js|ts|mjs|cjs)( |$)|/qwen( |$))' || { echo "PID <pid> is yours but is not a Qwen Code process — refusing to dump details"; exit 0; }
52
+ ```
53
+
54
+ If either guard prints, stop the diagnostic and surface the message verbatim. Otherwise, gather stats and the sidecar mapping, then jump to step 3:
55
+
56
+ ```
57
+ ps -p <pid> -o pid=,pcpu=,rss=,etime=,state=,comm=,command= -ww
58
+ grep -El '"pid"[[:space:]]*:[[:space:]]*<pid>\b' "$RUNTIME_DIR"/projects/*/chats/*.runtime.json 2>/dev/null
59
+ ```
60
+
61
+ Note: as in step 2, the `command=` column may include credentials passed as CLI args (e.g., `--openai-api-key=sk-…`). Redact such values to `***` before quoting them in the report.
62
+
63
+ `-E` is required so `\b` is interpreted as word boundary (BSD `grep` without `-E` treats `\b` as a backspace character, silently returning nothing on macOS). The `-l` flag returns the matching sidecar file path; the basename (stripped of `.runtime.json`) is the session ID for step 3's debug log read. If multiple sidecars match (rare — happens only after PID reuse leaves a stale file), prefer the most recently modified one: `ls -t <matches> | head -n 1`.
64
+
65
+ Otherwise (no arg, or symptom-only arg), run the general path below:
66
+
67
+ 1. **Enumerate live sessions via the runtime sidecar** (preferred, reliable):
68
+
69
+ Qwen Code writes a `runtime.json` sidecar for each interactive session at `"$RUNTIME_DIR"/projects/<sanitized-cwd>/chats/<sessionId>.runtime.json`. Each file contains `{schema_version, pid, session_id, work_dir, hostname, started_at, qwen_version}` — the authoritative source of `(pid, session_id, work_dir)` mappings.
70
+
71
+ Filter to live `(pid, sidecar-path)` pairs in one shot. Use Node (guaranteed available — qwen-code requires it) instead of `jq` (often missing on default macOS / minimal Linux) so this path doesn't silently degrade:
72
+
73
+ ```
74
+ node -e 'const fs=require("fs"); for (const f of process.argv.slice(1)) { try { const p=JSON.parse(fs.readFileSync(f,"utf8")).pid; if (p) { try { process.kill(p,0); console.log(p+" "+f); } catch {} } } catch {} }' "$RUNTIME_DIR"/projects/*/chats/*.runtime.json 2>/dev/null
75
+ ```
76
+
77
+ PID reuse is rare but possible — when you cross-reference with `ps` in step 2, skip pairs whose live PID's command line no longer looks like a Qwen Code process.
78
+
79
+ **If the command emits nothing** (no sidecars, or no live PIDs), fall through to step 2 — `ps` is the working fallback.
80
+
81
+ 2. **List Qwen Code processes via `ps`** (macOS/Linux) — used to enrich each live session with CPU/RSS/state/uptime, and to catch sessions that may have started before the sidecar feature existed:
82
+
83
+ ```
84
+ ps -xo pid=,pcpu=,rss=,etime=,state=,comm=,command= -u "$(id -u)" -ww | grep -E '((^|/)qwen-code[^ /]*/[^ ]*\.(js|ts|mjs|cjs)( |$)|/qwen( |$))' | grep -v grep
85
+ ```
86
+
87
+ `-u "$(id -u)"` restricts the scan to the current user — on shared hosts this avoids exposing other users' Qwen process paths/arguments into the chat. `-ww` disables column truncation so long "qwen" paths aren't cut off. The `comm` column will be `node` or `bun`, not `qwen`; filter to rows where the `command` column contains a qwen path (e.g., `qwen-code/dist/cli.js`, or a bin symlink ending in `/qwen`). Cross-reference with the PIDs from step 1.
88
+
89
+ Note: `ps` reports `rss` in **kilobytes** on both macOS and Linux. To report in MB, divide by 1024; to report in GB, divide by 1048576. The 4GB threshold is `4194304` KB — compare the raw `rss` value against that, or compare the GB value against 4. Do not divide once and then compare against 4; that would flag every process >4MB as "very high RSS".
90
+
91
+ Note: full command lines may contain credentials passed as CLI args (e.g., `--openai-api-key=sk-…`). Redact such values to `***` before quoting them in the report.
92
+
93
+ 3. **For anything suspicious**, gather more context. If the process state alone explains the problem (`T` = accidentally stopped, `Z` = parent not reaping), skip directly to the report — child / log / stack inspection adds nothing. Otherwise:
94
+ - Child processes (with state, so a hung `git` / `node` shows up): `CHILDREN=$(pgrep -P <pid> | tr '\n' ',' | sed 's/,$//'); [ -n "$CHILDREN" ] && ps -p "$CHILDREN" -o pid=,ppid=,pcpu=,state=,etime=,command= -ww`. Single `ps` call (avoids forking one per child) and `-ww` so long child command lines aren't truncated.
95
+ - If high CPU: sample again after 1-2s to confirm it's sustained
96
+ - **Network hang** — if CPU is low and state is `S` despite the user reporting "stuck", the most likely cause is a hung HTTPS request to the model API. macOS: `lsof -nP -i -p <pid> 2>/dev/null | head -20` (the `-nP` flags skip reverse-DNS and port lookups, which can themselves hang). If `lsof` itself feels slow, prefix with `timeout 10` (or `gtimeout 10` on macOS with Homebrew coreutils). Linux: `ss -tnp 2>/dev/null | grep "pid=<pid>,"`. Note that `ss -tnp`'s `-p` requires root or `CAP_NET_ADMIN` — without it, the PID column shows `-` and the grep returns empty. If you see no matches but `ss -t 2>/dev/null` does show ESTABLISHED sockets, fall back to `lsof -nP -i -p <pid>` rather than reporting "no connections". A long-lived `ESTABLISHED` connection to a model host (dashscope, openai, anthropic, etc.) with no recent traffic is the smoking gun.
97
+ - **Debug log** — start with `"$RUNTIME_DIR"/debug/latest` (symlink to the most recent session); if it matches the suspicious PID's session, that's usually the right one. Otherwise infer the session ID from the sidecar and read `"$RUNTIME_DIR"/debug/<session-id>.txt`. Bound the read with `tail -n 200 <path>` — debug logs can be GB-sized. The last few hundred lines typically show what the session was doing before hanging. Debug logs may contain prompts, file contents, or tokens from other sessions — paste only lines relevant to the hang, and never quote secrets/API keys you happen to see.
98
+
99
+ 4. **Consider a stack dump** for a truly frozen process (advanced, optional):
100
+ - macOS: `sample <pid> 3` gives a 3-second native stack sample. If `sample` itself seems to hang (the target's Mach task port may be wedged on a kernel-level freeze), wrap it: `timeout 15 sample <pid> 3` (or `gtimeout 15 ...` on Homebrew coreutils). Stack frames may include function arguments containing API keys or tokens held in memory — redact such values to `***` before including the dump in the report.
101
+ - Linux: `cat /proc/<pid>/stack` for kernel stack (read-only, no `ptrace` permissions needed). Avoid `strace -p` for this purpose: it requires `CAP_SYS_PTRACE` (often denied under `kernel.yama.ptrace_scope=1`), and `strace -c` blocks until the target exits — it would hang on the very kind of stuck process you are diagnosing.
102
+ - This is big — only grab it if the process is clearly hung and you want to know _why_
103
+
104
+ ## Report
105
+
106
+ Present a structured diagnostic report directly to the user with these sections:
107
+
108
+ **For each stuck/slow session found:**
109
+
110
+ - PID, CPU%, RSS (in MB), process state, uptime, full command line
111
+ - Child processes and their states
112
+ - Your diagnosis of what's likely wrong
113
+ - Relevant debug log tail if you captured it
114
+ - Stack dump output if you captured it
115
+ - Suggested next step for the user to decide (e.g., "user may consider `kill <pid>` if the session is unresponsive", "likely waiting on I/O — check disk", "accidentally stopped — user can resume with `kill -CONT <pid>`"). Do not execute these actions yourself — present them as options for the user.
116
+
117
+ **If every session looks healthy**, tell the user directly — no diagnostic dump needed. Mention how many sessions you checked and that none showed signs of being stuck.
118
+
119
+ **If no sessions are found at all** (zero sidecars and zero matching `ps` rows), say so explicitly: which `RUNTIME_DIR` you searched and that `ps` returned no qwen-related processes for the current user. Suggest the session may have already exited.
120
+
121
+ ## Notes
122
+
123
+ - Don't kill or signal any processes — this is diagnostic only.
124
+ - If the user gave an argument (e.g., a specific PID or symptom), focus there first.
@@ -7,32 +7,32 @@ import {
7
7
  hasRebuiltToolRegistry,
8
8
  rebuildToolRegistryOnOverride,
9
9
  resolveSubagentApprovalMode
10
- } from "./chunk-2WFU3IUH.js";
10
+ } from "./chunk-MNPZ2WO6.js";
11
11
  import "./chunk-5P5XGNYH.js";
12
12
  import "./chunk-K5PGHDBN.js";
13
13
  import "./chunk-O4PICXES.js";
14
14
  import "./chunk-TW522KN6.js";
15
15
  import "./chunk-MLZQVCF3.js";
16
- import "./chunk-JMZQICAL.js";
17
- import "./chunk-SOIEFHIK.js";
18
- import "./chunk-B7ZL7HUA.js";
16
+ import "./chunk-PPHYLJSS.js";
17
+ import "./chunk-G7YTSRES.js";
18
+ import "./chunk-XVHR7ATJ.js";
19
19
  import "./chunk-77WXWU44.js";
20
- import "./chunk-OCC4MZRS.js";
21
- import "./chunk-CAWKL3UC.js";
22
- import "./chunk-PCL3EJGY.js";
20
+ import "./chunk-F23NCRJ2.js";
21
+ import "./chunk-VTPOO6GV.js";
22
+ import "./chunk-C3LHPHN2.js";
23
23
  import "./chunk-SYCJMSIJ.js";
24
24
  import "./chunk-UWCTAVOD.js";
25
25
  import "./chunk-OFEVLU4C.js";
26
- import "./chunk-CM2IESUE.js";
27
- import "./chunk-UXW7MYAW.js";
28
- import "./chunk-G27O2LD2.js";
26
+ import "./chunk-PR4T27R7.js";
27
+ import "./chunk-CW44BRRA.js";
28
+ import "./chunk-D5NTAHYL.js";
29
29
  import "./chunk-T4VD6OJ4.js";
30
30
  import "./chunk-RDYWTWEM.js";
31
- import "./chunk-TPGOGCWM.js";
32
- import "./chunk-FYMSCRHM.js";
33
- import "./chunk-SQNQIOD5.js";
34
- import "./chunk-FKVKVE6N.js";
35
- import "./chunk-GJXIKCKL.js";
31
+ import "./chunk-JYQUJ5DS.js";
32
+ import "./chunk-FZIUV27X.js";
33
+ import "./chunk-2B7UBDY5.js";
34
+ import "./chunk-KXZ4TJB4.js";
35
+ import "./chunk-XP27SJMH.js";
36
36
  import "./chunk-E7E2MFYM.js";
37
37
  import "./chunk-ZERZSAZL.js";
38
38
  import "./chunk-QN5NZ3UQ.js";
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-KQIKOTQJ.js";
7
7
  import {
8
8
  RequestTokenizer
9
- } from "./chunk-BXNCPI75.js";
9
+ } from "./chunk-NAID3ZWF.js";
10
10
  import {
11
11
  Blob,
12
12
  File,
@@ -16,7 +16,7 @@ import {
16
16
  import {
17
17
  buildRuntimeFetchOptions,
18
18
  redactProxyError
19
- } from "./chunk-CAWKL3UC.js";
19
+ } from "./chunk-VTPOO6GV.js";
20
20
  import {
21
21
  CAPPED_DEFAULT_MAX_TOKENS,
22
22
  DEFAULT_TIMEOUT,
@@ -24,7 +24,7 @@ import {
24
24
  hasExplicitOutputLimit,
25
25
  safeJsonParse,
26
26
  tokenLimit
27
- } from "./chunk-UXW7MYAW.js";
27
+ } from "./chunk-CW44BRRA.js";
28
28
  import {
29
29
  FinishReason,
30
30
  GenerateContentResponse
@@ -32,7 +32,7 @@ import {
32
32
  import "./chunk-RDYWTWEM.js";
33
33
  import {
34
34
  createDebugLogger
35
- } from "./chunk-GJXIKCKL.js";
35
+ } from "./chunk-XP27SJMH.js";
36
36
  import "./chunk-E7E2MFYM.js";
37
37
  import {
38
38
  require_ms
@@ -6,10 +6,10 @@ import {
6
6
  BaseToolInvocation,
7
7
  ToolDisplayNames,
8
8
  ToolNames
9
- } from "./chunk-FYMSCRHM.js";
9
+ } from "./chunk-FZIUV27X.js";
10
10
  import {
11
11
  createDebugLogger
12
- } from "./chunk-GJXIKCKL.js";
12
+ } from "./chunk-XP27SJMH.js";
13
13
  import "./chunk-QWSRH265.js";
14
14
  import {
15
15
  init_esbuild_shims
@@ -108,6 +108,32 @@ var ca_default = {
108
108
  "Generate a one-line session recap now": "Generar ara un resum de la sessi\xF3 en una sola l\xEDnia",
109
109
  "Rename the current conversation. --auto lets the fast model pick a title.": "Canviar el nom de la conversa actual. --auto permet que el model r\xE0pid tri\xEF un t\xEDtol.",
110
110
  "Rewind conversation to a previous turn": "Rebobinar la conversa fins a un torn anterior",
111
+ "Rewind Conversation": "Rebobinar la conversa",
112
+ "No user turns to rewind to.": "No hi ha torns d'usuari per rebobinar.",
113
+ "Rewind to: ": "Rebobinar a: ",
114
+ "Restore code and conversation": "Restaura el codi i la conversa",
115
+ "Restore conversation only": "Restaura nom\xE9s la conversa",
116
+ "Restore code only": "Restaura nom\xE9s el codi",
117
+ "Never mind": "Tant \xE9s",
118
+ "Computing file changes...": "S'estan calculant els canvis als fitxers...",
119
+ "Restoring...": "S'est\xE0 restaurant...",
120
+ "Restored {{count}} file(s).": "S'han restaurat {{count}} fitxer(s).",
121
+ "Failed to restore files: {{error}}": "Error en restaurar els fitxers: {{error}}",
122
+ "Rewind failed: {{error}}": "Error en retrocedir: {{error}}",
123
+ "Cannot rewind conversation: no active model client.": "No es pot retrocedir la conversa: cap client de model actiu.",
124
+ "Code restored, but conversation could not be rewound (no active client).": "Codi restaurat, per\xF2 la conversa no s\u2019ha pogut retrocedir (cap client actiu).",
125
+ "Conversation rewound. Edit your prompt and press Enter to continue.": "Conversa retrocedida. Edita la teva indicaci\xF3 i prem Retorn per continuar.",
126
+ "Rewinding does not affect files edited manually or via shell commands.": "El retroc\xE9s no afecta els fitxers editats manualment o mitjan\xE7ant comandes de shell.",
127
+ "Cannot rewind to a turn that was compressed. Try a more recent turn.": "No es pot retrocedir a un torn que ha estat comprimit. Prova amb un torn m\xE9s recent.",
128
+ "File restore is unavailable for this turn (no captured file changes, or this turn predates the current session).": "La restauraci\xF3 de fitxers no est\xE0 disponible per a aquest torn (no s\u2019han capturat canvis, o aquest torn \xE9s anterior a la sessi\xF3 actual).",
129
+ "(+{{insertions}} -{{deletions}} in {{count}} file)": "(+{{insertions}} -{{deletions}} en {{count}} fitxer)",
130
+ "(+{{insertions}} -{{deletions}} in {{count}} files)": "(+{{insertions}} -{{deletions}} en {{count}} fitxers)",
131
+ "Failed to restore {{count}} file(s): {{files}}": "Error en restaurar {{count}} fitxer(s): {{files}}",
132
+ "Cannot restore files: this turn was created before file checkpointing was enabled.": "No es poden restaurar els fitxers: aquest torn es va crear abans que el punt de control de fitxers estigu\xE9s habilitat.",
133
+ "No files needed to be restored.": "Cap fitxer necessitava restauraci\xF3.",
134
+ "\u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to go back": "\u2191\u2193 per navegar \xB7 Enter per seleccionar \xB7 Esc per tornar",
135
+ "\u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to cancel": "\u2191\u2193 per navegar \xB7 Enter per seleccionar \xB7 Esc per cancel\xB7lar",
136
+ "Enter/Y to confirm \xB7 Esc/N to go back": "Enter/Y per confirmar \xB7 Esc/N per tornar",
111
137
  "change the theme": "canviar el tema",
112
138
  "Select Theme": "Seleccionar tema",
113
139
  Preview: "Previsualitzaci\xF3",
@@ -2,11 +2,11 @@
2
2
  "use strict";
3
3
  import {
4
4
  formatFetchErrorForUser
5
- } from "./chunk-FKVKVE6N.js";
5
+ } from "./chunk-KXZ4TJB4.js";
6
6
  import {
7
7
  Storage,
8
8
  createDebugLogger
9
- } from "./chunk-GJXIKCKL.js";
9
+ } from "./chunk-XP27SJMH.js";
10
10
  import {
11
11
  init_esbuild_shims
12
12
  } from "./chunk-A4BMJM77.js";
@@ -0,0 +1,350 @@
1
+ // Force strict mode and setup for ESM
2
+ "use strict";
3
+ import {
4
+ init_esbuild_shims
5
+ } from "./chunk-A4BMJM77.js";
6
+ import {
7
+ __name
8
+ } from "./chunk-J2S4EL5Y.js";
9
+
10
+ // packages/cli/src/serve/eventBus.ts
11
+ init_esbuild_shims();
12
+ var EVENT_SCHEMA_VERSION = 1;
13
+ var DEFAULT_MAX_QUEUED = 256;
14
+ var DEFAULT_RING_SIZE = 8e3;
15
+ var WARN_THRESHOLD_RATIO = 0.75;
16
+ var WARN_RESET_RATIO = 0.375;
17
+ var DEFAULT_MAX_SUBSCRIBERS = 64;
18
+ var SubscriberLimitExceededError = class extends Error {
19
+ static {
20
+ __name(this, "SubscriberLimitExceededError");
21
+ }
22
+ limit;
23
+ constructor(limit) {
24
+ super(`EventBus subscriber limit reached (${limit})`);
25
+ this.name = "SubscriberLimitExceededError";
26
+ this.limit = limit;
27
+ }
28
+ };
29
+ var EventBus = class {
30
+ constructor(ringSize = DEFAULT_RING_SIZE, maxSubscribers = DEFAULT_MAX_SUBSCRIBERS) {
31
+ this.ringSize = ringSize;
32
+ this.maxSubscribers = maxSubscribers;
33
+ }
34
+ static {
35
+ __name(this, "EventBus");
36
+ }
37
+ nextId = 1;
38
+ ring = [];
39
+ subs = /* @__PURE__ */ new Set();
40
+ closed = false;
41
+ /** Most recent id ever assigned by `publish`. 0 if no events published. */
42
+ get lastEventId() {
43
+ return this.nextId - 1;
44
+ }
45
+ /** Snapshot of the live subscriber count. */
46
+ get subscriberCount() {
47
+ return this.subs.size;
48
+ }
49
+ /**
50
+ * Publish an event to the bus. Returns the constructed `BridgeEvent`
51
+ * (with `id` + `v` assigned) on success, or `undefined` when the
52
+ * bus is closed.
53
+ *
54
+ * **Never throws** (BX9_p contract). Closing the bus mid-publish
55
+ * is the only abnormal path and is handled as a return-undefined
56
+ * no-op; subscriber-enqueue failures are caught internally and
57
+ * translated to per-subscriber eviction. Call sites can rely on
58
+ * this — the historical `try { publish(...) } catch {}` blocks in
59
+ * `httpAcpBridge.ts` are defense-in-depth, not load-bearing, and
60
+ * may be removed in a future cleanup pass without changing
61
+ * behavior. Don't add new try/catch wrappers around `publish()`.
62
+ */
63
+ publish(input) {
64
+ if (this.closed) return void 0;
65
+ const event = {
66
+ id: this.nextId++,
67
+ v: EVENT_SCHEMA_VERSION,
68
+ ...input
69
+ };
70
+ this.ring.push(event);
71
+ if (this.ring.length > this.ringSize) this.ring.shift();
72
+ for (const sub of Array.from(this.subs)) {
73
+ if (sub.evicted) continue;
74
+ if (!sub.queue.push(event)) {
75
+ sub.evicted = true;
76
+ const evictionFrame = {
77
+ v: EVENT_SCHEMA_VERSION,
78
+ type: "client_evicted",
79
+ data: { reason: "queue_overflow", droppedAfter: event.id }
80
+ };
81
+ sub.queue.forcePush(evictionFrame);
82
+ sub.queue.close();
83
+ sub.dispose();
84
+ continue;
85
+ }
86
+ const liveSize = sub.queue.size;
87
+ if (!sub.warned && liveSize >= sub.warnThreshold) {
88
+ sub.warned = true;
89
+ const warningFrame = {
90
+ v: EVENT_SCHEMA_VERSION,
91
+ type: "slow_client_warning",
92
+ data: {
93
+ queueSize: liveSize,
94
+ maxQueued: sub.maxQueued,
95
+ // `event.id` is always defined here — the just-published
96
+ // `event` is constructed at the top of `publish()` with
97
+ // `id: this.nextId++`. No `??` fallback needed.
98
+ lastEventId: event.id
99
+ }
100
+ };
101
+ sub.queue.forcePush(warningFrame);
102
+ } else if (sub.warned && liveSize <= sub.warnResetThreshold) {
103
+ sub.warned = false;
104
+ }
105
+ }
106
+ return event;
107
+ }
108
+ /**
109
+ * Note: registration is synchronous — by the time `subscribe()` returns,
110
+ * the subscriber is already attached and will receive any subsequent
111
+ * `publish()` even if the consumer hasn't started iterating yet. (A
112
+ * generator-style implementation would defer registration to the first
113
+ * `next()` call, which races with publishes that happen before the
114
+ * consumer's first await.)
115
+ *
116
+ * The returned iterator is NOT safe to drive from concurrent callers —
117
+ * two simultaneous `.next()` calls would race for the same event from
118
+ * the underlying queue. Daemon usage is sequential (`for await ... of`
119
+ * inside the SSE route), so this is safe in production. Callers that
120
+ * fan an iterator out to multiple consumers must serialize themselves.
121
+ */
122
+ subscribe(opts = {}) {
123
+ if (this.closed) {
124
+ return emptyAsyncIterable();
125
+ }
126
+ if (this.subs.size >= this.maxSubscribers) {
127
+ throw new SubscriberLimitExceededError(this.maxSubscribers);
128
+ }
129
+ const maxQueued = opts.maxQueued ?? DEFAULT_MAX_QUEUED;
130
+ const queue = new BoundedAsyncQueue(maxQueued);
131
+ const sub = {
132
+ queue,
133
+ evicted: false,
134
+ maxQueued,
135
+ warnThreshold: WARN_THRESHOLD_RATIO * maxQueued,
136
+ warnResetThreshold: WARN_RESET_RATIO * maxQueued,
137
+ warned: false,
138
+ dispose: /* @__PURE__ */ __name(() => {
139
+ }, "dispose")
140
+ };
141
+ this.subs.add(sub);
142
+ if (opts.lastEventId !== void 0) {
143
+ for (const e of this.ring) {
144
+ if (e.id !== void 0 && e.id > opts.lastEventId) {
145
+ queue.forcePush(e);
146
+ }
147
+ }
148
+ }
149
+ let disposed = false;
150
+ const dispose = /* @__PURE__ */ __name(() => {
151
+ if (disposed) return;
152
+ disposed = true;
153
+ this.subs.delete(sub);
154
+ opts.signal?.removeEventListener("abort", onAbort);
155
+ }, "dispose");
156
+ sub.dispose = dispose;
157
+ const onAbort = /* @__PURE__ */ __name(() => {
158
+ queue.close({ drain: false });
159
+ dispose();
160
+ }, "onAbort");
161
+ if (opts.signal) {
162
+ if (opts.signal.aborted) {
163
+ onAbort();
164
+ } else {
165
+ opts.signal.addEventListener("abort", onAbort, { once: true });
166
+ }
167
+ }
168
+ return {
169
+ [Symbol.asyncIterator]: () => ({
170
+ async next() {
171
+ const r = await queue.next();
172
+ if (r.done) dispose();
173
+ return r;
174
+ },
175
+ async return() {
176
+ queue.close();
177
+ dispose();
178
+ return { value: void 0, done: true };
179
+ }
180
+ })
181
+ };
182
+ }
183
+ /** Close all live subscribers and prevent further `publish`/`subscribe`. */
184
+ close() {
185
+ if (this.closed) return;
186
+ this.closed = true;
187
+ for (const sub of this.subs) sub.queue.close();
188
+ this.subs.clear();
189
+ }
190
+ };
191
+ function emptyAsyncIterable() {
192
+ return {
193
+ [Symbol.asyncIterator]: () => ({
194
+ async next() {
195
+ return { value: void 0, done: true };
196
+ }
197
+ })
198
+ };
199
+ }
200
+ __name(emptyAsyncIterable, "emptyAsyncIterable");
201
+ var BoundedAsyncQueue = class {
202
+ constructor(maxSize) {
203
+ this.maxSize = maxSize;
204
+ }
205
+ static {
206
+ __name(this, "BoundedAsyncQueue");
207
+ }
208
+ buf = [];
209
+ resolvers = [];
210
+ closed = false;
211
+ /**
212
+ * O(1) snapshot of how many LIVE (non-forced) entries are in `buf`.
213
+ * Maintained directly by `push()`/`next()`: any time a forced entry
214
+ * is added or removed `liveCount` is untouched; any time a live entry
215
+ * is added or removed `liveCount` moves with it. Replaces the
216
+ * position-dependent `forcedInBuf` heuristic — `liveCount` is correct
217
+ * no matter where in the queue the forced entries are.
218
+ */
219
+ liveCount = 0;
220
+ /**
221
+ * Number of LIVE (non-force-pushed) items currently waiting in the
222
+ * buffer. Backpressure decisions in `EventBus.publish()` (the
223
+ * `slow_client_warning` threshold) read this value.
224
+ */
225
+ get size() {
226
+ return this.liveCount;
227
+ }
228
+ /** Returns true if accepted, false if dropped due to overflow. */
229
+ push(value) {
230
+ if (this.closed) return false;
231
+ const r = this.resolvers.shift();
232
+ if (r) {
233
+ r({ value, done: false });
234
+ return true;
235
+ }
236
+ if (this.liveCount >= this.maxSize) return false;
237
+ this.buf.push({ value, forced: false });
238
+ this.liveCount += 1;
239
+ return true;
240
+ }
241
+ /** Bypasses the size cap. Used for replay frames, eviction terminal,
242
+ * and slow-client warnings. */
243
+ forcePush(value) {
244
+ if (this.closed) return;
245
+ const r = this.resolvers.shift();
246
+ if (r) {
247
+ r({ value, done: false });
248
+ return;
249
+ }
250
+ this.buf.push({ value, forced: true });
251
+ }
252
+ /**
253
+ * Mark the queue closed. By default `next()` continues to drain
254
+ * any items already in `buf` before returning `done: true` —
255
+ * that's what the eviction path relies on (the synthetic
256
+ * `client_evicted` frame is force-pushed THEN close is called,
257
+ * and we want the consumer to see the terminal frame before the
258
+ * iterator unwinds).
259
+ *
260
+ * Pass `{ drain: false }` to drop buffered items immediately
261
+ * (the AbortSignal-driven unsubscribe path uses this — the
262
+ * subscribe docstring says abort should close the iterator
263
+ * promptly, but draining hundreds of queued events first
264
+ * contradicts that and adds post-abort work to the SSE route).
265
+ */
266
+ close(opts = {}) {
267
+ if (this.closed) return;
268
+ this.closed = true;
269
+ if (opts.drain === false) {
270
+ this.buf.length = 0;
271
+ this.liveCount = 0;
272
+ }
273
+ while (this.resolvers.length > 0) {
274
+ this.resolvers.shift()({
275
+ value: void 0,
276
+ done: true
277
+ });
278
+ }
279
+ }
280
+ next() {
281
+ if (this.buf.length > 0) {
282
+ const entry = this.buf.shift();
283
+ if (!entry.forced) this.liveCount -= 1;
284
+ return Promise.resolve({ value: entry.value, done: false });
285
+ }
286
+ if (this.closed) {
287
+ return Promise.resolve({
288
+ value: void 0,
289
+ done: true
290
+ });
291
+ }
292
+ return new Promise((resolve) => this.resolvers.push(resolve));
293
+ }
294
+ };
295
+
296
+ // packages/cli/src/serve/status.ts
297
+ init_esbuild_shims();
298
+ var STATUS_SCHEMA_VERSION = 1;
299
+ var SERVE_STATUS_EXT_METHODS = {
300
+ workspaceMcp: "qwen/status/workspace/mcp",
301
+ workspaceSkills: "qwen/status/workspace/skills",
302
+ workspaceProviders: "qwen/status/workspace/providers",
303
+ sessionContext: "qwen/status/session/context",
304
+ sessionSupportedCommands: "qwen/status/session/supported_commands"
305
+ };
306
+ function createIdleWorkspaceMcpStatus(workspaceCwd) {
307
+ return {
308
+ v: STATUS_SCHEMA_VERSION,
309
+ workspaceCwd,
310
+ initialized: false,
311
+ discoveryState: "not_started",
312
+ servers: []
313
+ };
314
+ }
315
+ __name(createIdleWorkspaceMcpStatus, "createIdleWorkspaceMcpStatus");
316
+ function createIdleWorkspaceSkillsStatus(workspaceCwd) {
317
+ return {
318
+ v: STATUS_SCHEMA_VERSION,
319
+ workspaceCwd,
320
+ initialized: false,
321
+ skills: []
322
+ };
323
+ }
324
+ __name(createIdleWorkspaceSkillsStatus, "createIdleWorkspaceSkillsStatus");
325
+ function createIdleWorkspaceProvidersStatus(workspaceCwd) {
326
+ return {
327
+ v: STATUS_SCHEMA_VERSION,
328
+ workspaceCwd,
329
+ initialized: false,
330
+ providers: []
331
+ };
332
+ }
333
+ __name(createIdleWorkspaceProvidersStatus, "createIdleWorkspaceProvidersStatus");
334
+
335
+ export {
336
+ EVENT_SCHEMA_VERSION,
337
+ DEFAULT_RING_SIZE,
338
+ SubscriberLimitExceededError,
339
+ EventBus,
340
+ STATUS_SCHEMA_VERSION,
341
+ SERVE_STATUS_EXT_METHODS,
342
+ createIdleWorkspaceMcpStatus,
343
+ createIdleWorkspaceSkillsStatus,
344
+ createIdleWorkspaceProvidersStatus
345
+ };
346
+ /**
347
+ * @license
348
+ * Copyright 2025 Qwen Team
349
+ * SPDX-License-Identifier: Apache-2.0
350
+ */