Fix retry logic: Respect Retry-After headers for TPM throttling errors#2
Closed
Copilot wants to merge 3 commits into
Closed
Fix retry logic: Respect Retry-After headers for TPM throttling errors#2Copilot wants to merge 3 commits into
Copilot wants to merge 3 commits into
Conversation
Co-authored-by: wenshao <1166785+wenshao@users.noreply.github.com>
The TPM throttling error check now happens after the Retry-After header check. This ensures that server-specified retry delays take precedence over the hardcoded 1-minute TPM delay. Added a test case to verify the fix. Co-authored-by: wenshao <1166785+wenshao@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix bug in application and submit patch
Fix retry logic: Respect Retry-After headers for TPM throttling errors
Feb 10, 2026
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
wenshao
pushed a commit
that referenced
this pull request
Apr 1, 2026
- fix: sanitize remote filenames with basename() and isolate uploads in UUID subdirs to prevent path traversal and collision (#2-4, QwenLM#27) - fix: use crypto.randomInt() for pairing codes instead of Math.random() (QwenLM#5) - fix: pass config.sessionScope instead of hardcoded 'user' (QwenLM#6); add per-channel scope overrides via setChannelScope() for startAll (QwenLM#7) - fix: removeSession now returns removed session IDs and persists when chatId is provided (QwenLM#8) - fix: /clear only removes the cleared session from instructedSessions, not all sessions (QwenLM#9) - fix: DingTalk @mention stripping now removes only the first mention instead of all mentions (QwenLM#10) - fix: remove dead TELEGRAF_COMMANDS Set and its guard (QwenLM#13) - fix: WeChat cursor saved after message processing, not before (QwenLM#14) - fix: crash recovery uses time-window counting instead of resettable counter to prevent infinite restart loops (QwenLM#17) - fix: call channel.disconnect() before exit on crash exhaustion (QwenLM#18)
wenshao
added a commit
that referenced
this pull request
Apr 4, 2026
… tool call UI The follow-up suggestion generation was leaking into the conversation UI through three channels: 1. The forked query included tools in its generation config, allowing the model to produce function calls during suggestion generation. Fixed by setting `tools: []` in runForkedQuery's per-request config (kept in createForkedChat for speculation which needs tools). 2. logApiResponse and logApiError recorded suggestion API events to the chatRecordingService, causing them to appear in session JSONL files and the WebUI. Fixed by adding isInternalPromptId() guard that skips chatRecordingService for 'prompt_suggestion' and 'forked_query' IDs. uiTelemetryService.addEvent() is preserved so /stats still tracks suggestion token usage. 3. LoggingContentGenerator logged suggestion requests/responses to the OpenAI logger and telemetry pipeline. Fixed by skipping logApiRequest, buildOpenAIRequestForLogging, and logOpenAIInteraction for internal prompt IDs. _logApiResponse is preserved (for /stats) but its chatRecordingService path is filtered by fix #2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
wenshao
added a commit
that referenced
this pull request
Apr 9, 2026
…QwenLM#2872) * fix(core): prevent followup suggestion input/output from appearing in tool call UI The follow-up suggestion generation was leaking into the conversation UI through three channels: 1. The forked query included tools in its generation config, allowing the model to produce function calls during suggestion generation. Fixed by setting `tools: []` in runForkedQuery's per-request config (kept in createForkedChat for speculation which needs tools). 2. logApiResponse and logApiError recorded suggestion API events to the chatRecordingService, causing them to appear in session JSONL files and the WebUI. Fixed by adding isInternalPromptId() guard that skips chatRecordingService for 'prompt_suggestion' and 'forked_query' IDs. uiTelemetryService.addEvent() is preserved so /stats still tracks suggestion token usage. 3. LoggingContentGenerator logged suggestion requests/responses to the OpenAI logger and telemetry pipeline. Fixed by skipping logApiRequest, buildOpenAIRequestForLogging, and logOpenAIInteraction for internal prompt IDs. _logApiResponse is preserved (for /stats) but its chatRecordingService path is filtered by fix #2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: deduplicate isInternalPromptId into shared export from loggers.ts Address review feedback: extract isInternalPromptId() to a single exported function in telemetry/loggers.ts and import it in LoggingContentGenerator, eliminating the duplicate private method. Also update loggingContentGenerator.test.ts mock to use importOriginal so the real isInternalPromptId is available during tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: extract isInternalPromptId to shared utils, add tests Address maintainer review feedback: 1. Move isInternalPromptId() to packages/core/src/utils/internalPromptIds.ts using a ReadonlySet for the ID registry. Adding new internal prompt IDs only requires changing one file. loggers.ts re-exports for compatibility, loggingContentGenerator.ts imports directly from utils. 2. Extract `tools: []` magic value to a frozen NO_TOOLS constant in forkedQuery.ts. 3. Add unit tests for isInternalPromptId: prompt_suggestion → true, forked_query → true, user_query → false, empty string → false. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address Copilot review — docs, stream optimization, tests 1. Update forkedQuery.ts module docs to reflect that runForkedQuery overrides tools: [] at the per-request level while createForkedChat retains the full generationConfig for speculation callers. 2. Propagate isInternal into loggingStreamWrapper to skip response collection and consolidation for internal prompts, avoiding unnecessary CPU/memory overhead. 3. Add logApiResponse chatRecordingService filter tests: verify prompt_suggestion/forked_query skip recording while normal IDs still record. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: deep-freeze NO_TOOLS, add internal prompt guard tests Address Copilot review round 3: 1. Deep-freeze NO_TOOLS.tools array to prevent shared mutable state across forked query calls. 2. Add LoggingContentGenerator tests verifying that internal prompt IDs (prompt_suggestion, forked_query) skip logApiRequest and OpenAI interaction logging while preserving logApiResponse. 3. Add logApiError chatRecordingService filter tests matching the existing logApiResponse coverage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: reconcile createForkedChat JSDoc with module header Clarify that createForkedChat retains the full generationConfig (including tools) for speculation callers, while runForkedQuery strips tools at the per-request level via NO_TOOLS. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: build errors and Copilot round 4 feedback 1. Fix NO_TOOLS type: Object.freeze produces readonly array incompatible with ToolUnion[]. Use Readonly<Pick<>> instead; spread in requestConfig already creates a fresh mutable copy per call. 2. Fix test missing required 'model' field in ContentGeneratorConfig. 3. Track firstResponseId/firstModelVersion in loggingStreamWrapper so _logApiResponse/_logApiError have accurate values even when full response collection is skipped for internal prompts. 4. Strengthen OpenAI logger test assertion: assert OpenAILogger was constructed (not guarded by if), then assert logInteraction was not called. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove dead Object.keys check, add streaming internal prompt test 1. Simplify runForkedQuery: requestConfig always has tools:[] from NO_TOOLS spread, so the Object.keys().length > 0 ternary is dead code. Pass requestConfig directly. 2. Add generateContentStream test for internal prompt IDs to match the existing generateContent coverage, ensuring the streaming wrapper also skips logApiRequest and OpenAI interaction logging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: prevent Enter accept from re-inserting suggestion into buffer When accepting a followup suggestion via Enter, accept() queued buffer.insert(suggestion) in a microtask that executed after handleSubmitAndClear had already cleared the buffer, leaving the suggestion text stuck in the input. Add skipOnAccept option to accept() so the Enter path bypasses the onAccept callback. Also add runForkedQuery unit tests verifying tools: [] is passed in per-request config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): add speculation to internal IDs, fix logToolCall filtering, improve suggestion prompt - Add 'speculation' to INTERNAL_PROMPT_IDS so speculation API traffic and tool calls are hidden from chat recordings and tool call UI - Add isInternalPromptId check to logToolCall() for consistency with logApiError/logApiResponse - Improve SUGGESTION_PROMPT: prioritize assistant's last few lines and extract actionable text from explicit tips (e.g. "Tip: type X") - Fix garbled unicode in prompt text - Update design docs and user docs to reflect changes - Add test coverage for all new behavior * fix(core): deep-freeze NO_TOOLS, add speculation to loggingContentGenerator tests - Object.freeze NO_TOOLS and its tools array to prevent runtime mutation - Add 'speculation' to loggingContentGenerator internal prompt ID tests for consistency with loggers.test.ts and internalPromptIds.ts * fix(core): fix NO_TOOLS Object.freeze type error Use `as const` with type assertion to satisfy TypeScript while keeping runtime immutability via Object.freeze. * refactor(core): remove unused isInternalPromptId re-export from loggers.ts All consumers import directly from utils/internalPromptIds.js. The re-export was dead code with no importers. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
wenshao
added a commit
that referenced
this pull request
May 7, 2026
…host rows, dead doc Three findings from Copilot's PR review: 1. **1s interval kept ticking forever after expiry.** The gate was `entries.some(isAgentEntry)`, but `BackgroundTaskRegistry.getAll()` retains terminal entries indefinitely — once the last visible row passed its 8s window the panel returned null but the interval kept firing setNow each second, churning re-renders for nothing on screen. The gate now considers visibility (running / paused OR terminal-within-window) and the interval clears itself once the condition flips false. New entries restart the interval via the `entries` dep. 2. **Ghost rows when registry forgets an entry.** The live re-pull fell back to the snapshot when `registry.get()` returned undefined. The canonical case is a foreground subagent that unregisters silently after its statusChange fires (`unregisterForeground` deletes without emitting a follow-up transition) — the snapshot still says `running`, so the row would never clear. Trust the registry: when it says the entry is gone, drop the row. The snapshot-only fallback is preserved for the no-Config case (test fixtures). 3. **Dead doc reference.** The trailing comment in `subagents/index.ts` pointed at `docs/comparison/subagent-display-deep-dive.md`, which doesn't exist in this repo (it lives in the codeagents knowledge base, not qwen-code). Dropped the dead pointer; the in-tree pointers to `LiveAgentPanel` and `BackgroundTasksDialog` already tell readers where to look. Coverage delta: +2 cases on `LiveAgentPanel.test.tsx` — `drops snapshot rows the live registry no longer knows about` (issue #2) and `still shows the snapshot when no Config is mounted` (locks in the test-fixture fallback that issue #2's stricter rule would otherwise have broken).
wenshao
added a commit
that referenced
this pull request
May 7, 2026
…e context flag Five review findings on PR QwenLM#3919: 1. **Compact mode bypassed the scrollback summary** (gpt-5.5 via /qreview, ToolGroupMessage:324). `ToolGroupMessage` returns `CompactToolGroupDisplay` before the ToolMessage path when `compactMode === true`, so the new `isPending` gate on `SubagentExecutionRenderer` only protected the expanded path — committed terminal subagents in compact mode never reached `SubagentScrollbackSummary` and the LiveAgentPanel → committed- summary handoff broke for users who turned compact mode on. Force-expand the group when `!isPending` AND any tool call has a terminal `task_execution` resultDisplay. Stay compact while the parent turn is still live (`isPending`) — the panel below the composer owns that surface and an inline summary would duplicate it. Coverage: 4 new ToolGroupMessage cases (compact + completed-committed expands; compact + running-live stays compact; compact + completed-live stays compact; compact + failed-committed expands). 2. **Snapshot-coupled comment in `packages/core`** (Copilot, background-tasks.ts:292). The comment named CLI/UI consumers (`useBackgroundTaskView`, `BackgroundTasksDialog`) and asserted React batching guarantees from a core file. Reword to "snapshot-style consumers that re-pull `getAll()` from inside the callback" and drop the framework-specific batching claim. 3. **Two-phase emit needed an explicit signal** (Copilot, background-tasks.ts:283). Emitting `statusChange` twice without distinguishing the phases forced consumers to either do duplicate work or risk persisting a stale `entry` from the second callback. Add an optional second arg `context?: { removed?: boolean }` to `BackgroundStatusChangeCallback`; the post-delete emit passes `{ removed: true }` so consumers can disambiguate without re-querying the registry. Backwards compatible — existing callbacks ignore the new arg. Tests updated to assert both `mock.calls[0][1] === undefined` and `mock.calls[1][1] === { removed: true }`. 4. **`isPending` doc clarified** (Copilot, ToolMessage.tsx:507). Made the default semantics explicit: omitted/undefined is treated as committed (not pending); live-area renderers MUST pass `true` explicitly to suppress the scrollback summary. 5. (4 of the threads were duplicate Copilot fires of #2 + #3.) Coverage: 219 test files / 3369 passing across cli/ui + core/agents.
wenshao
pushed a commit
that referenced
this pull request
May 16, 2026
…#4133) * feat(skills): add /stuck diagnostic skill for frozen sessions Port the /stuck diagnostic capability to qwen-code as a bundled skill. Scans for stuck processes, high CPU/memory, hung subprocesses, and debug logs, then presents a structured diagnostic report. Adapted from claude-code's internal /stuck skill with: - Process identification via command path (node-based CLI, not compiled binary) - Debug log path updated to ~/.qwen/debug/ - Cross-platform stack dump support (macOS sample + Linux /proc/stack) - Direct user-facing output (no Slack dependency) 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): respect QWEN_RUNTIME_DIR/QWEN_HOME for debug log path in /stuck 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): add allowedTools and clarify diagnostic-only boundary in /stuck - Add allowedTools (run_shell_command, read_file) for convention consistency - Rephrase recommended actions as user-facing options, not model-executable commands 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): address review feedback for /stuck — security, accuracy, sidecar - Add explicit PID argument validation (reject shell metacharacters) to prevent the model from substituting injection payloads into shell commands - Mention macOS/BSD `U` state alongside Linux `D` for uninterruptible sleep, so I/O-blocked macOS sessions are not silently missed - Add `-ww` to `ps` to disable column truncation, so long qwen paths don't fall outside the grep window and cause sessions to be missed - Use `~/.qwen/projects/*/chats/*.runtime.json` sidecars as the primary source of (pid, sessionId, workDir) mappings; `ps` is now a supplement for CPU/RSS/state enrichment 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): apply minimal review fixes for /stuck - Filter ps to current UID via -u "$(id -u)" — avoid leaking other users' Qwen processes on shared hosts - Note that ps `rss=` is in KB; divide by 1024 before MB comparison - Replace `pgrep -lP` with `pgrep -P` + `ps -p` so child state shows up - Mention `advanced.runtimeOutputDir` setting alongside QWEN_RUNTIME_DIR / QWEN_HOME in the runtime-base description - Add half-line about PID reuse handling and not quoting secrets from debug logs (without inflating the prompt into a full workflow) 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): round-3 review fixes for /stuck - Resolve RUNTIME_DIR from QWEN_RUNTIME_DIR/QWEN_HOME and use it in the sidecar `ls`, debug log path, and `latest` symlink — the previous round only updated the prose and left the actual commands hardcoded - Add explicit fallthrough: when sidecar enumeration finds nothing, fall through to step 2 instead of getting stuck trying to make sidecar work - Replace metacharacter blacklist with digit-only PID whitelist — safer and shorter; "etc." in a blacklist outsourced completeness to the LLM - Drop `strace -p <pid> -c -f` from the Linux stack-dump branch: `-c` blocks until the target exits, hanging the diagnostic on the very conditions it should diagnose; `ptrace_scope=1` would also misreport permission errors as process symptoms. Keep `cat /proc/<pid>/stack` - Warn that `ps -ww` command lines may include CLI-arg credentials and that `sample` stack frames may include in-memory secrets — redact before quoting in the report - Cover the "no sessions found at all" case so a fresh machine doesn't get reported as "all healthy" when zero data was collected 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): /stuck overview-vs-step3 consistency and self-explanatory state triage - Update "What to look for" overview from `pgrep -lP <pid>` to `pgrep -P <pid>` to match step 3 (overview was left behind in the previous round when step 3 was upgraded to capture child state) - Add a triage sentence to step 3: when the state alone explains the problem (`T` = stopped, `Z` = zombie), skip child/log/stack inspection and go straight to the report 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): correct /stuck runtime base priority order and resolution The actual priority in `Storage.getRuntimeBaseDir()` is `QWEN_RUNTIME_DIR` > `advanced.runtimeOutputDir` setting > `QWEN_HOME` > `~/.qwen`. The previous round merged the `advanced.runtimeOutputDir` mention but listed it after `QWEN_HOME`, and the shell snippet skipped the settings layer entirely — so on a machine where only the setting was configured, the skill would silently look in `~/.qwen` and miss all sessions. - Reorder the prose priority list to match the source - Add a `jq`-based read of `~/.qwen/settings.json` between the env-var and `QWEN_HOME`/default fallbacks. Gracefully degrades if `jq` is absent or the setting is unset. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * feat(skills): improve /stuck diagnostic flow Functional upgrades found in self-review (no reviewer raised these): - Add network-hang detection bullet to step 3. Hung HTTPS requests to the model API are the most common qwen-code "stuck" mode and showed as healthy under all previous heuristics (low CPU + S state). macOS uses `lsof -i -p`, Linux uses `ss -tnp`. - Add a fast path at the top of "Investigation steps": when the user passes a digit-only PID, skip enumeration and go straight to per-PID ps + step 3. Avoids a full sidecar+ps scan in the targeted case. - Replace per-file sidecar liveness check with a single bash loop that emits only live (pid, sidecar-path) pairs. On machines with many stale sidecars this drops 50+ separate reads. - Promote `~/.qwen/debug/latest` to the primary debug-log entry point (it usually points to the suspicious session). Sidecar-derived path becomes the fallback. - Bound the debug-log read with `tail -n 200` so the model doesn't attempt to load multi-GB log files. - Replace the placeholder `<child_pids>` for `ps -p` with a runnable `pgrep -P <pid> | xargs -I{} ps -p {} -o ...` composition. - Drop the redundant "substitute <pid> only after validation" note in step 3 — the digit-only whitelist in Argument validation already enforces this; PIDs from ps/sidecar are inherently digit-only. End-to-end tmux smoke test confirms the flow runs to completion with a correct structured report. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): /stuck — RUNTIME_DIR preamble + jq-free sidecar liveness Two issues caught by Codex review: 1. **PID fast path left $RUNTIME_DIR unset.** Step 3 references `"$RUNTIME_DIR"/debug/<session-id>.txt` but the fast path skipped step 1 where it was resolved, so debug-log lookup degraded to `/debug/latest` (broken absolute path). Fix: extract RUNTIME_DIR resolution into a preamble that runs before both paths. Also add a `grep -l "pid": <PID>` lookup in the fast path so it can match the given PID to its sidecar and recover the session ID for log lookup. 2. **Sidecar liveness loop required `jq`.** Default macOS / minimal Linux images don't ship `jq`, so the loop emitted nothing for every sidecar — the "preferred reliable" path silently failed and the skill fell back to the less accurate `ps | grep`. Replace with a single-spawn `node -e` script: node is guaranteed present (qwen-code itself runs on it). The settings.json `jq` lookup stays — that one gracefully degrades to QWEN_HOME/default if `jq` is missing. Both verified by hand: liveness loop correctly emits live PID/sidecar pairs (56219, 33534), `grep -l` lookup correctly finds the sidecar for a given PID and emits empty for non-matches. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): /stuck — validate fast-path PID is a Qwen Code process Codex review caught that the targeted PID fast path accepted any digit-only PID and dumped its full command line, bypassing the Qwen- process filter that the general scan applies via `grep -E '(qwen|node.*qwen|bun.*qwen)'`. Cross-user PIDs are already filtered (`kill -0` returns EPERM), but **same-user non-Qwen processes** would have their argv (potentially including secret CLI flags) printed into the chat. Fix: add a single-line validation pipeline before the stats dump: `kill -0 <pid> && ps -p <pid> -o command= -ww | grep -qE '(qwen|node.*qwen|bun.*qwen)'`. If it returns non-zero, refuse with "PID is not a current-user Qwen Code session" and stop the diagnostic. Otherwise proceed. Verified by manual test against a real Qwen Code session PID (matches) and PID 1 / launchd (correctly rejected). 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): /stuck — settings path, sidecar grep, ps regex, lsof safety Four issues from PR review: 1. **Settings path honors QWEN_HOME.** The `jq` lookup in the preamble hardcoded `~/.qwen/settings.json`, but `getGlobalSettingsPath()` resolves to `$QWEN_HOME/settings.json` when set. Now uses `"${QWEN_HOME:-$HOME/.qwen}/settings.json"`. 2. **Sidecar grep uses `-El`.** Without `-E`, BSD `grep` on macOS may not treat `\b` as a word boundary in BRE. Also added a note: when PID reuse makes multiple sidecars match, prefer the most recently modified file via `ls -t | head -n 1`. 3. **Process regex tightened to avoid false positives.** The old `(qwen|node.*qwen|bun.*qwen)` matched any path containing "qwen" anywhere — so `qwen-playground/server.js`, `qwen-polyfill.js`, and even unrelated processes that pass a qwen-code path as `--cwd` (e.g., Codex plugin brokers) all matched. Replaced with `(qwen-code/[^ ]*\.(js|ts|mjs|cjs)( |$)|/qwen( |$))` — requires the `qwen-code/` substring to be followed by a script-file path, OR the bin invocation to end in `/qwen`. Verified on the local machine that broker processes are no longer matched while real Qwen sessions (worktree dev, dist/cli.js, qwen serve daemons) all are. 4. **lsof safety.** Added `-nP` to skip reverse-DNS and port lookups which can themselves hang. Mentioned `timeout 10` / `gtimeout 10` as an optional prefix when available — qwen-code's shell tool already has a backstop timeout, so this is belt-and-suspenders. Note: tested `\b` in BSD ERE on macOS — it does work correctly with `-E`, so the `-El` switch alone fully addresses concern #2's portability claim (BRE-without-E remains broken but is no longer used). 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): /stuck — expand ~ and resolve relative paths in RUNTIME_DIR `Storage.resolvePath()` in qwen-code expands `~` and resolves relative paths before using `advanced.runtimeOutputDir`. The shell preamble was reading the raw JSON value via `jq`, so a user with `"runtimeOutputDir": "~/.qwen-runtime"` would pass the literal string to the glob — bash does not expand `~` inside double quotes — and the sidecar scan would silently find nothing and fall back to ps-only mode. Add two bash lines after the jq lookup: - `${RUNTIME_DIR/#\~/$HOME}` to substitute leading tilde - `case ... cd && pwd` to resolve relative paths to absolute (clears RUNTIME_DIR if cd fails so the chain falls through to QWEN_HOME) Smoke tested: tilde paths expand, absolute paths pass through, relative paths resolve, nonexistent dirs clear cleanly, empty stays empty. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(skills): /stuck — round-N review feedback Adopted 9 of the 16 review suggestions; declined 5; 1 already done. - Anchor process regex to `(^|/)qwen-code[^ /]*/`. Now matches renamed clones (`qwen-code-dev`, `qwen-code-x1`, worktrees) AND rejects prefix false positives (`analyze-qwen-code/`, `my-qwen-code-tool/`). Verified against 10 cases. - Clarify RSS unit conversion: KB ÷ 1024 = MB, KB ÷ 1048576 = GB. The 4GB threshold is `4194304` KB raw, or 4 in GB. Prevents the model from dividing once and comparing to 4, which would over-alert by 1024×. - Add `State S with low CPU` to the Signs list so initial triage flags the most common hang signature (hung HTTPS to model API) instead of only catching it inside step 3. - Split fast path validation into two guards with distinct messages: dead/wrong-user vs. yours-but-not-Qwen. Plus add the same credential-redaction note that step 2 already has. - Replace `pgrep | xargs -I{} ps` with a single `ps -p $CHILDREN` call (avoids forking N times) and add `-ww` so long child cmdlines don't truncate. - Wrap macOS `sample <pid> 3` with optional `timeout 15` (or `gtimeout 15`). Same belt-and-suspenders pattern used for `lsof`. - Note that `ss -tnp -p` requires root/CAP_NET_ADMIN; non-root sees `-` in the PID column. Tell the model to fall back to `lsof` instead of concluding "no connections". Declined: self-PID via `$$` (wrong PID — `$$` is the spawned shell, not qwen), pgrep fallback for distroless (over-engineering), `\b` matches negative numbers (false alarm — `:[[:space:]]*` won't match through `-`), regex DRY abstraction (no value in markdown prompts), project-level settings.json read (already declined; same trade-off). 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * test(skills): add integration test that parses every bundled SKILL.md The bundled skill loader (`SkillManager.parseSkillFileInternal`) silently catches and debug-logs frontmatter parse errors, so a typo in any SKILL.md (missing `description`, broken `---` delimiter, `allowedTools` written as a scalar) merges with green CI and only surfaces when a user invokes the skill — at which point the skill is missing from autocomplete with no indication why. Add a tiny integration test that walks `packages/core/src/skills/bundled/`, runs every `SKILL.md` through the real `parseSkillContent` (no mocks), and asserts: name matches the directory, description is non-empty, body is non-empty, and `allowedTools` (if present) is an array. Lives in its own file because `skill-load.test.ts` mocks `fs/promises` and the YAML parser, which would defeat the purpose of an integration test. New file uses real fs and the real loader. Negative-case verified: deliberately corrupting `stuck/SKILL.md`'s frontmatter delimiter makes only that file's test fail; restoring it returns the suite to all-green. Addresses wenshao's standing [Critical] review (2026-05-15 12:29Z) about the bundled skill system lacking automated tests for SKILL.md parsing. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
PR QwenLM#1791 introduced TPM throttling detection but checked it before Retry-After headers. Server-specified delays were ignored, always waiting the hardcoded 1 minute. Fixed by reordering checks: Retry-After → TPM → exponential backoff.
Dive Deeper
The Bug
When a 429 error contains both a TPM throttling message and a Retry-After header, the original implementation:
The
continuestatement skips Retry-After evaluation. A server specifying 30 seconds via Retry-After gets ignored.The Fix
Converted to if/else-if chain establishing proper precedence:
HTTP Retry-After headers now take precedence over client-side heuristics, as specified by RFC.
Reviewer Test Plan
npx vitest run packages/core/src/utils/retry.test.tsAll 3637 core tests pass, including original TPM throttling tests.
Testing Matrix
Linked issues / bugs
Related to QwenLM#1791
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
gb4w8c3ygj-default-sea.rum.aliyuncs.com/opt/hostedtoolcache/node/24.13.0/x64/bin/node /opt/hostedtoolcache/node/24.13.0/x64/bin/node --conditions node --conditions development /home/REDACTED/work/qwen-code/qwen-code/node_modules/tinypool/dist/entry/process.js(dns block)/opt/hostedtoolcache/node/24.13.0/x64/bin/node /opt/hostedtoolcache/node/24.13.0/x64/bin/node --conditions node --conditions development /home/REDACTED/work/qwen-code/qwen-code/node_modules/tinypool/dist/entry/process.js git /usr/bin/grep niq | wc -l stash /home/REDACTED/.lonode grep phys�� | wc -l sh /bin/sh /entry/process.jgrep(dns block)/opt/hostedtoolcache/node/24.13.0/x64/bin/node /opt/hostedtoolcache/node/24.13.0/x64/bin/node --conditions node --conditions development /home/REDACTED/work/qwen-code/qwen-code/node_modules/tinypool/dist/entry/process.js dirname /.bin/sh niq | wc -l(dns block)If you need me to access, download, or install something from one of these locations, you can either:
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.