- Changed default channel from 'Desktop' to 'ACP' in AcpProcessClient and related interfaces.
- Updated tests to reflect new channel naming and added support for CLI channel overrides.
- Enhanced error handling in SessionSocketHub to normalize error responses for better clarity.
- Modified session creation and loading responses to include 'cwd' in the session object.
- Added a new test case to surface ACP prompt errors as structured protocol objects.
* feat(vscode): expose /skills as slash command with secondary picker
Add a secondary completion picker for the /skills slash command in the
VSCode IDE companion, allowing users to browse and select skills from
a dropdown before sending.
Changes:
- CLI: add 'skills' to ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE whitelist
- CLI: send available_skills_update via ACP with skill names/descriptions
- Extension: handle available_skills_update in session update handler
- Webview: implement secondary picker that triggers after selecting /skills
- Webview: allow spaces in completion trigger for /skills sub-queries
Closes#1562
Made-with: Cursor
* feat(vscode-ide-companion): embed skills in commands update metadata
- Move available skills from separate session update to _meta field of
available_commands_update for more efficient delivery
- Simplify skill data to just skill names (string array)
- Add skillsCompletion utility for secondary picker logic
- Cache available skills in WebViewProvider for replay on webview ready
- Update all related types and handlers to support the new structure
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* refactor(vscode-ide-companion): simplify skills picker flow
* refactor(vscode-ide-companion): extract skills completion utils to shared module
Move `isSkillsSecondaryQuery`, `shouldOpenSkillsSecondaryPicker`, and
`SKILL_ITEM_ID_PREFIX` from App.tsx and useCompletionTrigger.ts into a
shared `completionUtils.ts` file to eliminate duplication.
* fix(vscode-ide-companion): restore skills picker state on reload
Cache and replay available skills when the webview becomes ready again.
Clear stale skills when commands metadata does not include availableSkills.
* fix(vscode-ide-companion): replay slash commands after webview reload
Cache available commands in the webview provider.
Replay them on webviewReady so slash command state survives reloads.
* fix(vscode-ide-companion): import AvailableCommand from ACP SDK
* fix(vscode-ide-companion): fallback /skills to direct command
* test(vscode-ide-companion): cover skills secondary picker flow
* test(vscode-ide-companion): guard App mock initialization
* fix(vscode-ide-companion): remove duplicate AvailableCommand import
The auto-merge introduced a duplicate AvailableCommand in the
@agentclientprotocol/sdk import block, causing TS2300.
* fix(vscode-ide-companion): remove duplicate availableCommands replay in handleWebviewReady
The handleWebviewReady method was sending cachedAvailableCommands twice
on every webview-ready handshake, causing an unnecessary extra state
update in the webview.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(cli): add Traditional Chinese (zh-TW) as a UI language option
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix: use upstream unused-keys-only-in-locales.json to resolve conflict
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* revert: remove check-i18n.ts changes to avoid pre-existing zh.js issues
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(cli): add Traditional Chinese (zh-TW) as a UI language option
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): add WITTY_LOADING_PHRASES to zh-TW locale
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): sync zh-TW.js with en.js keys, fix double-escape, fix check-i18n.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix: resolve conflict in unused-keys-only-in-locales.json
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): add missing Performance translation to zh-TW
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): add quotes to Performance key in zh-TW
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): regenerate zh-TW.js with correct multi-line value parsing
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix: resolve conflict in unused-keys-only-in-locales.json
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): regenerate zh-TW.js with correct multi-line value parsing
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): standardize zh-TW.js key quoting and sync zh.js keys
- Convert zh-TW.js keys from double-quoted to single-quoted to match en.js style
- Fix zh.js key mismatches: add missing keys (Value:, No server selected, prompts, required, Enum) and remove extra keys (The name of the extension to update, Session (temporary))
- Regenerate unused-keys-only-in-locales.json
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): update loading phrases when UI language changes
Add getCurrentLanguage() to useMemo deps in usePhraseCycler so that
WITTY_LOADING_PHRASES re-evaluates after a /language switch instead of
staying locked to the language active at mount time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(i18n): normalize locale separators and fix case-insensitive language lookup
- detectSystemLanguage(): normalize POSIX locales (e.g. zh_TW.UTF-8 → zh-tw)
by replacing underscores with hyphens and lowercasing before matching, so
users with LANG=zh_TW.UTF-8 correctly detect zh-TW instead of falling
through to zh
- getLanguageNameFromLocale(): compare codes case-insensitively so that
normalizeOutputLanguage('zh-TW') resolves to 'Traditional Chinese' instead
of falling back to 'English'
- Add test cases for zh-TW / zh-tw / ZH-TW in normalizeOutputLanguage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(test): update getLanguageNameFromLocale mock to include zh-TW
Add 'zh-tw' entry to the mock map and normalize locale input with
toLowerCase() so the mock mirrors the real case-insensitive implementation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* perf(core): make chat recording writes async
Every recorded chat event (user message, assistant turn, tool call,
tool result, slash command, etc.) was issuing 4 sync fs syscalls on
the main event loop: existsSync(dir) + mkdirSync(dir) + existsSync(file)
+ appendFileSync(file). For a tool-heavy prompt this added ~88 sync
I/O calls per session, blocking the UI render and keypress handler
during each one.
- chatRecordingService.appendRecord: cache ensure-flags so dir/file
creation runs once per session, then enqueue the actual write on a
per-instance promise chain (writeChain). lastRecordUuid is updated
synchronously so chained createBaseRecord still sees the right
parentUuid without waiting for the previous write.
- chatRecordingService.flush: drains the chain — wired into
Config.shutdown so no records are lost on exit.
- jsonl-utils.writeLine: now actually async (fs.promises.mkdir +
fs.promises.appendFile) with per-dir mkdir cache. The existing
per-file mutex still serializes writes correctly.
- Tests updated to await flush() before assertions.
Trace measurement on a single tool-heavy prompt: 110 → 20 sync I/O
calls (-82%), with chatRecordingService dropping from 88 to 0.
* perf(core): cache repeated fs lookups on tool hot path
Each tool invocation went through validatePath → isPathWithinWorkspace
→ fullyResolvedPath, plus its own existence/dir checks. The same paths
got re-resolved across back-to-back tool calls, and ripGrep re-
discovered .qwenignore on every Grep.
- workspaceContext.fullyResolvedPath: bounded LRU on input path
(1024, FIFO). Failed resolutions are NOT cached so retries work.
- paths.validatePath: cache positive isDirectory results; ENOENT
falls through every time so a freshly created file is picked up
immediately.
- ripGrep: module-level caches for searchPath-is-dir and per-dir
.qwenignore presence (256 each, FIFO).
- fileUtils.processSingleFileContent: drop the existsSync gate;
let fs.promises.stat throw ENOENT and convert to FILE_NOT_FOUND
in catch.
Trace: 20 → 10 sync I/O calls. Cumulative reduction since the
chat-recording change: 110 → 10, -91%. All 6057 core tests pass.
* test(core): cache reset hooks + regression-guards from audit
Self-review pass on the previous two perf commits surfaced a few
follow-ups worth pinning down before they bite:
- Module-level caches (paths.isDirectoryCache, ripGrep dirIsDir/qwen-
Ignore, jsonl-utils.ensuredDirs) persisted across vitest cases
silently. Added underscore-prefixed `_reset*ForTest` exports and
wired one into the validatePath describe block so future cases
mutating the same absolute paths can't pass by accident.
- Documented the parentUuid-chain tradeoff on chatRecordingService
.appendRecord: when the async write rejects, lastRecordUuid was
already set sync, so subsequent records reference an absent
ancestor — readers like sessionService.reconstructHistory then
silently drop those descendants. Same observable failure mode as
the prior sync code's caught-and-logged throw.
- Documented the dir<->file mutation and mid-session .qwenignore
staleness windows for the validatePath / ripGrep caches.
- Added regression tests:
* validatePath does NOT cache ENOENT (Edit-then-Read works)
* validatePath skips re-stat on cache hit (perf assertion)
* flush() resolves immediately on a fresh service
* a rejected writeLine does not block the next record
Full core suite: 6061 pass, 2 skipped — no regressions.
* fix(core): cache chatsDirEnsured only on mkdir success
Pre-fix, the flag flipped to true even when mkdirSync threw, so a single
transient failure (NFS EACCES, sandbox mount race, parent dir briefly
missing) would short-circuit every subsequent appendRecord and silently
drop the rest of the session's transcript with no error surfaced.
Reported by zhangxy-zju on #3581.
* fix(cli): destroy stdout instead of process.exit on EPIPE
Routine CLI patterns like `qwen -p ... | head -1` / `| less` / `| grep -m1`
close the downstream pipe and trigger EPIPE. The previous handler called
process.exit(0), which bypassed the caller's runExitCleanup -> Config
.shutdown -> chat-recording flush() chain and silently dropped queued
JSONL writes (most recent assistant turn + tool results).
Destroying stdout instead lets writes fail fast and the natural function
return drive cleanup. We deliberately do not also abortController.abort()
here: the abort path runs handleCancellationError which itself calls
process.exit(130), re-introducing the same bypass.
Reported by zhangxy-zju on #3581.
* fix(cli): bound runExitCleanup with per-fn + wall-clock timeouts
Pre-fix, runExitCleanup was an unbounded series of awaits. After the
async-jsonl change moved chat-recording writes off the calling thread
(Config.shutdown now `await flush()`s the queue), any hung syscall
(slow disk, dead NFS mount, stuck MCP socket, telemetry HTTP stall)
would hang process exit indefinitely — sync writes were inherently
bounded by syscall return; async writes are not.
Adds per-cleanup 2s + overall 5s wall-clock failsafes on the same
shape as Claude Code's gracefulShutdown.ts. Also replaces dead
test-isolation code (`global['cleanupFunctions']` was never on global,
the array is module-private) with a `_resetCleanupFunctionsForTest`
hook matching the convention from d6485964c.
Follow-up flagged by zhangxy-zju on #3581.
---------
Co-authored-by: wenshao <wenshao@U-K7F6PQY3-2157.local>
* feat(vscode): add native context menu copy actions for webview chat
Add three right-click context menu items to the chat message area using
VSCode's native webview/context API:
- Copy Message: copies the right-clicked message's raw markdown content
- Copy All Messages: copies the full conversation in markdown format
- Copy Last Reply: copies the last assistant response
Implementation details:
- Commands registered in package.json with webview/context menu entries
- Clipboard writes go through extension host (vscode.env.clipboard) for
reliability in webview sandbox
- Message identification via data-msg-idx stamped after render
- Tool-call outputs supported including diff format (git diff style)
- i18n support via package.nls.json (English) and package.nls.zh-cn.json
- Menu only shown in message area (not input box or empty state)
Closes#3052
* fix(vscode): wrap tool-call content text in code blocks for copy
* fix(vscode): only wrap tool-call content in code blocks for Copy All, not single Copy Message
* fix(vscode): route copy commands to the right-clicked webview and use dynamic code fences
* fix(vscode): use childIndexMap for copy-message routing and extract shared message handling
Replace the wrapper-div approach (which broke CSS layout) with a
render-time childIndexMap that maps DOM child positions to allMessages
indices. This avoids both the useLayoutEffect index-drift bug and the
wrapper-div CSS side effects.
- Remove data-msg-idx wrapper divs; messages render directly as
container children, preserving original [&>*] CSS layout
- Build childIndexMap during MessageList render, skipping null items
(empty AssistantMessage, hidden tool calls via shouldShowToolCall)
- findMessageIndex walks up from click target to container's direct
child, then maps through childIndexMap
- Filter hidden tool calls and empty content in copyAllMessages
- Extract handleCommonWebviewMessage to deduplicate routing logic
across sidebar, editor panel, and restored panel handlers
- Clear lastContextMenuProvider on dispose to prevent memory leaks
* fix(vscode): handle image messages in copy and resolve intermittent copy failure
- Copy Message on image messages now outputs markdown format 
instead of empty string
- Copy All Messages includes image messages as  instead of
skipping them
- Copy Last Reply skips empty assistant placeholders during streaming
- Resolve intermittent copy failure by pre-resolving message index on
right-click instead of storing a DOM element reference that can become
stale after React re-renders
* feat(vscode-companion): support /export session command
* fix(vscode-ide-companion/webview): prefer ACP session id for export
* feat(vscode-ide-companion): support /export slash command
Add nested /export completion and ACP command availability for the VS Code companion.
Reuse the shared export flow, write to the default path, and show clickable export results in chat.
* fix(export): align slash command messaging
Restore the CLI export description to the existing wording.
Keep the VS Code companion error message consistent with the required /export subcommands.
* fix(webui): support explicit markdown file links
Handle local markdown file links in assistant messages even when automatic file-link detection is disabled.
Normalize encoded paths and line fragments so exported files can be opened from the VS Code webview.
* test(vscode-ide-companion): make export path assertion cross-platform
* fix(vscode-ide-companion): use public session export entrypoint
* fix(cli): replay standalone ESC after early capture
* fix(vscode-ide-companion): resolve rebase artifacts and vitest export alias
Remove duplicate AvailableCommand import caused by merge, and add
vitest resolve alias for @qwen-code/qwen-code/export so the session
export service tests can resolve the CLI export module from source.
* fix(cli): fix getAvailableCommands test mock to use getCommandsForMode
The test mock was only setting up getCommands but getAvailableCommands
calls getCommandsForMode. Add getCommandsForMode to the mock and set up
test data on it instead.
* fix(vscode-ide-companion): fix export file link click and add save dialog
- Fix file:/// URI handling in MarkdownRenderer: normalizeExplicitFileLink
now strips the file:// scheme before checking isAbsolutePath, so exported
file links are properly recognized and clickable
- Replace direct cwd file write with vscode.window.showSaveDialog() so
users can choose the export destination and filename
- Handle cancelled save dialog gracefully (return null, skip success message)
* fix(webui): scope file link handler to file:// URIs only, fix # in filenames
- normalizeExplicitFileLink now returns early for file:// URIs without
splitting on #, since vscode.Uri.file() encodes # as %23 in the path.
This prevents filenames containing # from being truncated after decode.
- Explicit-link click handler now only fires for file:// URI hrefs,
not arbitrary relative paths. This prevents model-generated markdown
links from bypassing enableFileLinks=false and opening arbitrary files.
- Remove unused KNOWN_FILE_EXTENSIONS constant.
* fix(vscode-ide-companion): update export tests for save dialog, fix stale JSDoc
- Add showSaveDialog mock to sessionExportService.test.ts
- Update existing test to verify save dialog is called with correct args
- Add test for cancelled save dialog returning null
- Fix JSDoc that incorrectly claimed fallback-to-cwd behavior
* fix(core): preserve reasoning_content during session resume and active sessions (GH#3579)
* chore(core): remove dead thinkingThresholdMinutes config after latch removal (GH#3579)