* 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>
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>
* 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>
* 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>
* 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>
* 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
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
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
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
* 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.
* 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
* 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>
- 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>
* 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>
* 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.
* 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.
* 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.
`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
* 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>
* fix(input): preserve tab characters in pasted content
Tab-separated data pasted from spreadsheets (e.g. Excel) was silently
lost through three interception layers: stripUnsafeCharacters filtered
tab as a C0 control char, TextInput consumed tab for autocomplete, and
InputPrompt consumed tab for suggestion acceptance.
- Add tab (0x09) to the preserve list in stripUnsafeCharacters
- Skip tab→autocomplete interception when key.paste is true
- Skip tab→suggestion-accept in InputPrompt when key.paste is true
* test: skip flaky AskUserQuestionDialog test on Windows
The "shows unanswered questions as (not answered) in Submit tab" test
fails intermittently on Windows CI due to arrow key navigation timing
issues in the ink test renderer.
The `|| key.name === 'return'` fallback in TextInput matched every Return
keypress (Shift+Enter, Ctrl+Enter, etc.) and routed them all to the submit
path, making the NEWLINE handler dead code. Multiline inputs like the agent
creation description step could not insert newlines via keyboard.
Reorder checks so NEWLINE is evaluated first in multiline mode, and restrict
the broad return fallback to single-line inputs only.
* fix: prevent statusline script from corrupting settings.json
Some models generate shell commands with complex quoting (e.g. single-quote
escaping like '\'') that break JSON syntax when written to settings.json,
causing qwen-code to fail to start with a FatalConfigError.
This adds four layers of defense:
1. **Agent prompt** (builtin-agents.ts): Require commands using jq/pipes/quotes
to be saved as script files instead of inline in settings.json. Mark examples
as script-only to prevent models from copying them inline.
2. **Write validation** (commentJson.ts): Validate JSON output before writing
to disk in updateSettingsFilePreservingFormat.
3. **Startup recovery** (settings.ts): When settings.json has invalid JSON,
try .orig backup first, then degrade gracefully to empty settings instead
of crashing. Rename corrupted file to .corrupted for manual recovery.
Show warning to user via migrationWarnings.
4. **Test update** (settings.test.ts): Update test to verify graceful
degradation behavior instead of expecting FatalConfigError.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review comments on statusline JSON corruption fix
1. Backup recovery now surfaces warning via migrationWarnings (reviewer: P2 correctness)
2. Corrupted file uses timestamped suffix to avoid overwriting (reviewer: P2 robustness)
3. Remove misleading underscore prefix on used catch variable (reviewer: P2 code quality)
4. updateSettingsFilePreservingFormat returns boolean (reviewer: P2 correctness)
5. Add 3 new tests: backup recovery, both-corrupted, rename-failure (reviewer: P2 testing)
6. Consistent shebang lines in agent prompt examples (reviewer: P3 nit)
7. Improve catch block error message for backup recovery (reviewer: P2 correctness)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: warningMsg says "renamed" even when rename fails
Move warningMsg construction after renameSync so the message accurately
reflects the outcome: "renamed to X" on success, "fix manually" on failure.
Add assertion to rename-failure test verifying the fallback message.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): improve markdown table rendering in terminal
* fix(cli): restore theme colors and inline markdown rendering in tables
Improvements over previous commit:
- Restore theme.border.default color for table borders
- Restore theme.text.link color + bold for table headers
- Add renderMarkdownToAnsi() to render **bold**, `code`, *italic*,
~~strikethrough~~, <u>underline</u>, [links](url), and bare URLs
as ANSI-styled text in table cells (mirrors RenderInline behavior)
- Use raw ANSI escape codes instead of chalk (chalk.level=0 in tests)
- Remove dead code: INLINE_MARKDOWN_REGEX, hasInlineMarkdown,
ANSI_BOLD_START/END constants, unused vi/beforeEach in tests
- Update 8 snapshots to reflect themed output
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): address Copilot review comments on table rendering
- renderRowLines: normalize cells to exactly colCount (pad/truncate)
to prevent undefined access when row has fewer cells than headers
- calculateMaxRowLines: iterate colCount instead of row.length to
prevent undefined columnWidths access for extra cells
- tableSeparatorRegex: add (?=.*\|) lookahead to require at least one
pipe character, preventing `---` (horizontal rule) from being
mis-parsed as a table separator
- Add test: horizontal rule after pipe line is not a table separator
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): address Copilot round-2 review on table rendering
- idealWidths: use getRenderedWidth() (markdown→ANSI→stripAnsi→stringWidth)
instead of getPlainTextLength() so link URLs are accounted for in
column width calculation
- calculateMaxRowLines: use getFormattedCellText() (same as renderRowLines)
so vertical fallback decision matches actual rendered row height
- renderVerticalFormat: normalize row to colCount (pad/truncate) for
consistency with horizontal format
- renderVerticalFormat: render markdown in labels via renderMarkdownToAnsi()
instead of showing raw syntax
- Remove unused getCellPlainText helper and getPlainTextLength import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): address Copilot round-3 review on table rendering
- Early return empty <Box /> when headers is empty (colCount === 0)
to prevent malformed border output
- Always apply theme.text.link color to header cells regardless of
ANSI content, matching original Ink implementation behavior
- Validate separator column count matches header column count before
entering table mode, preventing mismatched separators like
`| A | B |` followed by `|---|` from creating invalid tables
- Add test for column count mismatch detection
- Update 2 snapshots for consistent header link color
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): address Copilot round-4 review on table rendering
- getMinWordWidth: use renderMarkdownToAnsi output so link URLs are
included as unbreakable tokens in minimum column width calculation
- Remove now-unused stripInlineMarkdown function
- Header alignment: respect explicit alignment markers from separator;
only default to center when no alignment is specified for the column
- Header color nesting: re-apply theme.text.link color after inner
foreground resets (from inline code/links) to match Ink's nested
color behavior where parent color is restored after child resets
- Add getColorCode() helper for extracting raw ANSI color escape
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): address Copilot round-5 review on table rendering
- Apply theme.text.primary color to non-header cells and re-apply
after inner foreground resets, matching header recolor behavior
- Use nullish coalescing (??) for vertical format labels so empty
header strings are preserved instead of replaced with Column N
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): re-apply cell color after full ANSI reset (\x1b[0m)
Add recolorAfterResets() helper that handles both \x1b[39m (foreground
reset) and \x1b[0m (full SGR reset). Applies to both header and body
cells so mixed ANSI content keeps consistent theme coloring.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): apply recolorAfterResets to vertical format labels
Vertical fallback labels with inline markdown (code, URLs) now
re-apply link color after SGR resets, consistent with horizontal
header/body cell behavior.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): apply primary color to vertical format values
Vertical fallback values now get theme.text.primary color with
recolorAfterResets, consistent with horizontal body cell styling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): preserve internal blank lines in wrapped cell content
wrapText now only trims trailing empty lines (wrap-ansi artifacts)
instead of filtering all empty lines, preserving intentional blank
lines within multi-paragraph cell content.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): validate hex colors and deduplicate applyColor/getColorCode
- Add HEX_COLOR_RE validation; invalid hex like #ff00 or #gg0000
now returns unchanged text instead of producing NaN in ANSI escapes
- Refactor applyColor to delegate to getColorCode, eliminating
duplicated hex parsing logic
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): precompute cell metrics and fix column width overflow
- Precompute per-cell rendered text, visible width, and min word width
once via computeMetrics(), eliminating repeated renderMarkdownToAnsi
calls across width calculation, max-row-lines check, and rendering
- Add post-pass in totalMin > availableWidth branch: shave wider
columns until sum(columnWidths) <= availableWidth, preventing
MIN_COLUMN_WIDTH floor from causing unnecessary vertical fallback
- Remove now-unused getMinWordWidth standalone function
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: show description for active setting in /settings dialog
Display the schema description of the currently highlighted setting
below the settings list, so users can understand what each option does
without needing to check external documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: update SettingsDialog snapshots for description display
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Fast Model setting in the settings dialog previously used a plain text
input, making it hard for users to discover available models. This replaces
it with the same model picker dialog used by `/model --fast`, adds a `▸`
visual indicator for sub-dialog settings, and supports right arrow to open
and left arrow to return.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add "(--fast for suggestion model)" to the /model command description
so users can discover the feature from the command list, since --fast
completion no longer appears on empty input.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace vague "background tasks" with specific "prompt suggestions and speculative
execution" in the --fast flag description across all i18n locales, docs, and VS Code
schema. Update example model name from qwen3.5-flash to qwen3-coder-flash. Also fix
completion logic to require a non-empty partial arg before suggesting --fast, preventing
Tab+Enter from accidentally entering fast model mode.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The "Compact Mode" label is more intuitive than "Verbose Mode" for users,
as it directly describes the default compact view experience. This change
inverts the boolean semantics (compactMode=false means show full output)
and exposes the setting in the /settings dialog (showInDialog: true).
- Rename ui.verboseMode → ui.compactMode with inverted default (false)
- Rename VerboseModeContext → CompactModeContext (file and exports)
- Rename TOGGLE_VERBOSE_MODE → TOGGLE_COMPACT_MODE in key bindings
- Update all consumer components with inverted logic
- Update i18n keys across 6 locales (verbose → compact)
- Update VS Code settings schema
- Add ui.compactMode documentation to settings.md
- Fix Ctrl+O description in keyboard-shortcuts.md
PR #2943 fixed headers in buildHeaders() but the login flow in
waitForLogin() still used a hardcoded incomplete header object.
Reuse the shared buildHeaders() so all endpoints send consistent
iLink-App-Id and iLink-App-ClientVersion headers.
Also wrap channel.connect() in startSingle() with a try/catch so
configuration errors print a clean message instead of dumping the
yargs help text and a stack trace.
Add !key.shift guard to the Tab key handler for prompt suggestion
acceptance, so Shift+Tab only toggles approval mode without inserting
the placeholder text into the input.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover config validation, command execution with exec options, stdin JSON
payload, stale generation rejection, debouncing, config removal, cleanup
on unmount, EPIPE handling, command hot-reload, all state change triggers
(token count, model, branch, vim toggle, file lines), and process management.
The previous commit reverted the code to use the original i18n key
but the locale files still had the renamed key, causing translations
to not resolve.
* feat(plan): add "Yes, restore previous mode" option when exiting plan mode
When exiting plan mode, users previously had no way to restore their
original approval mode (e.g. YOLO). Add a new default option that
restores the pre-plan approval mode, with a dynamic label showing
which mode will be restored.
Closes#3002
* test: add fallback test for RestorePrevious when no prePlanMode recorded
* fix: handle RestorePrevious in telemetry and ACP mode notification
- Add RestorePrevious to telemetry decision mapping as ACCEPT
- Fix sendCurrentModeUpdateNotification to read actual mode for
RestorePrevious instead of defaulting to 'default'
* test: add plan confirmation tests for RestorePrevious in permissionUtils
* fix: serialize subagent confirmation focus to prevent concurrent input conflicts
When multiple subagents run in parallel and each triggers a confirmation
prompt, all prompts previously received keyboard focus simultaneously,
causing a single keypress to be dispatched to every active confirmation.
This change introduces a first-come-first-served focus lock mechanism:
- Track subagents with pending confirmations via a type guard
- Use a useRef-based lock so only one confirmation is focused at a time
- Automatically promote focus to the next pending subagent on resolution
- Show a waiting indicator on non-focused confirmations
Fixes#2929
* fix(cli): use dedicated prop for subagent approval waiting state
---------
Co-authored-by: 思晗 <housihan.hsh@alibaba-inc.com>
- 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#2709Closes#2673
* test: add tests for confirmation-bus, prompt-registry, and cli/core modules
Add 42 new tests covering previously untested core modules:
- MessageBus: publish, subscribe/unsubscribe, request-response pattern (13 tests)
- PromptRegistry: register, dedup, query by server, clear, remove (11 tests)
- performInitialAuth: success, failure, no authType cases (3 tests)
- validateTheme: found, not found, no config cases (4 tests)
- initializeApp: i18n, auth, theme, IDE mode, auth dialog logic (11 tests)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: improve test quality - copyright headers, env safety, cleanup
- Fix copyright headers from Google LLC to Qwen Code in all 5 test files
- Use vi.stubEnv() instead of manual process.env mutation in initializer test
- Add removeAllListeners() cleanup in message-bus debug test
- Add void prefix to un-awaited publish() calls in message-bus test
- Verify invoke reference preserved after prompt rename in prompt-registry test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(message-bus): add AbortSignal coverage for request()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The /context command was missing the subcommand autocomplete feature
that other commands like /stats have. Now users can type '/context '
and see 'detail' as a suggestion in the dropdown.
- Added 'detail' subCommand to contextCommand with its own description
- Subcommand delegates to main action with 'detail' arg
- Added missing translation key for full description in zh.js
- Updated commands.md documentation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>