Commit graph

5465 commits

Author SHA1 Message Date
Reid
d439e7d738
fix(sdk): avoid leaking process exit listeners in ProcessTransport (#3295)
* fix(sdk): avoid leaking process exit listeners in ProcessTransport

* Strengthen ProcessTransport cleanup during process exit.

  This updates the shared process-exit cleanup path to use a best-effort
  SIGTERM/SIGKILL sequence and adds coverage to verify the global exit
  handler terminates all active child processes.

  It keeps the listener leak fix in place while closing the remaining gaps
  found in review.
2026-04-16 10:52:19 +08:00
DennisYu07
b5115e731e
feat(hooks): Add HTTP Hook, Function Hook and Async Hook support (#2827)
* add http/async/function type

* fix url error

* resolve comment

* align cc non blocking error

* fix hookRunner for async

* fix(hooks): update hook type validation to support http and function types

- Change validated hook types from ['command', 'plugin'] to ['command', 'http', 'function']
- Add validation for HTTP hooks requiring url field
- Add validation for function hooks requiring callback field
- Add comprehensive test coverage for all hook type validations

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(hooks): align SSRF protection with Claude Code behavior

- Allow 127.0.0.0/8 (loopback) for local dev hooks
- Allow localhost hostname for local dev hooks
- Allow ::1 (IPv6 loopback) for local dev hooks
- Add 100.64.0.0/10 (CGNAT) to blocked ranges (RFC 6598)
- Update tests to match Claude Code's ssrfGuard.ts behavior

This fixes HTTP hooks failing to connect to local dev servers.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* refactor(hooks): align HTTP hook security with Claude Code behavior

- Add CRLF/NUL sanitization for env var interpolation (header injection)
- Implement combined abort signal (external signal + timeout)
- Upgrade SSRF protection to DNS-level with ssrfGuard
  - Allow loopback (127.0.0.0/8, ::1) for local dev hooks
  - Block CGNAT (100.64.0.0/10) and IPv6 private ranges
- Increase default HTTP hook timeout to 10 minutes
- Fix VS Code hooks schema to support http type
  - Add url, headers, allowedEnvVars, async, once, statusMessage, shell fields
  - Note: "function" type is SDK-only (callback cannot be serialized to JSON)

* feat(hooks): enhance Function Hook with messages, skillRoot, shell, and matcher support

- Add MessagesProvider for automatic conversation history passing to function hooks
- Add FunctionHookContext with messages, toolUseID, and signal
- Add skillRoot support for skill-scoped session hooks
- Add shell parameter support for command hooks (bash/powershell)
- Add regex matcher support for hook pattern matching
- Add statusMessage to CommandHookConfig
- Change default function hook timeout from 60s to 5s
- Add comprehensive unit tests for all new features

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* add session hook for skill

* fix function hook parsing

* refactor ui for http hook/async hook/function hook

* update doc and add integration test

* change telemetryn type and refactor SSRF

* fix project level bug

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-16 10:10:33 +08:00
ChiGao
70396d1276
feat: optimize compact mode UX — shortcuts, settings sync, and safety (#3100)
* feat: optimize compact mode UX — shortcuts, settings sync, and safety improvements

- Add Ctrl+O to keyboard shortcuts list (?) and /help command
- Sync compact mode toggle from Settings dialog with CompactModeContext
- Protect tool approval prompts from being hidden in compact mode
  (MainContent forces live rendering during WaitingForConfirmation)
- Remove snapshot freezing on toggle — treat as persistent preference,
  not temporary peek (differs from Claude Code's session-scoped model)
- Add compact mode tip to startup Tips rotation for non-intrusive discovery
- Remove compact mode indicator from footer to reduce UI clutter
- Add competitive analysis design doc (EN + ZH) comparing with Claude Code
- Update user docs (settings.md) and i18n translations (en/zh/ru/pt)

Relates to #3047, #2767, #2770

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: remove frozenSnapshot dead code and Chinese design doc

- Remove frozenSnapshot state, useEffect, and all related logic from
  AppContainer, MainContent, CompactModeContext, and test files
- Simplify MainContent to always render live pendingHistoryItems
- Delete compact-mode-design-zh.md (redundant Chinese translation)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review feedback for compact mode optimization

- Add refreshStatic() call after setCompactMode in SettingsDialog
  so already-rendered Static history updates immediately
- Fix outdated column split comment in KeyboardShortcuts (5+4+4)
- Update design doc: remove all frozenSnapshot references, renumber
  optimization recommendations, fix file reference descriptions
- Add missing i18n keys for de.js and ja.js locales
- Add test for SettingsDialog compact mode sync with CompactModeContext

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: prevent subagent confirmation from being hidden in compact mode

hasConfirmingTool only checks ToolCallStatus.Confirming, but subagent
approvals arrive via resultDisplay.pendingConfirmation while the tool
status remains Executing. Add hasSubagentPendingConfirmation to the
showCompact guard so tool groups with pending subagent confirmations
are always force-expanded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: force show subagent confirmation result in compact mode

The previous fix (47ee03c) correctly force-expanded the tool group
wrapper when a subagent had pending confirmation, but each inner
ToolMessage still hid its resultDisplay due to compactMode check,
which hid the AgentExecutionDisplay containing the inline confirmation
UI.

Add isAgentWithPendingConfirmation to forceShowResult conditions so
the inner AgentExecutionDisplay is rendered even in compact mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(compact-mode): merge consecutive tool groups across hidden items

In compact mode, sequential tool calls across multiple LLM turns each
produced a separate bordered box, defeating the "compact" intent. The
model typically emits a `gemini_thought` between consecutive tool calls,
which is hidden in compact mode — so visually the boxes look adjacent,
but in `history` they are separated by hidden items.

This commit adds render-time merging of consecutive tool_group history
items, where "consecutive" allows hidden-in-compact items
(`gemini_thought`, `gemini_thought_content`) between them.

Key pieces:
- New `mergeCompactToolGroups` utility that merges adjacent mergeable
  tool_groups, skipping hidden items between them. Force-expand
  conditions (Confirming/Error tools, subagent pending confirmation,
  user-initiated, focused embedded shell) preserve group boundaries so
  authorization prompts, errors, and shell focus stay visible.
- `MainContent.tsx` applies the merger only when `compactMode === true`
  (verbose mode is unchanged) and calls `refreshStatic()` when a merge
  consolidates items, because Ink's `<Static>` is append-only and
  cannot replace already-committed terminal content.
- `CompactToolGroupDisplay.tsx` shows a `× N` count when a merged
  group contains more than one tool, matching the existing single-turn
  multi-tool display style.
- 19 unit tests covering empty/single/multiple groups, hidden-item
  skipping (the 8-tool real-world scenario), force-expand boundaries,
  mixed tool types, and complex sequences.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 09:29:24 +08:00
DennisYu07
08d3d6eb6f
feat(acp): add complete hooks support for ACP integration (#3248)
* complete hooks for acp

* resolve comment

* reslove test

* resolve comment for SessionEnd/SessionStart/PostToolUseFailure/PostToolUse
2026-04-16 09:28:26 +08:00
tanzhenxin
a6612940f8
fix(cli): block discontinued qwen-oauth model selection in ModelDialog (#3299)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
PR #3291 discontinued the Qwen OAuth free tier but intentionally left the
ModelDialog unchanged, relying on server rejection for qwen-oauth models.
This follow-up adds proper UI handling consistent with the AuthDialog:

- Mark qwen-oauth model entries with "(Discontinued)" label and warning color
- Replace descriptions with "Discontinued — switch to Coding Plan or API Key"
- Block selection with inline error message instead of calling switchModel
- Show ⚠ discontinuation notice in the detail panel for highlighted entries
- Runtime OAuth models (existing cached tokens) remain selectable until server
  rejects them (soft cutoff principle from PR #3291)
- Add i18n strings for the new error message across all 7 locale files

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-15 23:17:32 +08:00
tanzhenxin
17269fa0e6
chore(release): bump version to 0.14.5 (#3298)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-15 22:43:29 +08:00
易良
0ca2d23103
fix(channels/dingtalk): prioritize senderStaffId over senderId for allowedUsers matching (#3294)
DingTalk provides two user identifiers: `senderId` (an internal platform ID
like `$:LWCP_v1:xxx`) and `senderStaffId` (the human-readable staff/employee
ID). The current code uses `senderId` first when constructing the Envelope,
which means `allowedUsers` configuration cannot match against the staff ID
that users actually know and configure.

Swap the priority so `senderStaffId` is preferred when available, falling back
to `senderId` only when `senderStaffId` is absent.

Made-with: Cursor
2026-04-15 22:34:08 +08:00
tanzhenxin
f6271c61b6
feat(auth): discontinue Qwen OAuth free tier (2026-04-15 cutoff) (#3291)
* feat(auth): discontinue Qwen OAuth free tier (2026-04-15 cutoff)

The Qwen OAuth free tier has reached its end-of-life date. This updates
all client-side messaging, blocks new OAuth signups, and guides existing
users to alternative providers.

* fix(test): add getModelsConfig mock and update QWEN_OAUTH test expectations

- Add getModelsConfig() to Config mocks in gemini.test.tsx (3 failures)
- Update validateNonInterActiveAuth test to expect exit for QWEN_OAUTH
  since validateAuthMethod now returns an error for discontinued free tier
2026-04-15 22:30:20 +08:00
Yan Shen
679446d1da
fix(cli): ignore literal Tab input in BaseTextInput (#3270)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* refactor(BaseTextInput): ignore literal Tab input in keyboard handler

- Prevent insertion of literal tab characters in the BaseTextInput component
- Require consumers to intercept Tab via onKeypress for custom behavior
- Ensure smoother handling of Tab key without affecting buffer content

* fix(cli): preserve tabbed paste in paste workaround

- Mark single-line raw chunks containing tabs as paste events in the pasteWorkaround path
- Keep a literal Tab key as a non-paste keypress
- Add KeypressContext coverage for literal Tab keys and single-line tab-separated raw chunks
2026-04-15 15:49:52 +08:00
zhangxy-zju
ae424e004b
feat(acp): LLM-based message rewrite middleware with custom prompts (#3191)
* feat(acp): LLM-based message rewrite middleware

Add MessageRewriteMiddleware that intercepts ACP messages and appends
LLM-rewritten versions with _meta.rewritten=true at turn boundaries.

Original messages pass through unmodified. At the end of each turn
(before tool calls or at response end), accumulated thought/message
chunks are sent to LLM for rewriting into business-friendly text.

- TurnBuffer: accumulates chunks per turn
- LlmRewriter: calls LLM with configurable prompt
- MessageRewriteMiddleware: orchestrates intercept → buffer → rewrite → emit
- BaseEmitter.sendUpdate: routes through middleware when configured
- Session: initializes middleware from settings.messageRewrite config

Enable via settings.json:
{
  "messageRewrite": {
    "enabled": true,
    "target": "both",
    "prompt": "custom system prompt for rewriter"
  }
}

Rewritten messages carry _meta.rewritten=true for frontend to
prioritize display. Original messages remain for debugging.

* fix: TypeScript 编译错误修复 + 优化默认改写 prompt(参考竞品风格)

* fix: 从 user/workspace originalSettings 读取 messageRewrite 配置(绕过 schema 校验)

* feat: 非交互 CLI 模式也支持 message rewrite(eval 可用)

* fix: 禁用 rewriter LLM 的 thinking,过滤 thought 部分只取纯文本输出

* fix: cron 路径补齐 message rewrite flush + 代码质量优化

- Session.ts cron 路径添加 messageRewriter.flushTurn() 调用
- nonInteractiveCli.ts cron 路径添加 turnBuffer 累积 + flush + rewrite
- 提取 loadRewriteConfig() 共享函数,消除两处重复配置读取
- 主路径和 cron 路径添加 turnBuffer.markToolCall()
- rewrite 调用添加 30s 超时保护(AbortSignal.timeout)
- 修复 import 语句被 const 声明分割的问题

* feat: rewrite 支持 async/sync 模式(默认 async,不增加执行时间)

* feat: rewrite prompt 通用化 + 上下文连贯 + promptFile + async 修复

- 默认 prompt 改为通用英文版(适配任意 coding agent,不绑定数据分析场景)
- 支持 promptFile 配置项,从文件加载自定义 prompt(优先于 inline prompt)
- 上下文连贯性:lastOutput 记录上一轮改写结果,拼接到下一轮输入,
  避免连续 turn 间信息重复
- 修复 CLI 非交互模式 async rewrite 丢失:void doRewrite() 改为
  pendingRewrites 数组 + emitResult 前 Promise.allSettled
- 增加 debug logging:REWRITE INPUT/OUTPUT 完整内容 + prev_output 长度

* refactor: remove sync rewrite mode, always use async (non-blocking) rewrite

- Remove `async` field from MessageRewriteConfig
- MessageRewriteMiddleware.flushTurn() always fires in background
- nonInteractiveCli.ts main & cron paths always push to pendingRewrites
- No user-facing latency from rewrite calls

* fix: address review feedback — trust check, timeout, history replay

1. loadRewriteConfig: skip workspace settings when !isTrusted, preventing
   untrusted repos from enabling rewriter with a custom prompt
2. MessageRewriteMiddleware.flushTurn: always enforce 30s timeout internally,
   even when caller provides no AbortSignal (interactive path)
3. Install rewriter AFTER history replay completes (Session.installRewriter),
   so historical messages are never rewritten on session load

* fix: address second round review — target filter, timeout, rewrite queue

1. nonInteractiveCli: apply rewriteConfig.target filter to accumulation
   (main path and cron path), matching MessageRewriteMiddleware behavior
2. nonInteractiveCli: add 30s AbortSignal.timeout to rewrite calls in
   both main and cron paths
3. MessageRewriteMiddleware: replace single pendingRewrite slot with
   pendingRewrites array + Promise.allSettled, ensuring all rewrites
   complete before session exits

* test: add unit tests for TurnBuffer, loadRewriteConfig, MessageRewriteMiddleware

- TurnBuffer: flush, reset, isEmpty, markToolCall, whitespace filtering (12 tests)
- loadRewriteConfig: isTrusted gating, workspace/user precedence (5 tests)
- MessageRewriteMiddleware: target filtering, tool_call boundary flush,
  pendingRewrites queue, rewrite metadata (9 tests)

* fix: config.test.ts use unknown cast for LoadedSettings stub (fix tsc --build)

* fix: filter LLM literal "empty string" responses in rewriter output

LLM sometimes outputs "(空字符串)" or similar text instead of actual
empty string when instructed to "return empty string". Add regex patterns
to catch common variants and treat them as null (skip rewrite output).

* revert: remove LLM empty-string pattern defense, rely on prompt fix instead

* fix: prevent async rewrite from corrupting adapter state + honor config.model

1. nonInteractiveCli: rewrite promises now return data only, adapter
   emission happens synchronously via emitSettledRewrites() at safe
   boundaries (before next turn starts, before cron next turn, before
   final result). Prevents concurrent startAssistantMessage corruption.
2. LlmRewriter: use rewriteConfig.model when set, fallback to
   config.getModel(). Previously model field was defined but ignored.

* docs: add messageRewrite configuration guide to settings.md

* Revert "docs: add messageRewrite configuration guide to settings.md"

This reverts commit ecd57e2d5a.

* feat: add contextTurns config for rewrite history context

Allow configuring how many previous rewrite outputs are included as
context when rewriting a new turn:
- contextTurns: 1 (default) = last rewrite only
- contextTurns: 0 = no context
- contextTurns: N = last N rewrites
- contextTurns: "all" = all previous rewrites

* refactor: rename target 'both' to 'all' + add LlmRewriter unit tests

- Rename target value 'both' → 'all' for future extensibility (e.g. 'tool')
- Add LlmRewriter tests: contextTurns (0/1/N/all), model override, filtering
- Total: 35 tests across 4 test files

* refactor: remove message rewrite from non-interactive CLI mode

Non-interactive mode (qwen -p "..." --output-format json) consumers are
scripts/programs that don't need user-friendly rewrites. Additionally,
the JSON output adapter doesn't support _meta fields, so rewritten text
was silently mixed into normal assistant messages without any marker.

Rewrite middleware is now ACP-only (Session path).

* revert: restore package-lock.json and nonInteractiveCli.ts to main state

* docs: add README for message rewrite middleware

Explain the feature purpose (business-oriented output customization),
mark it as a temporary solution, and reference the hook-based
alternative (#3266) for future discussion.

* docs: move temporary-solution notice to top of README

* docs: simplify temporary-solution notice in rewrite README
2026-04-15 14:53:24 +08:00
Reid
3c556c01f3
fix(cli): make /bug easier to open in terminals without hyperlink support (#3257)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
2026-04-15 10:37:39 +08:00
Shaojin Wen
e4f7a7f380
fix(core): allow thought-only responses in GeminiChat stream validation (#3251)
Models using thinking/reasoning modes may emit only thought content
without explicit text output. The stream validation previously rejected
these as 'empty' responses. Now accepts responses that contain either
text content or thought content when a finish reason is present.

(cherry picked from commit a0b13911f4)

Co-authored-by: mingholy.lmh <mingholy.lmh@alibaba-inc.com>
2026-04-15 10:29:57 +08:00
jinye
17d9d8c706
fix(core): respect custom Gemini baseUrl from modelProviders (#3212)
* fix(core): pass gemini baseUrl via httpOptions

---------

Co-authored-by: QwenCode <qwen-coder@alibabacloud.com>
2026-04-15 08:58:37 +08:00
dreamWB
9f9ffbf955
fix(vscode): limit session tab title length to prevent tab bar overflow (#3249)
Some checks are pending
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* fix(vscode): limit session tab title length to prevent tab bar overflow

- Extract truncatePanelTitle() into panelTitleUtils.ts; operates on
  Unicode code points to avoid splitting surrogate pairs (e.g. emoji);
  truncates to first 50 code points + '…'
- Apply truncation uniformly in WebViewProvider for all updatePanelTitle
  messages, covering both createOrShowPanel and restorePanel paths
- Remove redundant 50-char truncation in SessionMessageHandler; panel
  title truncation is now the sole responsibility of WebViewProvider
- Align getSessionTitle() to use the same limit (50 code points + '…')
  for visual consistency between session list and tab title
- Add unit tests for truncatePanelTitle covering empty string, boundary,
  long title, and emoji surrogate-pair edge cases

Fixes #2873

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(vscode): reuse truncatePanelTitle in getSessionTitle and fix fallback title logic

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 19:59:55 +08:00
Shaojin Wen
6254b85cb6
fix(core): detect rate-limit errors from streamed SSE frames (#3246)
DashScope throttling (`Throttling.AllocationQuota`) surfaces as an
SSE `event:error` frame mid-stream with `:HTTP_STATUS/429` as a
comment and a non-numeric `code` in the payload. The existing
detection paths missed it, so subagents failed immediately with
`Failed to run subagent: id:1 event:error ...` instead of retrying.

- `getErrorStatus`: add a final fallback that parses `HTTP_STATUS/NNN`
  out of `error.message`, bounded by `\b` and the 100-599 range, so
  streamed errors where the SDK never sees a real HTTP status can
  still be classified.
- `getErrorCode`: fix three `|| null` early-returns that swallowed
  later fall-through paths when the provider code was non-numeric
  (`isApiError` top-level, `isApiError` JSON-in-message, and
  `isStructuredError`). The branches now fall through on non-numeric
  values so `.status` or the new `HTTP_STATUS/NNN` fallback can
  recover the real code.
2026-04-14 19:58:26 +08:00
qqqys
81dc742c93
feat(vscode-ide-companion): add /account for account display (#2984)
* feat(vscode-ide-companion): add /account for account display

* fix(account): intercept typed /account command and read session-level config

1. Handle literal /account in useMessageSubmit.ts so typing it triggers
   the account dialog instead of sending it as a chat message.
2. Pass sessionId through extMethod params and read from session config
   instead of agent-level config, so /account reflects model changes
   made via /model within the current session.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-14 17:22:33 +08:00
Shaojin Wen
83b394e423
feat(core): implement fork subagent for context sharing (#2936)
* feat(core): implement fork subagent for context sharing

- Make subagent_type optional in AgentTool
- Add forkSubagent.ts to build identical tool result prefixes
- Run fork processes in the background to preserve UX

* fix(core): fix test failures related to root execution and optional subagent_type

- Skip pathReader and edit tool permission tests when running as root
- Fix agent.test.ts to correctly mock execute call with extraHistory
- Remove unused imports in forkSubagent.ts

* fix(core): fix fork subagent bugs and add CacheSafeParams integration

Bug fixes:
- Fix AgentParams.subagent_type type: string -> string? (match schema)
- Fix undefined agentType passed to hook system (fallback to subagentConfig.name)
- Fix hook continuation missing extraHistory parameter
- Fix functionResponse missing id field (match coreToolScheduler pattern)
- Fix consecutive user messages in Gemini API (ensure history ends with model)
- Fix duplicate task_prompt when directive already in extraHistory
- Fix FORK_AGENT.systemPrompt empty string causing createChat to throw
- Fix redundant dynamic import of forkSubagent.js (merge into single import)
- Fix non-fork agent returning empty string on execution failure
- Fix misleading fork child rule referencing non-existent system prompt config
- Fix functionResponse.response key from {result:} to {output:} for consistency

CacheSafeParams integration:
- Retrieve parent's generationConfig via getCacheSafeParams() for cache sharing
- Add generationConfigOverride to CreateChatOptions and AgentHeadless.execute()
- Add toolsOverride to AgentHeadless.execute() for parent tool declarations
- Fork API requests now share byte-identical prefix with parent (DashScope cache hits)
- Graceful degradation when CacheSafeParams unavailable (first turn)

Docs:
- Add Fork Subagent section to sub-agents.md user manual
- Add fork-subagent-design.md design document

* fix(core): apply subagent tool exclusion to forked agents

Fork children were inheriting parent's cached tool declarations directly,
bypassing prepareTools() filtering and gaining access to AgentTool and
cron tools. Extract EXCLUDED_TOOLS_FOR_SUBAGENTS as a shared constant
and apply it to forkToolsOverride.

* fix(core): skip env history whenever extraHistory is provided

Previously gated on generationConfigOverride, which meant the no-cache
fallback path (CacheSafeParams unavailable) still ran getInitialChatHistory
and duplicated env bootstrap messages already present in the parent's
history. Gate on extraHistory instead so both fork paths skip env init.

* fix(core): use explicit skipEnvHistory flag for fork env handling

The previous fix gated env-init skipping on the presence of extraHistory,
but agent-interactive (arena) also passes extraHistory — its chatHistory is
env-stripped by stripStartupContext() and DOES need fresh env init for the
child's working directory. Skipping env there broke the interactive path.

Replace the implicit gate with an explicit skipEnvHistory option that only
fork sets (when extraHistory is present, since fork's history comes from
getHistory(true) and already contains env).

* fix(core): defend skipEnvHistory gate against empty extraHistory

Edge case: when the parent's rawHistory ends with a user message and has
length 1, extraHistory becomes []. The previous gate (extraHistory !==
undefined) would set skipEnvHistory: true, leaving the fork with neither
env bootstrap nor parent history. Check length > 0 so empty arrays fall
through to the normal env-init path.

* fix(core): apply skipEnvHistory to stop-hook retry execute

The second subagent.execute() call in the SubagentStop retry loop was
missing skipEnvHistory, so on retry the fork's env context would be
duplicated — same bug as the initial tanzhenxin report, just on a less
common code path.
2026-04-14 14:27:38 +08:00
jinye
7103c905f7
feat(cli): add startup performance profiler (#3232)
feat(cli): add startup performance profiler (#3219)

  Add a lightweight startup profiler activated via QWEN_CODE_PROFILE_STARTUP=1.
  When enabled, collects performance.now() timestamps at 7 key phases in main()
  and writes a JSON report to ~/.qwen/startup-perf/. Also records
  process.uptime() at T0 to capture module loading time not covered by
  checkpoint-based measurement.

  Key design decisions:
  - Only profiles inside sandbox child process to avoid duplicate reports
  - initStartupProfiler() is idempotent (resets state on each call)
  - Filename uses report.sessionId for consistency with JSON content
  - Zero overhead when disabled (single env var check)

  Initial measurement: module loading ~1342ms (94%), main() ~85ms (6%),
  confirming barrel exports and eager dependency loading as primary
  optimization targets for #3011.

  Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-14 14:14:42 +08:00
Shaojin Wen
1486e85385
feat(cli/sdk): expose /context usage data in non-interactive mode and SDK API (#2916)
Some checks are pending
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* feat(cli): implement non-interactive /context output and diagnostic

- Extract collectContextData() from contextCommand.ts for shared usage.
- Register /context in ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE.
- Extend SDK control protocol with GET_CONTEXT_USAGE request.
- Implement handleGetContextUsage in SystemController for programmatic token queries.
- Expose getContextUsage() method in the TypeScript SDK Query interface.

* fix: address review feedback and fix critical bugs in context usage feature

- Add missing `get_context_usage` route in ControlDispatcher (SDK calls would throw)
- Fix `executionMode` defaulting: use `?? 'interactive'` to match other commands
- Validate dynamic import of `collectContextData` before invoking
- Preserve original error message in handleGetContextUsage catch block
- Add ControlDispatcher test for get_context_usage routing
- Add JSDoc comment for context command in non-interactive allowlist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: re-check abort signal after async operations in handleGetContextUsage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add getContextUsage() to SDK TypeScript documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: clarify getContextUsage showDetails is a display hint, not a data filter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: make showDetails affect response shape, add getContextUsage test

- When showDetails is false, return empty detail arrays instead of full
  data so /context and /context detail produce different payloads
- Add unit test for Query.getContextUsage() covering request payload
  and response handling

* fix: strip UI type from SDK response, sync Java SDK protocol

- Remove leaked `type: 'context_usage'` from control response payload
- Add GET_CONTEXT_USAGE to Java SDK protocol mirror (enum, interface,
  union type)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 03:28:32 +08:00
pomelo
e90abf4c35
docs: update quota exceeded alternatives to OpenRouter and Fireworks (#3217)
* docs: update quota exceeded alternatives to OpenRouter and Fireworks

- Update README.md news section to recommend OpenRouter and Fireworks
  as primary alternatives, with ModelStudio as third option
- Update retry.ts quota error message to include OpenRouter and
  Fireworks URLs for users whose OAuth quota has been exhausted

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(test): update retry test assertions to match new quota error message

* docs: update free tier quota to 100 req/day with sunset notice and alternatives

Update all references to reflect the Qwen OAuth free tier policy change:
- 1,000 → 100 requests/day across code, i18n, and docs
- Add 2026-04-15 sunset date everywhere
- Guide users to OpenRouter, Fireworks AI, or ModelStudio in docs
- Remove CHANGELOG.md

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: tanzhenxin <tanzhenxing1987@gmail.com>
2026-04-13 21:45:38 +08:00
tanzhenxin
4daf7f9353
feat(core): add microcompaction for idle context cleanup (#3006)
Some checks are pending
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* feat(core): add microcompaction for idle context cleanup

Clear old tool result content from chat history when the user returns
after an idle period (default 60 min). Replaces functionResponse output
with a sentinel string for compactable tools (read_file, shell, grep,
glob, web_fetch, web_search, edit, write_file), keeping the N most
recent results intact (default 5). Runs before full compression so it
can shed tokens cheaply without an API call.

- Time-based trigger reuses lastApiCompletionTimestamp from thinking cleanup
- Per-part counting so keepRecent applies to individual tool results
  even when batched in parallel
- Preserves tool error responses (only clears successful outputs)
- Configurable via settings.json (context.microcompaction) with env var
  overrides for E2E testing
- Enabled by default

* refactor(config): unify idle cleanup settings under clearContextOnIdle

Consolidate thinking block cleanup and tool results microcompaction
config into a single `context.clearContextOnIdle` settings group:

  {
    "context": {
      "clearContextOnIdle": {
        "thinkingThresholdMinutes": 5,
        "toolResultsThresholdMinutes": 60,
        "toolResultsNumToKeep": 5
      }
    }
  }

- Use -1 on either threshold to disable that cleanup (no enabled bool)
- Remove separate `microcompaction` and `gapThresholdMinutes` settings
- Thinking cleanup: 5 min default (unchanged)
- Tool results cleanup: 60 min default
- Preserve tool error responses (only clear successful outputs)

* feat(vscode-ide-companion): add clearContextOnIdle settings configuration

- Add gapThresholdMinutes settings for thinking blocks, tool results, and retention count
- Remove deprecated gapThresholdMinutes from root settings level

This reorganizes the context clearing settings into a dedicated clearContextOnIdle object with configurable thresholds for thinking blocks and tool results.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): restrict microcompaction to user-initiated messages only

Move microcompactHistory() inside the UserQuery/Cron guard so model
latency during tool-call loops doesn't count as user idle time.

* docs: update settings docs for clearContextOnIdle config rename

Replace stale `context.gapThresholdMinutes` entry with the new
`context.clearContextOnIdle.*` settings group introduced in the
microcompaction feature.

* fix(core): address review comments on microcompaction PR

- Guard against NaN in toolResultsNumToKeep with Number.isFinite()
- Report effective keepRecent (after Math.max) in meta, not raw config
- Fix comment to mention cron messages alongside user messages

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-13 18:51:35 +08:00
tanzhenxin
34d560adcf
fix(core): allow Unicode characters in agent names (#3194)
The agent name validation regex only permitted ASCII letters, numbers,
hyphens, and underscores, silently rejecting agents with non-ASCII names
(e.g., Chinese "项目管理"). Replace the regex with Unicode property
escapes (\p{L}\p{N}) to allow letters and numbers from any script.

Also guard the lowercase naming convention warning so it only fires when
the name contains ASCII letters, since case is meaningless for CJK
scripts.

Fixes #3149
2026-04-13 18:24:13 +08:00
tanzhenxin
8d74a0cf0a
feat(subagents): add disallowedTools field to agent definitions (#3064)
* feat(subagents): add disallowedTools field to agent definitions

Add a `disallowedTools` blocklist to agent frontmatter, letting agents
specify tools they should not have access to. Supports exact tool names,
MCP server-level patterns (e.g., `mcp__slack`), and display name aliases.

Applied as a post-filter in AgentCore.prepareTools() after the existing
`tools` allowlist. Persisted through serialize/parse roundtrips.

* docs: document disallowedTools and MCP tool behavior for subagents

Add Tool Configuration section to sub-agents docs explaining:
- tools allowlist and disallowedTools blocklist
- How MCP tools follow the same allowlist/blocklist rules
- MCP server-level patterns in disallowedTools

* fix(subagents): validate disallowedTools in SubagentValidator

Reuse the existing validateTools() method to validate disallowedTools
entries at config validation time, catching non-string and empty entries
before they reach runtime.

* test: remove flaky BaseSelectionList scroll test on Windows
2026-04-13 18:24:02 +08:00
tanzhenxin
35420b03bc
feat(cli): support bare exit/quit commands to exit the CLI (#3201)
Typing `exit`, `quit`, `:q`, `:q!`, `:wq`, or `:wq!` at the prompt now
exits the CLI — same as `/quit`. This matches Claude Code behavior and
helps users on mobile (e.g. Termux) where Ctrl+C is harder to type.

Closes #3169
2026-04-13 18:21:27 +08:00
tanzhenxin
1604a7f418
fix(cli): stop refilling input with prior prompt on cancel (#3208)
When ESC (or Ctrl+C) cancelled an in-progress model response, the
input buffer was being repopulated with the just-submitted prompt.
The handler unconditionally read userMessages.at(-1) and called
buffer.setText with it, surprising users who expected ESC to leave
the input clean. The previous prompt is still recoverable via Up
arrow / Ctrl+P history navigation.

Queued follow-up messages still get moved into the buffer for
editing on cancel, but now via the atomic popAllMessages helper,
and any in-progress draft the user typed is preserved by prepending
the queued text instead of clobbering it (matching the existing
popQueueIntoInput convention in InputPrompt).

Fixes #3204
2026-04-13 18:21:04 +08:00
tanzhenxin
8d34d33246
chore: bump version to 0.14.4 (#3209)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-13 18:15:54 +08:00
tanzhenxin
62867702f7
fix(cli): honor --openai-api-key in non-interactive auth validation (#3187)
validateAuthMethod's pre-flight check only inspected OPENAI_API_KEY (and
settings.security.auth.apiKey), so credentials supplied via --openai-api-key
were rejected even though refreshAuth would have accepted them. macOS users
were unaffected because OPENAI_API_KEY is commonly exported in their shell
profile; on Linux without that env var, the CLI failed to start.

hasApiKeyForAuth now prefers the API key already resolved into
generationConfig.apiKey when a Config is provided. The unified resolver
folds CLI flags, env vars, settings, and modelProvider envKey lookups into
this single value, so validation matches runtime behavior.

Fixes #3171
2026-04-13 18:06:27 +08:00
tanzhenxin
9cdf7bd7c8
fix(core): show clear error when MCP server cwd does not exist (#3192)
* fix(core): show clear error when MCP server cwd does not exist

Validate that the configured cwd directory exists before spawning the
MCP server process. Previously, a non-existent cwd caused Node.js to
emit "spawn <cmd> ENOENT" — indistinguishable from the command binary
being missing. Now throws a descriptive error naming the server and
the missing path.

Fixes #3163

* test(core): add test for MCP stdio transport without cwd
2026-04-13 18:02:14 +08:00
tanzhenxin
9a889dc614
feat(skills): add model override support via skill frontmatter (#2949)
* feat(skills): add model override support via skill frontmatter

Allow skills to specify a `model` field in YAML frontmatter to override
which model is used for subsequent turns within the same agentic loop.
The override flows through ToolResult → ToolCallResponseInfo →
SendMessageOptions and naturally expires when the loop ends.

Resolves #2052

* fix(core): only include modelOverride in response when defined

Fixes strict equality test failures in nonInteractiveToolExecutor.test.ts
where the extra undefined modelOverride field caused object mismatch.

* fix(skills): fix model override pipeline issues

- Wire up modelOverride in interactive CLI path (useGeminiStream)
- Fix inherit/no-model unable to clear a prior override by using
  'in' operator instead of truthiness checks in scheduler and CLI
- Reject empty/whitespace model strings in parseModelField()
- Extract shared parseModelField() to deduplicate skill-load and
  skill-manager parsing logic
- Propagate modelOverride through stop-hook continuation in client

* fix(skills): persist model override across turns in interactive and cron paths

The interactive path stored the skill model override in a local variable,
causing it to be lost when subsequent non-skill tool turns ran. Use a ref
to persist the override for the duration of the agentic loop, resetting on
new user messages. Also propagate modelOverride in the cron execution loop
for consistency with the main non-interactive path.

* fix(skills): preserve model override on retry and add unit tests

Retry in interactive mode was clearing modelOverrideRef, causing the
skill-selected model to silently fall back to session default. Guard
the reset so retries preserve the active override.

Add unit tests for parseModelField (edge cases, type validation) and
modelOverride propagation through the skill tool result path.
2026-04-13 17:57:41 +08:00
tanzhenxin
189df1b098
fix(core): respect respectGitIgnore setting in @file injection path (#3197)
pathReader.ts hardcoded respectGitIgnore: true when filtering files for
@{path} injection (used by slash commands), ignoring the user's
context.fileFiltering.respectGitIgnore setting. This meant gitignored
files were silently dropped even when the user explicitly set the
setting to false.

Now reads the filtering options from config instead of hardcoding.

Fixes #3142
2026-04-13 17:56:35 +08:00
tanzhenxin
0026777828
feat(subagents): propagate approval mode to sub-agents (#3066)
* feat(subagents): propagate approval mode to sub-agents

Replace hardcoded PermissionMode.Default with resolution logic:
- Permissive parent modes (yolo, auto-edit) always win
- Plan-mode parents keep sub-agents in plan mode
- Agent definitions can declare approvalMode in frontmatter
- Default fallback is auto-edit in trusted folders
- Untrusted folders block privileged mode escalation

Also maps Claude permission aliases (acceptEdits, bypassPermissions,
dontAsk) to qwen-code approval modes in the converter.

* fix(subagents): correct dontAsk mapping and add approval mode resolution tests

Map Claude's `dontAsk` to `default` instead of `auto-edit` — `dontAsk`
denies prompts (restrictive) so `default` is a closer semantic match.

Add 9 unit tests covering the full `resolveSubagentApprovalMode` decision
matrix: permissive parent override, agent-declared modes, trusted/untrusted
folder blocking, and plan-mode fallback.

* test: remove flaky InputPrompt tab-suggestion test on Windows
2026-04-13 17:50:26 +08:00
Shaojin Wen
b3bc42931e
feat: add contextual tips system with post-response context awareness (#2904)
* feat: add contextual tips system with post-response context awareness

Add a context-aware tips system that proactively shows helpful tips based
on session state. Post-response tips warn when context usage exceeds 80%
or 95%, suggesting /compress. Startup tips rotate across sessions via LRU
scheduling with cross-session persistence (~/.qwen/tip_history.json).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use value import for runtime values in useContextualTips

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address PR review feedback

- Use lastSessionTimestamp instead of totalShown for cross-session LRU
- Move getTipHistory singleton from Tips.tsx to services/tips/index.ts
- Defer TipHistory.load() when hideTips is true (no side effects)
- Use os.tmpdir() in tests for cross-platform portability
- Add proper translations for de/ja/pt/ru locale files
- Accept TipHistory | null in useContextualTips

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Copilot review feedback

- Validate tips field type in TipHistory.load() to handle corrupted JSON
- Split approval-mode tip into platform-specific variants using ctx.platform
- Add afterEach cleanup for temp files in all test suites
- Guard useContextualTips against null tipHistory

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: import shared DEFAULT_TOKEN_LIMIT, harden tipHistory, set file permissions

- Import DEFAULT_TOKEN_LIMIT from @qwen-code/qwen-code-core instead of
  hardcoding 1_048_576 in tipRegistry.ts and useContextualTips.ts
- Add normalizeEntry() to defensively handle corrupted tip history entries
- Write tip_history.json with mode 0o600 for privacy on multi-user systems

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove unused compressionThreshold from TipContext

compressionThreshold was defined in TipContext but never used by any tip's
isRelevant check. Remove it to avoid misleading consumers into thinking
tips respect the user's compression settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: sanitize sessionCount and getLastShown against corrupted tip history

- Validate sessionCount is finite and non-negative in TipHistory.load()
- Use normalizeEntry() in getLastShown() for corrupted lastSessionTimestamp

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add contextual tips user documentation

Add docs/users/features/tips.md covering startup tips, post-response
context warnings, tip history persistence, and the hideTips setting.
Update settings.md description and register the new page in _meta.ts.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 17:40:27 +08:00
易良
6af0f37bb8
ci(release): parallelize release validation (#3132)
* ci(release): parallelize release validation

* ci(release): allow publish when tests are skipped

* ci(release): drop planning artifact from workflow PR

* ci(release): address workflow review findings

* ci(release): fix quality job bootstrap

* ci(release): fix docker test and dry-run notify flow
2026-04-13 17:16:53 +08:00
pomelo
4a3ccbc3f9
fix(cli): set qwen3.5-plus as default model for Coding Plan (#3193)
- Change default model from qwen3.6-plus to qwen3.5-plus for both China and Global regions
- qwen3.6-plus requires Pro subscription, Lite users cannot use it
- Add description to qwen3.6-plus indicating Pro subscription requirement
- Update MAINLINE_CODER_MODEL to qwen3.5-plus for OpenAI-compatible API default

Fixes #3037

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-13 17:11:50 +08:00
pomelo
338c0b1e9e
refactor: merge test-utils package into core (#3200)
* refactor: merge test-utils package into core

Consolidate the standalone @qwen-code/qwen-code-test-utils package
into packages/core/src/test-utils/, eliminating the need for a
separate package that only provided createTmpDir, cleanupTmpDir,
and FileSystemStructure type.

Changes:
- Move file-system-test-helpers.ts into core/src/test-utils/
- Re-export from core's test-utils index
- Update 3 core test files to use relative imports
- Update cli useAtCompletion test to import from @qwen-code/qwen-code-core
- Remove test-utils devDependency from core and cli package.json
- Delete packages/test-utils/ directory

All affected tests pass (fileSearch, crawler, ignore, useAtCompletion).

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix: remove deleted test-utils from build order

The test-utils package was merged into core but the build script still
tried to build it separately, causing CI failures.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-13 17:11:03 +08:00
pomelo
54dd9252e0
chore: remove legacy directories (.gcp, .aoneci, hello, .allstar) (#3199)
Remove directories and files that are no longer needed for Qwen Code:

- .gcp/: Google Cloud Platform build configs (legacy from Gemini CLI)
- .aoneci/: Alibaba AoneCI workflow (replaced by GitHub Actions)
- hello/: Example extension template (not needed in repo root)
- .allstar/: Google Allstar security policy config (Google legacy artifact)

These artifacts are either obsolete or superseded by
GitHub Actions workflows and the current Dockerfile.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-13 17:09:45 +08:00
Shaojin Wen
fd6c846979
fix(cli): recover from stuck bracketed-paste mode and keep Ctrl+C reachable (#3181)
* fix(cli): recover from stuck bracketed-paste mode and keep Ctrl+C reachable

If bracketed-paste-start (`ESC[200~`) arrives but paste-end (`ESC[201~`)
is lost — for example on Ghostty + Sogou pinyin on macOS, which a user
reported in a working directory with only three files — `isPaste` stays
`true` forever. The order of checks inside `handleKeypress`:

  1. `key.name === 'paste-start'`  → set isPaste = true, return
  2. `key.name === 'paste-end'`    → reset + flush + return
  3. `if (isPaste) { pasteBuffer.append; return }`
  4. backslash / return handling
  5. arrow keys
  6. Ctrl+C

means that every subsequent key — **including Ctrl+C** — is appended to
the paste buffer and silently returned. The user has no keyboard escape
hatch; they must kill the process / restart the terminal.

Two layered fixes:

1. **Ctrl+C escape hatch.** Move the Ctrl+C check above the `isPaste`
   branch and clear paste state when it fires. Ctrl+C now always reaches
   the broadcast regardless of any stuck paste state.

2. **Idle timeout auto-recovery.** Add `PASTE_IDLE_TIMEOUT_MS = 1000`.
   Start the timer on paste-start, reset it on each paste content byte,
   clear it on paste-end. If the timer fires, force-flush the paste
   buffer as a regular paste event and reset `isPaste`. A
   `pasteAlreadyFlushed` flag guards against a stale paste-end event
   arriving later and broadcasting a spurious empty/image paste.

Two regression tests cover both paths:
- `Ctrl+C escapes a paste mode that never received its paste-end marker`
- `auto-recovers from a stuck paste mode via idle timeout`

Both fail on main and pass with this change. Full cli test suite:
3968 pass / 7 skipped, no regressions.

* test(cli): derive paste idle-timeout wait from PASTE_IDLE_TIMEOUT_MS

Address review feedback: the auto-recovery regression test hard-coded a
1500ms sleep instead of referencing the production constant. Import
PASTE_IDLE_TIMEOUT_MS and derive the wait as `constant + 200ms buffer`
so the test stays in sync if the production timeout is ever tuned.

* docs(cli): clarify paste state-machine comments from review feedback

Address review bot nits:
- forceFlushStuckPaste: explain why the empty-guard is asymmetric
  (isPaste/buffer can be out of sync after a Ctrl+C vs idle-timeout race)
- paste-end handler: note that pasteAlreadyFlushed=false is the reset
  for the next paste cycle
- auto-recovers test: frame it as the "automatic recovery safety net"
  counterpart to the manual Ctrl+C escape test above

Comments only, no behavioural change.
2026-04-13 17:02:50 +08:00
pomelo
53a9c0a28a
docs(readme): add announcement for Qwen OAuth free tier policy adjustment (#3207)
Add news item about:
- Daily quota reduced from 1,000 to 100 requests/day
- Free tier discontinuation on 2026-04-15
- Migration path to Alibaba Cloud Coding Plan

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-13 16:54:35 +08:00
Shaojin Wen
6e29505b08
feat(core): intelligent tool parallelism with Kind-based batching and shell read-only detection (#2864)
* feat(core): implement intelligent tool parallelism via Kind-based batching

Replace the hard-coded Agent-vs-others split with consecutive batching
based on tool Kind. Read-only tools (Read, Search, Fetch, Think) now
execute in parallel; mutating tools (Edit, Execute) run sequentially.

- Add CONCURRENCY_SAFE_KINDS set to tools.ts
- Add partitionToolCalls() for consecutive batch grouping
- Add isConcurrencySafe() helper (Agent name + Kind check)
- Add runConcurrently() with configurable concurrency cap
  (QWEN_CODE_MAX_TOOL_CONCURRENCY env var, default 10)
- Update MockTool to support custom Kind for testing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(core): add conditional concurrency for shell read-only commands

Shell commands detected as read-only (e.g., git log, cat, ls) now run
concurrently with other safe tools instead of breaking parallel batches.

Uses the existing isShellCommandReadOnly() checker (synchronous,
fail-closed). Commands that can't be verified as read-only remain
sequential.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Copilot review on tool parallelism

- Remove Kind.Think from CONCURRENCY_SAFE_KINDS (save_memory and
  todo_write write to disk)
- Use .finally() instead of .then() in runConcurrently for cleanup
- Validate maxConcurrency (clamp to >= 1, default 10)
- Add comment explaining why sync checker is used over async AST
- Add test for mixed safe/unsafe tool batch partitioning

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update comment to match CONCURRENCY_SAFE_KINDS (remove Think)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove abort break in runConcurrently to prevent stuck scheduled calls

Let all calls go through executeSingleToolCall which handles abort
internally, ensuring every tool reaches a terminal state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: isolate concurrency tests from QWEN_CODE_MAX_TOOL_CONCURRENCY env

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Copilot review - comment, test label, shell test

- Update batching comment to clarify Execute conditional safety
- Rename describe block to "Concurrent tool execution"
- Add test for shell read-only concurrency (git log + ls parallel,
  npm install sequential)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add indexOf !== -1 guards to concurrency test assertions

Prevents false-positive test passes when expected log entries are
missing (indexOf returns -1 which is always < any positive index).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:06:55 +08:00
DennisYu07
dddb56d885
feat: add stopFailure and postCompact (#2825) 2026-04-13 12:54:44 +08:00
jinye
732cee2604
feat(cli): warn when workspace overrides global modelProviders (#3148)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* feat(cli): warn when workspace overrides user modelProviders

* fix(cli): align modelProviders warning with current merge behavior

---------

Co-authored-by: jinye.djy <jinye.djy@alibaba-inc.com>
2026-04-13 10:43:16 +08:00
jinye
1557d93043
feat(cli): support tools.sandboxImage in settings (#3146)
Co-authored-by: jinye.djy <jinye.djy@alibaba-inc.com>
2026-04-13 09:43:34 +08:00
feyclaw
116796b2a4
feat(channels): add voice message support in TelegramAdapter (#3150)
Add handler for message:voice in Telegram channel adapter.
Voice messages are downloaded via Telegram Bot API and passed as
audio attachments in the Envelope, matching the existing pattern
for document handling.

This enables users to send voice messages through Telegram which
can then be processed by the model or transcription skills.

Co-authored-by: Teafey <Teafey@users.noreply.github.com>
2026-04-13 09:39:40 +08:00
Shaojin Wen
7614c8c58e
fix(followup): fix follow-up suggestions not working on OpenAI-compatible providers (#3151)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* fix(followup): fix follow-up suggestions not working on OpenAI-compatible providers

- Respect request.model in pipeline so fastModel setting takes effect
- Skip empty tools array to avoid 400 errors from providers
- Override enable_thinking/reasoning from extra_body when thinking is
  explicitly disabled for suggestion generation
- Filter thought parts from response text in both forkedQuery and
  baseLlm paths to prevent thinking content leaking into suggestions
- Add debug logging (tag: FOLLOWUP) for suggestion generation diagnostics

* fix(followup): validate fastModel belongs to current authType

When the configured fastModel is from a different auth type/provider
than the main model, the API call silently fails because the current
content generator rejects unknown model IDs. Fall back to the main
model in that case so suggestion generation stays functional.

Reported by @yiliang114 in #3151.
2026-04-12 16:07:00 +08:00
John London
fea61e1788
fix(core): handle empty OAuth refresh response body (#3123)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* fix(core): handle empty OAuth refresh response body

When Qwen's OAuth server returns 200 with an empty body (e.g., stale
refresh token), response.json() throws 'Expecting value: line 1 column 1
(char 0)' instead of a usable error message. This forces users to
re-authenticate with no indication of what went wrong.

Fix: read response.text() first, then JSON.parse with a try/catch that
clears credentials and provides a clear error message.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): address review feedback on OAuth refresh error handling

- Don't clear credentials on malformed 200 responses (treat as retryable)
- Clear credentials on explicit 400/401 auth-invalid responses
- Add text() to all refresh-path test mocks
- Add regression tests for malformed 200 and 401 responses

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-12 11:08:23 +08:00
易良
19f2d292f9
fix(core): fall back to CLI confirmation when IDE diff open fails (#3031)
* refactor: centralize IDE diff interaction in CoreToolScheduler

- Move openDiff/confirmation handling from edit.ts and write-file.ts into
  CoreToolScheduler.openIdeDiffIfEnabled(), called after permission hooks
- Use structuredClone in buildInvocation to prevent params mutation leaking
  to LLM history (fixes #2709 token waste)
- Use confirmationDetails as single data source for IDE diff content,
  only rely on ModifyContext.createUpdatedParams() for parameter transform
- Skip inline modify when IDE content unchanged, preserving original tool
  params for multi-edit-on-same-file scenarios (mitigates #2702)
- Remove ideConfirmation field from ToolEditConfirmationDetails
- Remove dead resolveIdeDiffForOutcome from ACP Session.ts
- Fix memory tool scope fallback in createUpdatedParams

Closes #2709
Closes #2673

* fix(core): fall back to CLI confirmation when IDE diff open fails

* fix(core): narrow IDE diff error handling scope

---------

Co-authored-by: 胡玮文 <huweiwen.hww@alibaba-inc.com>
Co-authored-by: tanzhenxin <tanzhenxing1987@gmail.com>
2026-04-12 10:39:56 +08:00
tanzhenxin
56392c7397
fix: lazy-load channel plugins to eliminate DEP0040 startup warning (#3134)
Some checks are pending
Qwen Code CI / Lint (push) Waiting to run
Qwen Code CI / Test (push) Blocked by required conditions
Qwen Code CI / Test-1 (push) Blocked by required conditions
Qwen Code CI / Test-2 (push) Blocked by required conditions
Qwen Code CI / Test-3 (push) Blocked by required conditions
Qwen Code CI / Test-4 (push) Blocked by required conditions
Qwen Code CI / Test-5 (push) Blocked by required conditions
Qwen Code CI / Test-6 (push) Blocked by required conditions
Qwen Code CI / Test-7 (push) Blocked by required conditions
Qwen Code CI / Test-8 (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* fix: lazy-load channel plugins to eliminate DEP0040 startup warning

Channel plugins (telegram/weixin/dingtalk) were eagerly imported at
module load time via channel-registry.ts, which transitively loaded
grammy → node-fetch@2 → whatwg-url@5 → require("punycode"), triggering
the DEP0040 deprecation warning on every CLI invocation (Node 22+).

Switch to dynamic import() so plugins are only loaded when a user
actually runs `qwen channel` subcommands.

* fix(channels): use cached promise in ensureBuiltins to prevent race condition

Replace boolean flag with a cached promise so concurrent callers
properly await the same initialization instead of seeing an empty
registry.
2026-04-11 17:04:43 +08:00
tanzhenxin
7219469285
fix(channels): apply proxy settings to channel start command (#3136)
`qwen channel start` never calls `loadCliConfig`, so the proxy
configured via `--proxy` or `HTTPS_PROXY`/`HTTP_PROXY` env vars
was not applied. This caused Telegram's `getMe` (and all other
channel HTTP traffic) to bypass the proxy entirely.

The fix has two parts:

1. Resolve proxy in `start.ts` bootstrap and call
   `setGlobalDispatcher(new ProxyAgent(...))` for native fetch()
   calls (file downloads, other channels). This mirrors the same
   pattern used by Config constructor in the main CLI path.

2. Thread the proxy URL through `ChannelBaseOptions` so adapters
   can configure their own HTTP clients. TelegramAdapter passes
   an `HttpsProxyAgent` to grammy's `baseFetchConfig.agent` since
   grammy uses node-fetch which ignores undici's global dispatcher.

Fixes #3122
2026-04-11 16:44:14 +08:00
tanzhenxin
e216ab35fc
fix(core): cap recursive file crawler at 100k entries to prevent OOM (#3138)
When the @ autocomplete triggers RecursiveFileSearch, the crawler
materialises the entire project tree into memory with no upper bound.
For very large workspaces (missing .gitignore, huge node_modules, home
directory as cwd) this pushes Node.js past its heap limit and crashes.

- Add `maxFiles` option to CrawlOptions; use fdir's withMaxFiles() to
  stop traversal early instead of post-hoc truncation
- Apply file-level ignore patterns during crawl via fdir filter() so
  ignored files don't consume the maxFiles budget
- Include maxFiles in the crawl cache key for correctness
- Set MAX_CRAWL_FILES = 100 000 in RecursiveFileSearch (caps peak
  memory at ~50 MB for the file list)

Fixes #3130
2026-04-11 16:44:02 +08:00
Shaojin Wen
61ad9db9c1
feat(cli): queue input editing — pop queued messages for editing via ↑/ESC (#2871)
* feat(cli): add queue input editing via Up arrow key

Allow users to edit queued messages by pressing the Up arrow key when
the cursor is at the top of the input. All queued messages are popped
into the input field for revision before resubmission, reducing wasted
turns from incorrect queued instructions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add missing mocks for InputPrompt tests and attachment mode guard

- Add popAllQueuedMessages mock and messageQueue to UIState/UIActions
  mocks in InputPrompt.test.tsx to fix 25 test failures
- Add !isAttachmentMode guard to prevent queue pop from conflicting
  with attachment navigation
- Add single-message popAllMessages test case

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address Copilot review - restrict to Up arrow, add tests, update docs

- Only trigger queue pop on NAVIGATION_UP (arrow key), not HISTORY_UP
  (Ctrl+P), preserving existing Ctrl+P history navigation behavior
- Update AsyncMessageQueue class docs to describe popLast() LIFO semantics
- Add InputPrompt tests: Up arrow pops queue, Up arrow falls back to
  history when queue empty, Ctrl+P not intercepted by queue pop

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update fileoverview docs and make popAllMessages atomic via ref

- Update @fileoverview to describe FIFO+LIFO capability instead of
  "Simple FIFO queue"
- Use queueRef to make popAllMessages atomic, preventing duplicate
  pops from key auto-repeat before React re-renders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: sync queueRef in addMessage/clearQueue and fall through on null pop

- Update queueRef inside addMessage setter and clearQueue to keep ref
  in sync between renders, preventing stale reads after clearQueue
- When popAllQueuedMessages returns null (queue already cleared), fall
  through to normal history navigation instead of consuming the key

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove dead popLast() and align popAllMessages separator to \n\n

- Remove unused AsyncMessageQueue.popLast() (no production callers)
- Change popAllMessages join separator from \n to \n\n for consistency
  with getQueuedMessagesText and auto-submit behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use hook's drainQueue for mid-turn drain to prevent double-consumption race

The midTurnDrainRef previously used a separate messageQueueRef (synced
from React state), while popAllMessages uses the hook's internal
queueRef. If a tool completed between popAllMessages clearing queueRef
and React re-rendering, midTurnDrainRef would read stale data and
consume the same messages a second time.

Switching to the hook's drainQueue makes both paths read from the same
synchronous ref, eliminating the window for double consumption.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add missing popAllMessages mock and prepend branch test

Add popAllMessages to useMessageQueue mock in AppContainer tests.
Add test for prepending queued messages before existing input text.

* feat: add ESC trigger, cursor preservation, and progressive hint

- ESC pops queued messages before double-ESC clear logic
- Cursor stays at user's editing position after pop via moveToOffset
- Extract popQueueIntoInput helper to share logic between Up and ESC
- QueuedMessageDisplay hint hides after 3 empty→non-empty transitions

* test: add null-pop fallthrough test for queue race condition

Verify that when React state shows non-empty queue but the ref is
already drained (popAllQueuedMessages returns null), Up arrow falls
through to normal history navigation instead of getting stuck.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 14:38:32 +08:00