mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-10 01:29:17 +00:00
5711 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
faf7c434fc
|
fix(core): reject fractional LSP limit inputs (#6455)
* fix(lsp): validate limit as positive integer Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> * fix(lsp): declare limit minimum in schema Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> --------- Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> |
||
|
|
560e6103a9
|
feat(cli): review auto-generated skills with an inline preview, editor handoff, and an in-dialog off switch (#6393)
* feat(cli): skill review dialog — inline preview, open-in-editor, turn-off option The auto-skill review dialog now shows the staged SKILL.md inline (sanitized, bounded reads, wrap-aware height cap), opens it in the configured editor without advancing (with watcher-based preview refresh so non-blocking GUI editors work), and offers a visible last option to turn the feature off — effective immediately in-session, persisted at workspace scope, non-destructive to the pending batch. Bulk options render only while at least two skills remain. Re-enabling auto-skill from /memory can resurface a batch put aside by turn-off. * test(integration): render harness + capture scenarios for the skill review dialog Browser-free harness that renders the production dialog from source via an ESM loader hook; its before mode renders the globally installed qwen (no local fixture — the baseline is what actually shipped, or a loud failure). Terminal-capture scenarios produce the PR's before/after screenshots. * fix(cli): address review findings on the skill review dialog - Sanitize the model-generated name and description in the dialog header, same as the preview body — an escape sequence in the frontmatter must not reach the terminal through the header fields. - Clamp the preview width to the dialog container cap (min(columns-4, 100), the same clamp DiffDialog uses) instead of the raw terminal width, which broke the wrapped-row accounting on terminals wider than ~106 columns. - Catch settings persistence failures in the turn-off option: surface the error in the dialog and leave the feature untouched instead of letting the throw escape the keypress handler. - Extract the auto-open gate into shouldAutoOpenSkillReview and cover it with a truth table (turn-off, /memory overlap, re-enable, Esc-dismiss). - Cover the MemoryDialog auto-skill ON->OFF toggle direction. - Release the capture harness temp dir with try/finally. * fix(cli): guard the preview watcher against async errors and event bursts An FSWatcher 'error' event after attach had no listener, so Node raised it as an uncaught exception and the global handler exited the CLI. Consume it and drop the watcher; the blocking-editor reload still works. Also debounce the watch callback (300ms, same as SettingsWatcher): a single editor save fires several raw events, and each one re-read the file and re-attached the watcher. * test(cli): drop white-box watcher tests, keep the end-to-end refresh test The prototype-spy scaffolding tested implementation details (listener registration, synthetic event bursts) and leaned on vite-node interop quirks. The existing on-disk refresh test already exercises the watcher path, debounce included. * fix(cli): sanitize action errors, log preview read failures, cover key guards - Render actionError through sanitizeMultilineForDisplay: error messages can embed the staged path, whose basename derives from the model-generated skill name. - Log the underlying cause when the preview read fails; all failures render the same 'Preview unavailable' otherwise. - Cover Ctrl+O/Cmd+O inertness and Esc dismissal with tests. - Document that getAutoSkillEnabled() also gates on bare/safe mode. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
27f8f2c95d
|
feat(cli): Add serve env isolation and total admission (#6416)
* feat(cli): add serve env isolation and total admission Add runtime-local serve env snapshots, explicit env injection for low-cost workspace-scoped consumers, and sourceEnv support for ACP child spawn. Add a daemon-wide maxTotalSessions admission reservation hook for fresh session creation while keeping multi-workspace sessions gated. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6416) Reject fractional maxTotalSessions values so the daemon-wide session cap remains an integer count and matches the documented limit semantics. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address PR review feedback (#6416) Always pass the runtime env to A2UI stdio transports, keep daemon runtime env metadata coherent after env reload fallback, and tighten total-admission coverage. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address total admission review feedback (#6416) Add retryable ACP error data for total session limits, log total-admission REST rejections, and keep session-limit response scopes explicit. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address env review feedback (#6416) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): restore scheduled task serve deps (#6416) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): isolate runtime env reload base (#6416) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6416) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6416) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6416) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6416) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: fix CI failure on PR #6416 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address daemon admission review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address runtime env review feedback Scrub daemon bearer tokens from A2UI stdio MCP environments and prune reload-owned keys from the daemon runtime base before rebuilding runtime env snapshots. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): preserve daemon env base on reload Keep runtime env rebuilds anchored to the boot-time daemon base snapshot, preventing reload-owned key pruning from dropping valid shell-exported values. Also carry env file read failure details into runtime metadata and daemon logs. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): satisfy env metadata lint rules Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
86ae16a6d6
|
chore(release): v0.19.7 (#6484)
* chore(release): v0.19.7 * docs(changelog): sync for v0.19.7 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
65c82bed66
|
feat(web-shell): unify scheduled task sessions — bind chat-created tasks + clock icon (#6453)
* fix(web-shell): rename scheduled tasks "查看历史" to "查看对话" * feat(serve): bind cron_create durable tasks to dedicated sessions via keepalive The cron_create tool (core layer) writes durable tasks to disk without a sessionId because it has no access to the session bridge. The keepalive loop runs in the daemon process where the bridge IS available, so it retroactively binds unbound tasks to dedicated sessions — the same flow POST /scheduled-tasks uses for UI-created tasks. Each unbound task gets: spawnOrAttach(sessionScope:'thread'), named ⏰ prompt, sessionId written back to disk. This makes chat-created tasks show "查看对话" with a clock icon in the session list, matching the UI's "新建定时任务". * feat(serve): watch tasks file for immediate binding of new cron_create tasks The keepalive interval is 2-5 minutes, so a chat-created task could wait that long before being bound to a dedicated session — showing no "查看对话" link until the next tick. Adding a file watcher (same directory-watch + debounce pattern the scheduler uses) triggers an immediate tick when cron_create writes to disk, so the task is bound within ~500ms. * feat(serve): bind cron_create tasks to current session + ⏰ rename via keepalive Switch from creating a separate dedicated session to binding the task to the current chat session (so the first message is already in the transcript). The keepalive then renames that session to ⏰ prompt — the core layer can't rename sessions (no bridge access), but the daemon process can. A Set tracks renamed sessions to avoid repeated updateMetadata calls. Unbound tasks (legacy/CLI) still get new sessions via the existing bind path. * fix(core): keep createDurable() tasks unbound by default Reverts the auto-binding of durable tasks to the current session in createDurable(). Binding to a specific session means only that session can fire the task (#shouldFireDurable), but non-daemon paths (TUI, ACP, headless) have no keepalive to rehydrate the session after exit — making tool-created durable tasks go dormant. The daemon keepalive (bindAndNameSessions) already handles binding unbound tasks to dedicated sessions with ⏰ naming, so daemon-mode tasks get the same UX without the regression. * fix(serve): roll back orphan sessions in keepalive binding + add tests When bindAndNameSessions spawns a dedicated session for an unbound task but the subsequent updateCronTasks write fails (or the task was deleted between read and write), the spawned session was left behind with no owning task — the next tick would see the task still unbound (or spawn more orphans). Add rollback: closeSession + removeSession on failure, matching the POST /scheduled-tasks rollback pattern. Also add positive test coverage for the new binding paths: - unbound task → spawn + name + write sessionId to disk - bound task without ⏰ prefix → named exactly once (renamed Set dedup) - task vanishes before write → spawned session is rolled back * fix(serve): add timeout to spawnOrAttach in keepalive binding + test hardening BZ-D: spawnOrAttach in bindAndNameSessions had no timeout boundary — a hung spawn would keep running=true and stall all subsequent ticks, stopping heartbeats/revives for every scheduled-task session. Wrap with withTimeout (configurable via spawnTimeoutMs, default 30s) and attach a background handler to clean up late-resolved orphans. Also generalized withTimeout error messages to include the operation name, and made spawn timeout configurable for tests. Test improvements (GPT-5 review suggestions): - Assert spawnOrAttach payload (workspaceCwd + sessionScope: thread) - Verify SessionService.removeSession called during rollback - Regression test: createDurable stays unbound after enableDurable - Hung-spawn test: tick completes despite non-abortable spawn hang * fix(serve): keepalive hardening + i18n sync (review suggestions) - i18n: sync English 'View history' → 'View conversation' to match Chinese '查看对话' - Prune renamed Set alongside reviveState when tasks are removed - fs.watch: clarify null filename handling for Linux (treat as match) - updateCronTasks: skip .map() when task not found (no-op optimization) - Add tests: disabled unbound exclusion, naming failure resilience |
||
|
|
3d1122d284
|
perf(cli): defer startup prefetch tasks (#6303)
Some checks failed
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
SDK Python / Classify PR (push) Has been cancelled
SDK Python / SDK Python (3.10) (push) Has been cancelled
SDK Python / SDK Python (3.11) (push) Has been cancelled
SDK Python / SDK Python (3.12) (push) Has been cancelled
* perf(cli): defer startup prefetch tasks * fix(cli): await IDE for prompt-interactive startup * perf(cli): defer interactive telemetry startup * test(cli): add missing assertions and Zed/ACP path coverage for startup prefetch Address three test coverage gaps identified during code review: - Assert mockStartEarlyStartupPrefetches in both kitty protocol tests (C1: API preconnect call was wired but never verified) - Add Zed/ACP integration test verifying deferIdeConnection is false when getExperimentalZedIntegration returns true (C2: Zed path was entirely untested) - Assert mockStartBackgroundHousekeeping in startup-prefetch test (C3: unconditional housekeeping dispatch was never verified) * docs: move startup prefetch design doc to performance subdirectory * docs: translate startup prefetch design doc to English * fix(cli): address startup prefetch review comments Tighten the startup prefetch follow-up fixes from review while keeping prompt-interactive telemetry on the fast interactive startup path. - Preserve Error objects when deferred startup tasks fail - Remove the unbalanced api_preconnect profiler lifecycle event - Guard background housekeeping so it only runs for interactive configs - Document and test prompt-interactive telemetry deferral semantics * fix(cli): initialize telemetry for prompt-interactive prompts Ensure sessions launched with an initial interactive prompt have telemetry ready before the auto-submitted first request runs. - Exclude prompt-interactive startup from telemetry deferral - Pass a post-render telemetry option through interactive UI startup - Skip duplicate post-render telemetry startup for initial prompts - Update tests to cover the first-prompt telemetry guarantee Note: Plain interactive TUI startup still defers telemetry post-render. * fix(cli): preserve startup first-request guarantees Keep deferred startup work from weakening first-request behavior in interactive sessions that submit prompts automatically or remotely. - Store telemetry deferral on Config and reuse that decision at render time - Keep IDE startup awaited for prompt-interactive and input-file sessions - Add a timeout for deferred IDE connection failures - Cover ordinary interactive telemetry deferral and IDE startup edge cases * fix(cli): make post-render IDE connection opt-in Default startInteractiveUI to the already-connected IDE path so future callers do not accidentally connect twice when initializeApp used its eager default. - Change the post-render IDE connection default to false - Update startInteractiveUI tests to assert the safer default * perf(cli): surface deferred IDE connection status Make ordinary interactive IDE startup visible while preserving the post-render prefetch path and first-paint performance tradeoff. - Emit deferred IDE connection lifecycle events for connecting, success, and failure states - Surface IDE startup status in the TUI footer without blocking input - Log late underlying IDE failures after timeout for better diagnostics - Document telemetry deferral tradeoffs and add startup lifecycle tests --------- Co-authored-by: heyang.why <heyang.why@alibaba-inc.com> |
||
|
|
e3d7d10d1d
|
[codex] add natural channel memory intents (#6376)
* feat(channels): add natural channel memory intents * fix(channels): add explicit guard and exhaustiveness check for clear_confirm intent The clear_confirm path was handled as implicit fall-through at the bottom of handleChannelMemoryIntent. If a new intent kind were added to the ChannelMemoryIntent union, it would silently execute clearChannelMemory without user confirmation — a data-loss risk. Add explicit if (intent.kind === 'clear_confirm') guard and a const _exhaustive: never assertion so TypeScript flags any unhandled kinds at compile time. * fix(channels): close session leak in classifier and fix regex separator - BridgeChannelMemoryIntentClassifier now wraps prompt() in try/finally to always call cancelSession(), preventing daemon session leaks on every classifier invocation. Cleanup errors are caught so they cannot mask a successful classification result. - Add missing optional punctuation separator to the 以后记住 regex pattern for consistency with other Chinese remember patterns. * fix(channels): enforce pending clear state for channel memory confirmation The clear_confirm intent executed clearChannelMemory directly without verifying a prior clear_request was issued for the same chat. Any authorized user could clear any chat's memory by sending the confirmation phrase standalone, bypassing the two-step flow. Add a per-target pending clear map (chatId + threadId, 60s TTL) that is set during clear_request and verified+consumed during clear_confirm. Standalone confirmation phrases now get rejected with a prompt to issue the clear request first. * fix(channels): include senderId in pendingClears key to prevent cross-user confirmation User A could initiate clear_request in a group chat and User B could confirm it, since the pending key only included chatId+threadId. Add senderId to the key so only the user who initiated the clear can confirm it. * fix(channels): harden memory intent review fixes * fix(channels): cover memory clear sender guard * fix(channels): block group memory mutations * fix(channels): avoid ambiguous memory saves * test(channels): cover memory classifier cleanup * test(channels): cover memory clear expiry * fix(channels): restore channel memory slash aliases * test(channels): cover memory intent edge cases |
||
|
|
467b292b50
|
feat(channels): add WeCom intelligent robot channel (#6436)
* feat(channels): add WeCom smart bot channel * fix(channels): harden wecom review suggestions * fix(channels): address wecom critical review * fix(channels): include wecom mixed voice text * fix(channels): tighten wecom outbound media * fix(channels): harden wecom outbound sends * fix(channels): address wecom review blockers * fix(channels): address wecom review followups * fix(channels): harden wecom inbound handling * fix(channels): address wecom auth and media review * fix(channels): tighten wecom inbound cleanup * fix(channels): harden wecom media safety * fix(channels): address wecom review typecheck * fix(channels): harden wecom media review gaps * fix(channels): address wecom review blockers * fix(channels): tighten wecom media edge cases * fix(channels): address wecom review blockers * fix(channels): address wecom media review blockers * fix(channels): address wecom review follow-ups * fix(channels): address wecom review blockers * fix(channels): close wecom review blockers * fix(channels): close wecom preflight dedup race * fix(channels): close wecom review gaps * fix(channels): harden wecom kick reconnect * fix(channels): defer wecom session resolution * fix(channels): clean wecom session attachments * fix(channels): harden wecom reconnect and media cleanup * fix(channels): address wecom review diagnostics * fix(channels): improve wecom diagnostics * fix(channels): reset wecom kick retries * fix(channels): improve wecom diagnostics * fix(channels): preserve sync cancel preflight * fix(channels): close wecom connection and ssrf gaps * fix(channels): clean coalesced wecom attachments * fix(channels): bound wecom sdk connect wait * fix(channels): scope wecom untracked attachment cleanup * fix(channels): block wecom nat64 local-use ssrf * fix(channels): harden wecom media handling * fix(channels): harden wecom group gates * fix(channels): bound wecom kick reconnect cycles * fix(channels): drain loop collect prompts directly * fix(channels): align wecom buffer hooks * fix(channels): harden wecom delivery failures * fix(channels): recover from wecom attachment write failures * fix(channels): surface wecom media send failures * fix(channels): harden wecom replay and reconnect * fix(channels): clarify wecom partial delivery cleanup * fix(channels): close wecom rejected downloads * fix(channels): retain wecom dedup after processing starts * fix(channels): harden wecom reconnect and media errors * fix(channels): add wecom media error context * fix(channels): improve wecom dns diagnostics * fix(channels): keep wecom kick retry alive * fix(channels): allow wecom quoted bot replies * fix(channels): preserve wecom code fences across chunks * fix(channels): harden wecom reconnect lifecycle * fix(channels): report wecom media dir setup failures * fix(channels): harden wecom reconnect recovery * fix(channels): align wecom review fixes * fix(channels): harden wecom marker parsing * fix(channels): keep wecom reconnect timers alive * fix(channels): handle wecom tilde fences * fix(channels): preserve wecom fence state * fix(channels): clean up wecom attachment races * fix(channels): bind wecom media reads to file handles * fix(channels): prevent wecom symlink media opens * fix(channels): address wecom review blockers * fix(wecom): remove media URL from error messages to prevent credential leakage The guardedHttpsDownload error messages included rawUrl (truncated to 120 chars), which leaks private WeCom media download URLs into stderr and log aggregation systems. Remove the URL from redirect and HTTP error messages. * fix(wecom): address review feedback — tests, security, correctness - Remove stale URL assertions from media download error tests (the error messages no longer include raw URLs after the credential-leak fix) - Redact sensitive fields (secret, aeskey, token, password, authorization) in formatSdkError's JSON.stringify fallback to prevent credential leakage in logs - Add indented code block detection to findCodeRanges so [IMAGE: path] inside 4-space/tab-indented code is not stripped as a media marker - Add disconnectGeneration guard before mkdirSync in downloadAttachments to prevent orphaned temp directories when disconnect() races with in-flight attachment downloads * fix(wecom): wrap client.disconnect() in catch block to preserve connection error In the connect() catch block, client.disconnect() could throw (e.g. if the WebSocket was already destroyed), masking the original connection error. Wrap in try/catch so cleanup failures never shadow the root cause. * fix(channels): address wecom reconnect review blockers * fix(channels): harden wecom reconnect review fixes * fix(channels): harden wecom review blockers * fix(channels): address wecom review blockers * fix(channels): preserve unsupported wecom media markers * fix(channels): address wecom reliability suggestions * fix(channels): allow wecom retry after early drops --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
971d4ba27e
|
feat(web-shell): add column reorder, resize, and freeze controls to markdown table (#6444)
* feat(web-shell): add markdown table column controls Support resizing, reordering, and freezing table columns while preserving visible-order copy behavior and selection stability. * fix(web-shell): address markdown table review feedback * fix(web-shell): refine markdown table column reordering * fix(web-shell): address markdown table review feedback --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
55b2886909
|
fix(web-shell): split-view pane fixes (remove "current" badge, clear composer on send) (#6454)
* fix(web-shell): remove meaningless "current" badge from split-view panes In the split view every pane is an equal, independently interactive session (its own DaemonSessionProvider, SSE, transcript, and approvals), so tagging one pane as the workspace's "current" session carried no operational meaning. It only leaked a single-view concept into a peer-of-equals layout and reliably prompted "what is this?" questions from users. Panes are already identified by their titles. Drop the isCurrent badge and its border highlight from ChatPane, and stop passing isCurrent from SplitView. The sidebar and session overview keep their "current" indicators, which are legitimate "you are here" navigation. currentSessionId is retained only to seed the initial pane. * fix(web-shell): clear the split-view composer on send, not at turn end A split-view pane kept the just-sent text sitting in its composer until the whole turn finished. handleSubmit committed the draft on the sendPrompt promise resolving, but that promise resolves via waitForAcceptedPromptCompletion (turn end), not at admission. Switch to the onAdmitted hook so the composer clears the moment the daemon accepts the prompt, matching the main view. A prompt rejected before admission still preserves the draft and surfaces the error. * fix(web-shell): pass commitAccepted directly as onAdmitted; cover admit-then-fail Review follow-up: - commitAccepted is already `() => void`, so pass it directly as the onAdmitted option instead of wrapping it in a redundant `() => commitAccepted?.()` closure. - Add a test for the turn failing after admission: the draft stays cleared (no second commit) and the error is still surfaced to onError. |
||
|
|
1d19fe7172
|
fix(web-shell): refine tool call summaries (#6450)
* fix(web-shell): refine tool call summaries * fix(web-shell): address tool summary review feedback --------- Co-authored-by: ytahdn <ytahdn@gmail.com> |
||
|
|
40340ef505
|
fix(serve): classify interrupted model stream errors (#6422)
* fix(serve): classify interrupted model streams * fix(serve): address interrupted stream review * test(webui): cover legacy terminated turn error fallback * fix(web-shell): preserve error message data shape * test(daemon): cover turn error fallback boundaries * fix(web-shell): preserve classified error data --------- Co-authored-by: ytahdn <ytahdn@gmail.com> |
||
|
|
d1d53d7122
|
fix(monitor): preserve inherited git pager (#6429) | ||
|
|
9bb68b323c
|
fix(core): strip system-reminder blocks from session title and recap side-query prompts (#6435)
* fix(core): strip system-reminder pollution from session title and recap prompts The `filterToDialog` function in sessionTitle.ts and sessionRecap.ts was including system-reminder blocks (skills list, CLAUDE.md, MCP announcements) in the prompt sent to the title/recap model. When the user's first message was short, `flattenToTail` would reach back into these injected entries, causing titles like "resolve-cr-comments" instead of reflecting the actual conversation. - Skip startup prelude entries via `getStartupContextLength` - Strip `<system-reminder>` blocks from text parts - Add shared `stripSystemReminderBlocks` helper in environmentContext.ts - Add regression tests for both sessionTitle and sessionRecap Closes #6419 * fix(core): preserve mixed reminder prompt turns |
||
|
|
6fdd0fc710
|
fix(core): Support large text range reads (#6404)
* fix(core): support large text range reads Allow text reads to stream bounded line ranges for files larger than the previous 10MB guard, while preserving media size limits and forwarding cancellation through read_file/read_many_files/ACP paths. Refs #6403 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): address large text review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): propagate abort signals in text reads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): validate streamed utf8 reads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): handle disabled line truncation for large reads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): use kebab-case for text range reader Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): preserve artifact size errors for large sources Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): address large text review follow-up Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): allow default large text reads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): honor text read byte caps Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): clarify invalid utf8 range read errors Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): prevent truncated full large reads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): preserve unbounded line-zero reads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): forward artifact read cancellation Pass artifact execution abort signals into source file reads and preserve cancellation semantics when the read is aborted. Add regression coverage for unbounded large UTF-8 range reads and offsets beyond EOF. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): address file read review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): preserve large text mutation reads Allow default unbounded readTextFile calls to keep reading full large text files so mutation tools can prepare complete snapshots after a prior ranged read. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
17138b525f
|
fix(web-shell): hide rotating loading phrase in split-view pane status (#6447)
Split-view panes now render a compact streaming status: the spinner, elapsed time, token count, and cancel hint stay, but the rotating "witty" loading phrase is suppressed. StreamingStatus gains a showPhrase prop (default true, so the main chat is unchanged); when false it also skips the phrase-rotation timer so each pane avoids a needless interval. |
||
|
|
cfb1febfe3
|
Avoid refreshing session activity on load (#6439)
* fix(core): avoid refreshing session activity on load * test(core): align resumed title source expectations * docs(core): clarify resumed title reanchor |
||
|
|
bd6816b7ac
|
fix(web-shell): keep errored turns expanded (#6424)
* fix(web-shell): keep errored turns expanded * test(web-shell): cover errored turns with final answer --------- Co-authored-by: ytahdn <ytahdn@gmail.com> |
||
|
|
ce2fee926f
|
fix(web-shell): clear stale floating todos (#6425)
* fix(web-shell): clear stale floating todos * test(web-shell): cover floating todo reset --------- Co-authored-by: ytahdn <ytahdn@gmail.com> |
||
|
|
c7d22dc1d4
|
fix(web-shell): improve user tags and mobile menu layout (#6441) | ||
|
|
f7296d0333
|
feat(web-shell): add Qwen logo beside the sidebar new-chat button (#6437)
Place the Qwen brand mark to the left of the sidebar's New chat button. The artwork is the same SVG used for the browser-tab favicon (and the QwenLM GitHub avatar), inlined rather than hot-linked because the Web Shell CSP is `img-src 'self' data: blob:`, which blocks remote images. When the sidebar is collapsed there is no room beside the compact button, so the mark is hidden and only the New chat button remains. |
||
|
|
55652d4912
|
fix(web-shell): keep split-view session list fresh and preserve panes across view switches (#6418)
* fix(web-shell): keep split-view session list fresh and preserve panes across view switches The in-window split view's "add pane" picker read a stale session snapshot — `useSessions` only fetches on mount — so sessions created after entering the split never appeared. And switching away from the split and back cleared the panes, because the live pane set lived in local state that died on unmount while the seed it re-mounted from was never updated (and the no-arg "Open Split View" button reset it to empty). - Reload the picker list when it opens and when the parent's session-list reload token changes, so it never offers a removed session or misses a new one. - Mirror the live pane set up to the app via onPanesChange so it survives SplitView unmounting; restore it (instead of reseeding empty) when the split is reopened without an explicit selection. * test(web-shell): cover split-view refresh/restore per review; coalesce token reloads Addresses review feedback on #6418: - SplitView: skip a token-driven reload while one is already in flight, so a burst of session-list changes (bulk create/delete) doesn't fire a redundant concurrent round-trip per bump (matches the sidebar's poll guard). - SplitView test: the freshness test now proves the picker re-renders with the refreshed list — a session appearing only after reload shows up — not just that reload() was called. - App test: cover the openSplitView preserve/restore path end-to-end — a reported pane set survives leaving the split and is restored on reopen. * fix(web-shell): reload split picker on every token bump (drop in-flight guard) The in-flight guard added in the previous commit could drop a session-list reload token that arrives while a reload is still running: the effect has already run for that token value, and clearing the in-flight flag in `finally` doesn't re-run it, so the picker could stay stale after burst create/delete/ rename activity — and the split has no polling fallback to recover. Reload on every distinct token bump instead. `useDaemonResource` serializes responses via its sequence counter (last write wins), so overlapping reloads are correct, and the token is bumped only on discrete session-change events — an occasional redundant fetch is far cheaper than a lost refresh. * test(web-shell): cover openSplitView explicit-selection branch (dedupe + cap) Per review: the restore branch of openSplitView was covered but the explicit-selection branch (dedupe + MAX_SPLIT_PANES cap, replacing any prior set) was only exercised, not asserted. Add a `?split=` URL test with duplicate and over-cap ids that asserts the split seeds exactly the deduped, capped selection. |
||
|
|
077a2471f9
|
fix(core): prevent re-invoking loaded skill from appending duplicate content (#6430)
When a skill is invoked multiple times in a session, each invocation previously appended the full SKILL.md body content to the conversation history as a new tool result, wasting context tokens. Add an isSkillLoaded callback to SkillToolInvocation that checks loadedSkillNames before building the full content. On re-invocation, return a short confirmation message instead of the full body. The check runs after successful skill load (so disabled/not-found paths are unaffected) but before content construction, hooks registration, and allowedTools application (which are idempotent and already applied on first load). Fixes #6427 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
736b710ed0
|
feat(core): add tools.visible config for selective deferred-tool visibility at startup (#6372)
* feat(core): add tools.visible config for selective deferred-tool visibility * fix(cli): wire tools.visible from settings.json into Config Add settingsSchema entry for tools.visible and plumb it through loadCliConfig into ConfigParameters.visibleTools. Without this the core-level visibleTools support was unreachable from settings.json. Adds 3 CLI-level tests: visibleTools passthrough, empty default, safe-mode suppression. Fixes #6368 * fix(core): exclude visibleTools from tool_search candidates and reveal path Add visibleTools gate to collectCandidates() and loadAndReturnSchemas in tool-search.ts to prevent KV-cache invalidation when tool_search is invoked for a visible-deferred tool. Without this, a tool listed in tools.visible would still appear as a keyword-search candidate and select: would still trigger revealDeferredTool + setTools, defeating the purpose of promoting it to first-class visibility. 3 tests: keyword-search exclusion, select: no-reveal/no-setTools, and select: still works for non-visible deferred tools. * fix(cli): include visibleTools in /context per-tool token breakdown Add config.getVisibleTools().has(tool.name) gate to the deferred-tool skip condition in collectContextData. Without this, visible-deferred tools appear in the headline total (via getFunctionDeclarations()) but are excluded from the per-tool breakdown, causing the sum to mismatch. 1 test: visibleTools included in breakdown despite deferred+unrevealed. * fix: address all 7 review suggestions for tools.visible 1. settingsSchema: user-facing description instead of internal jargon 2. config.ts: use normalizeToolNameList (generic name) for both disabled and visible 3. config.test.ts: add bare-mode exclusion test 4. tool-registry.test.ts: disabledTools > visibleTools priority test 5. tool-registry.ts: update JSDoc for getDeferredToolSummary 6. tool-search.test.ts: mixed select: visible+non-visible test 7. tool-registry.test.ts: visible survives clearRevealedDeferredTools * chore: regenerate settings.schema.json after description update CI check detected that settings.schema.json was out of sync with settingsSchema.ts after the description was changed in commit 73e879882. * fix: address wenshao review — extract isDeferredAndHidden, clean up abstractions 1. Remove normalizeToolNameList (empty wrapper, violates AGENTS.md no-abstraction rule) 2. Extract ToolRegistry.isDeferredAndHidden() — 5 call sites reduced to 1 predicate source 3. Add dirty-input test for tools.visible (whitespace, duplicates, empty strings) 4. Add MergeStrategy.UNION test for tools.visible across user + workspace scopes --------- Co-authored-by: Aleks-0 <aleks-0@users.noreply.github.com> |
||
|
|
7e0e79b6bc
|
fix(daemon): preserve user message source metadata (#6385) | ||
|
|
5d2bfbd21b
|
feat(core): add Tool(param:value) permission syntax for parameter-level access control (#6106)
* feat(core): add Tool(param:value) permission syntax for parameter-level access control Introduces key:value parameter matching in permission rules, allowing users to grant or deny tool access based on specific input parameters. - Parse key:value pairs from specifiers for literal-kind rules - Support wildcard patterns (*), multiple params, and mixed syntax - Thread toolParams through PermissionCheckContext and matchesRule - Add 11 unit tests covering parsing, matching, wildcards, and edge cases - Maintain backward compatibility with existing specifier kinds Example rules: Agent(model:opus) # deny agents using Opus model Agent(coder,model:*) # deny coder-type agents with any model Bash(git:*) # still works (legacy :* → git *) Closes #6100 * fix(permissions): resolve PR #6106 review comments for tool param permission syntax - buildPermissionRules now propagates toolParamMatchers for 'Always Allow' flow - MCP tool rules now check param matchers after name matching - Added Object.hasOwn check to prevent prototype chain lookup vulnerability - Replaced matchesCommandPattern with matchesParamValuePattern for param value matching - Added diagnostic logging for param matching failures - Added warnings for empty valuePattern, invalid keys, and non-literal key:value syntax - Added type checking for non-primitive param values * fix(core): address critical review feedback on Tool(param:value) permission syntax - Add 's' flag to RegExp in matchesParamValuePattern for multiline support - Filter buildPermissionRules to stable param keys only (model, subagent_type, skill, server_name) to prevent sensitive data leakage - Reject MCP rules with unsupported specifiers instead of silently ignoring - Fix :* wildcard conversion to use global replace for backward compatibility - Extract shared evaluateParamMatchers helper to deduplicate MCP and standard branches * fix(permissions): address remaining review feedback for Tool(param:value) syntax - Remove unused @ts-expect-error in gitWorktreeService.ts (CI blocker) - Fix ReDoS in matchesParamValuePattern: replace regex with linear-time glob matcher using indexOf, avoiding catastrophic backtracking on multi-wildcard patterns like *a*a*a*a*b - Fix MCP backward compatibility: exclude MCP tools from key:value parsing in parseRule and buildPermissionRules to preserve existing MCP deny rule semantics - Add tests for MCP + param matcher, partial wildcards, ReDoS prevention, number coercion, and buildPermissionRules with toolParams (stable params, volatile params, sensitive data, round-trip) * fix(permissions): address PR #6106 review comments and fix useStatusLine test timeout - Make matchesParamValuePattern case-insensitive to match matchesDomainPattern convention - Remove duplicate JSDoc block before matchesParamValuePattern - Remove dead server_name from stableParamKeys in buildPermissionRules - Add PermissionManager integration tests with toolParams (evaluate, findMatchingDenyRule, hasRelevantRules, hasMatchingAskRule) - Add type guard tests for evaluateParamMatchers (null, undefined, boolean, object) - Fix useStatusLine.test.ts timeout by stubbing cron-task exports in core mock --------- Co-authored-by: 易良 <1204183885@qq.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
fde18828ae
|
feat(cli): support stacked slash-skill invocations (#6361)
* feat(cli): support stacked slash-skill invocations (#6355) Allow users to chain multiple slash-skill commands in a single prompt (e.g. `/feat-dev /e2e-testing implement X`). The skill bodies are concatenated with the remaining user text and submitted as one `submit_prompt` to the model, so the model receives all loaded skill contexts at once. - Add `parseStackedSlashCommands()` with a `MAX_STACKED_SKILLS = 5` cap - Integrate stacked detection into both interactive (slashCommandProcessor) and non-interactive dispatch paths - Record `recordSkillInvocation` telemetry for each stacked skill - Emit a warning when more than 5 skills are requested - 29 new test cases covering parsing edge cases and both dispatch paths Closes #6355 * fix(cli): address review feedback for stacked skill invocations - Fix whitespace tokenization to match all \s chars, not just spaces - Align telemetry recording: record success based on actual result type (both dispatch paths now consistent) - Surface error messages from non-submit_prompt skill results - Propagate modelOverride from first submit_prompt skill - Add 7 new tests: tab whitespace, mixed whitespace, non-submit_prompt exclusion, telemetry accuracy, modelOverride propagation * fix(acp-bridge): use static import in logRedaction test to avoid timeout The dynamic `await import('./spawnChannel.js')` inside the test body was pulling in a heavy module graph at runtime. Under CI contention (667 tests running concurrently), this exceeded the 5-second default timeout. Convert to a static top-level import — the same pattern used by spawnChannel.test.ts. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): move stacked skill block inside try/finally and collect onComplete callbacks - Move stacked skill dispatch into try block so finally cleanup runs (setIsProcessing, chat recording, telemetry) preventing TUI freeze - Set invocationSentToModel=true for stacked invocations so chat history correctly classifies the command as sent to model - Collect and forward onComplete callbacks from all submit_prompt skill results in both interactive and non-interactive paths --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
001d20ff26
|
feat(scheduled-tasks): run each task in its own dedicated, named session (#6389)
* feat(scheduled-tasks): run each task in its own dedicated, named session Scheduled tasks created through the Web Shell management page were never firing in the daemon-only case: the durable-cron tick runs inside an active agent session, and the Web Shell creates a session only lazily on the first prompt, so a task created on the management page (with no chat open) had nothing ticking it. This binds every management-page task to a dedicated session, minted at create time and named "⏰ <task>". The task fires ONLY inside that session — its transcript is the task's run history — instead of via the shared per-project durable owner. A daemon-side keepalive heartbeats those sessions so the idle reaper doesn't stop them, and a boot-time rehydration reloads them after a restart. Archiving, deleting, or unarchiving the session disables, removes, or re-enables the bound task (covered on both the REST and ACP surfaces). Also adds task editing, a live next-run countdown, run history, a one-per-row card layout, and a "run now" that executes in the task's bound session and updates the last-run time. All resident-session management is opt-in and enabled only by the real daemon (runQwenServe), so createServeApp embeds/tests are unaffected. * fix(scheduled-tasks): address code review on per-session task feature Review fixes for #6389: - Distinguish archive-disabled from user-disabled tasks: disableTasksForSessions now marks disabledByArchive; enableTasksForSessions only re-enables tasks carrying that flag, so a task the user deliberately disabled stays disabled across an archive/unarchive cycle. [Critical] - Rehydrate task sessions concurrently with a per-session 30s timeout so one hung loadSession can't stall the boot sweep or leave healthy tasks dormant. [Critical] - Await runScheduledTask + reload before executing the prompt in handleRunNow, so a record failure surfaces and the card's "last run" reflects the trigger. [Critical] - Log keepalive/rehydrate read + heartbeat failures at debug instead of swallowing them silently, so a persistently-failing keepalive is diagnosable. [Critical] - Add integration tests: deleteDaemonSessions -> removeTasksForSessions and unarchiveDaemonSessions -> enableTasksForSessions (guard the coupling). [Critical] - DELETE route: single atomic updateCronTasks that captures the bound session and removes the task in one cycle, closing the read-then-remove TOCTOU. - Stop the keepalive timer during shutdown (matters for embedders that don't process.exit) so it can't fire against a disposed bridge. - Deduplicate DEFAULT_BUILDER: export it once from scheduledTasksSchedule and drop the dialog's copy so the create form and cron-reversal can't drift. - Reject empty-string sessionId in isValidTask: a bound task with "" would silently run unbound under the scheduler's truthy guard. * fix(scheduled-tasks): isolate task sessions, fix catch-up/jitter/revive Second review round (#6389): - Force `sessionScope: 'thread'` when minting a task's session. The daemon's default scope is 'single', which attaches to (reuses) the shared workspace session — so a second task, or a task alongside an open chat, would bind to the same session, rename it, land runs in the wrong transcript, and close it on delete. Thread scope guarantees each task an isolated session. [Critical] - Re-seat a recurring task's schedule anchor to now when a PATCH changes its cron (or flips one-shot→recurring), not just on re-enable. A bound task's catch-up runs on every file-watch reload, so a bare cron edit to an expression with an already-past slot would fire immediately on save. [Critical] - Revive a non-resident bound session from the keepalive when its heartbeat fails (reaper let it go while disabled/archived, now re-enabled). Covers the unarchive and PATCH false→true paths uniformly and retries each interval, so a re-enabled task actually resumes instead of showing a live countdown that never fires. Best-effort, timeout-bounded, non-blocking. [Critical] - Report `nextRunAt` using the scheduler's jittered fire time (`nextDurableFireMs`) instead of the bare cron boundary, so the UI countdown lines up with the real fire (the tick offsets each fire by up to the jitter window) rather than expiring early and advancing prematurely. All four are mutation-verified. The cross-daemon double-fire on bound tasks (same session live in two schedulers) is a separate, architecturally-invasive fix (claim-then-fire on the durable file) tracked as a follow-up. * fix(scheduled-tasks): sync bound session name on task rename Create names a task's session after the task (`⏰ <name>`), but a later PATCH that renamed the task (or edited the prompt of an unnamed task) left the session's display name stale. The PATCH route now re-applies `updateSessionMetadata` with the task's effective label whenever that label actually changes — a bare cron/enabled edit does not touch the session. Best-effort: a metadata failure doesn't fail the committed schedule change. Mutation-verified. * fix(scheduled-tasks): mirror run sessionId on client type; clarify server wiring Review follow-up (#6389, qqqys): - [Medium] `DaemonScheduledTaskRun` now mirrors the daemon's `CronTaskRun` `sessionId?: string`, so run-attribution the wire already sends isn't silently dropped by the client type (not surfaced in the UI yet; passthrough cast means no mapping change needed). - [Nit] Comment the `app.locals.stopScheduledTaskKeepalive` set site, noting it follows the same convention as `fsFactory`/`boundWorkspace`/`acpHandle` and is read by the run-qwen-serve shutdown path (kept the convention rather than diverge to a one-off return value / declaration merge). - [Nit] Comment the outer `.catch(() => {})` on rehydrate as intentional defense-in-depth (the function already handles read + per-session failures). * fix(scheduled-tasks): couple archive/enable + record manual run only on enqueue Two [Critical] review items (#6389, gpt-5-codex): - PATCH re-enable coupling: reject `enabled: true` on a task disabled BY archiving its session (`disabledByArchive`) with 409 `task_session_archived`. Re-enabling it here would show an enabled task with a countdown while its bound session stays archived and can never fire — the caller must unarchive the session (which clears the marker and reloads it). A user-disabled task (no marker) and non-enable edits are unaffected. - Manual "run now" ordering: record the run only AFTER the prompt is enqueued, not before. `runTaskManually` now returns a promise that resolves on enqueue and rejects if the bound session can't be opened (archived/deleted), is superseded, or times out; the dialog awaits it before writing /scheduled-tasks/:id/run, so a failed session switch no longer leaves a phantom run in history. Runs are serialized (one pending at a time, button disabled) so two quick clicks can't drop a prompt on the single bound-run latch. Added coverage for failed session load and double-click; all new tests mutation-verified. * fix(scheduled-tasks): close dormancy/orphan/overflow gaps from review Five items from GPT-5 /review (#6389): - [Critical] Bind tasks to sessions only when resident management is on: createServeApp now passes the bridge to the scheduled-task routes only when `manageScheduledTaskSessions` is set. Embedders that leave it off get UNBOUND tasks (shared-owner firing) instead of bound tasks nothing keeps resident or reloads (which would silently go dormant). - [Critical] Keep the keepalive/revive loop running whenever task sessions are managed, not only when a reaper is active — archiving closes a task session, so a re-enabled one still needs reviving with the reaper disabled. Size the interval under the reaper window (≤ half of it) so a small idle timeout can't let a session be reaped before its first heartbeat. - [Critical] Record a manual run only after the prompt is admitted: the bound run latch now resolves only if `sendPrompt` admitted the prompt and rejects on cancellation (e.g. onSubmitBefore) / failure, so a cancelled Run now no longer advances lastFiredAt or appends history. - [Critical] Clamp the dialog's reload timer to the 32-bit setTimeout ceiling (~24.8 days) so a months-away schedule can't overflow and spin a reload loop. - [Suggestion] Pre-check the task cap before spawning a session, so an over-cap create never mints an orphan task session it must roll back. New tests (route unbound-when-no-bridge, cap-no-spawn, computeKeepaliveIntervalMs bounds, far-future timer clamp) mutation-verified; full server suite green. * fix(scheduled-tasks): guard catch-up double-fire, run-now hang, /run + cron edits Four items from GPT-5 /review (#6389): - [Critical] Bound-task catch-up could double-fire: detection ran on every file-watch reload and read the stale on-disk lastFiredAt, so a reload racing the async catch-up persist (a foreign write to the tasks file) re-detected and re-fired the same overdue slot. Track ids whose catch-up was DELIVERED but not yet persisted (`deliveredCatchUp`) and skip re-detecting them until the write lands; a merely-buffered-then-dropped catch-up isn't tracked, so it still re-detects from disk (recovery preserved). - [Critical] "Run now" hung the full 30s switch timeout when the bound session was ALREADY the current, loaded one (no dep change → the consuming effect never re-ran). Fire the enqueue directly after loadSidebarSession resolves as well as from the effect; whoever runs first nulls the latch, so it runs once. - [Critical] POST /run recorded a run with no enabled/disabledByArchive guard, unlike PATCH — a direct API caller could write a phantom "ran" record onto a paused/archived task. Return 409 task_disabled for a disabled task. - [Suggestion] Anchor re-seat on cron edit compared the raw string, so a cosmetic change (`0 9 * * *` → `00 9 * * *`) dropped a pending catch-up. Compare the canonical (parsed) schedule instead. (The setTimeout-overflow and keepalive-floor reports were already fixed in 2a12cba.) New tests for the first three + the cosmetic-cron case are mutation-verified; full core scheduler + route suites green. * fix(scheduled-tasks): block disabled-task run in UI; record manual run at admission Two [Critical] review follow-ups (#6389): - A disabled task could still EXECUTE from the Web Shell: the Run button was only gated on `runningTaskId`, so clicking it enqueued the prompt and the server's `/run` `task_disabled` guard merely refused the later history write — a real, unrecorded run. Gate `handleRunNow` and disable the button on `!task.enabled` too, so a disabled task's prompt is never enqueued. - Manual run recorded only after the whole turn: the bound-run latch resolved via sendPrompt, which completes through waitForAcceptedPromptCompletion, so a long/permission-blocked run or a closed tab could execute without ever being recorded. Add an `onAdmitted` callback to sendPrompt (fired when the daemon accepts the prompt, before the turn) and resolve the manual-run latch at admission instead — cancellation before admission still rejects. New dialog test (disabled task → no enqueue) mutation-verified; webui/web-shell typecheck + existing session-action tests green. * fix(scheduled-tasks): guard tick double-fire, cap rehydration, harden lifecycle writes Review follow-ups (#6389): - Extend the fire-persist re-detection guard to ON-TIME tick fires, not just catch-ups (renamed deliveredCatchUp → firePersistPending): a bound task fired by the tick advances lastFiredAt asynchronously, so a reload racing that write (bound detection runs every reload) could re-detect the slot and double-fire. The tick persist now adds its ids to the guard and clears them when the write lands, symmetric to the catch-up persist. - Bound boot-rehydration concurrency (batches of 4): each loadSession forks a child, so loading up to 50 at once spiked the host and risked spawn failures that strand tasks. The keepalive revive path was already sequential. - Archive disable failure is now logged (was fully swallowed) so a broken archive→pause coupling — where the keepalive would revive the just-archived session — is diagnosable. - Unarchive re-enable failure is surfaced in the result `errors` and logged, and enableTasksForSessions also runs for already-active sessions — so a task left stranded ({enabled:false, disabledByArchive:true}) by a prior failed enable is recoverable by re-unarchiving, instead of being permanently stuck. - Create rollback now removes the persisted session (close + removeSession), so the loser of a concurrent create at the cap boundary (passes the pre-check, loses the authoritative write) doesn't leave an orphan named session. New tests (tick-fire guard, bounded rehydration, already-active recovery) mutation-verified; full core scheduler + serve suites green. * fix(scheduled-tasks): one-shot run/edit correctness; tick persist non-regression Review follow-ups (#6389, ci-bot): - [Critical] Manual /run on a ONE-SHOT task now removes it from the store. Its slot is still in the future, so stamping lastFiredAt=now didn't stop the scheduler firing it again at its original time — a double run. A one-shot's manual run IS its single fire, so the task is spent. - [Critical] PATCH recurring:false now re-seats the one-shot's createdAt anchor. The old (long-past) anchor made the scheduler read it as a MISSED one-shot and fire + permanently delete it. Re-seating createdAt points its next fire at the upcoming occurrence. Also covers a cron edit on an existing one-shot. - [Suggestion] The tick persist no longer regresses lastFiredAt: it skips the write when the on-disk stamp is already >= the tick slot (a concurrent manual /run or catch-up may have stamped newer), mirroring the catch-up persist guard. - [Suggestion] Added the missing create-rollback test: a post-spawn commit failure closes AND removes the minted session (no orphan). New tests (one-shot run removal, recurring→one-shot re-seat, rollback teardown) mutation-verified; core scheduler + route suites green. * fix(scheduled-tasks): ref-count fire guard, real rehydration cap, authoritative run check Four [Critical] review follow-ups (#6389): - Ref-count firePersistPending (was a boolean Set): the same task can have two lastFiredAt persists in flight (fired again before the first write landed); clearing on the first settle dropped the guard while the second was still pending, re-opening the double-fire window. The count holds it until the last persist settles. - Rehydration concurrency is now enforced on the REAL loads: loadSession isn't abortable, so a timed-out load kept forking in the background while the next batch started. A bounded worker pool holds each slot until the underlying load actually settles, so in-flight child spawns never exceed the cap. - Unarchive recovery reports failures for the full resume set: it enables both unarchived AND already-active sessions but only logged/returned errors for unarchived, so a failed already-active recovery surfaced errors:[] and left a task stranded. Deduped one list used for the call, log, and errors. - Manual "run now" re-checks server-authoritative state before enqueuing: the dialog snapshot can be stale (another tab/API disabled/deleted the task), so it would execute the prompt and only the /run record would 409. It now refreshes, bails if gone/disabled, and enqueues the FRESH prompt/session. New tests (ref-count, slot-held-past-timeout, stale-disabled re-check) mutation-verified; core scheduler + serve + dialog suites green. * fix(scheduled-tasks): catch-up non-regression, disabled-edit re-seat, run/timer/keepalive hardening Review follow-ups (#6389): - [Critical] Catch-up persist no longer regresses lastFiredAt: use `>=` like the tick persist, so a newer stamp (a cross-process manual /run) landing while the catch-up write is in flight isn't overwritten back to the older minute. - [Critical] The PATCH anchor re-seat now runs for schedule edits even while the task is disabled — editing a disabled one-shot's cron then re-enabling it (two separate requests) no longer leaves a stale anchor that fires + deletes it. - [High] Manual "run now" of a bound ONE-SHOT consumes it server-side (/run, which deletes) BEFORE enqueuing, so a record failure leaves a recoverable "recorded but never ran" instead of a silent double execution at its slot. - [Medium] The dialog reload timer backs off a stuck past-due nextRunAt (fast reloads to catch a just-fired advance, then a slow lane) instead of spinning a 1 Hz GET loop. - [Medium] The manual-run latch bounds the admission phase with a timeout, so a send that wedges before admission degrades to a visible "run failed" instead of freezing the run controls. - [Suggestion] Keepalive: an in-flight guard skips a tick while the previous pass runs (no duplicate concurrent loadSession spawns), and per-session exponential backoff stops retrying a permanently-gone session every interval. New tests mutation-verified. Two deeper items (a task session winning the durable lock and firing unbound tasks; tearing down a consumed one-shot's session) are left open as tracked follow-ups — both need new daemon↔child infrastructure. * fix(scheduled-tasks): one-shot anchor on unarchive, memoize next-fire, sanitize + log Review follow-ups (#6389): - [Critical] enableTasksForSessions now re-seats a ONE-SHOT's createdAt anchor (not just recurring's lastFiredAt) on unarchive — otherwise unarchiving a task that was converted to recurring:false while disabled fires it as a missed one-shot and permanently deletes it. - [Critical] Log the DELETE-path removeTasksForSessions failure (was fully swallowed) like the archive/unarchive paths — the session is already gone, so a silent write failure leaves the still-enabled bound task a permanent ghost. - [Medium] Memoize nextDurableFireMs (deterministic per id/cron/recurring/anchor) — a sparse cron costs hundreds of ms per scan and the route recomputed it per task on every request, stalling the event loop for 50 yearly tasks. - [Nit] The consumed one-shot /run response now nulls nextRunAt (it was advertising a future fire on an entity the next GET omits). - [Suggestion] scheduledTaskSessionName strips terminal control sequences (the bridge title guard rejects them → silently drops the rename) and truncates on a code-point boundary (no lone surrogate broadcast as U+FFFD). - [Critical/doc] Document that firePersistPending is instance-scoped — the narrow cross-instance restart window is an accepted edge. - Added the missing test for editing an enabled one-shot's cron. New tests mutation-adjacent; suites green. Two deeper items (session deleted outside the daemon orphaning a bound task; surfacing bound tasks in cron_list) are left open as tracked follow-ups. * fix(scheduled-tasks): re-seat one-shot anchor on re-enable; guard duplicate revive Two [Critical] review follow-ups (#6389): - Re-enabling a one-shot now re-seats its createdAt anchor (added justReEnabled to the one-shot branch). A one-shot disabled past its slot then re-enabled was otherwise read as a missed one-shot on the next reload — fired immediately and permanently deleted. Updated the prior "leaves anchor untouched" test to the safe behavior (fires at next occurrence). - Keepalive revive no longer spawns a duplicate child: loadSession isn't abortable, so a timed-out revive keeps running; a later tick (past its backoff) would start a SECOND load for the same session. An in-flight `reviving` set (cleared on the load's TRUE settlement, not the timeout) blocks that — without holding the sequential tick, so other sessions' heartbeats aren't delayed. Added a configurable reviveTimeoutMs for the test. Both mutation-verified. (The one-shot /run session teardown raised again is the same item as the open deferral — a synchronous close there would break the run, which executes after /run; it's tracked for the keepalive orphan-sweep.) * fix(scheduled-tasks): strip bidi override/isolate chars from session name The bridge's title guard (hasControlCharacter) only rejects C0/DEL, so Unicode bidi override/embedding/isolate controls (U+202A–202E, U+2066–2069) slip past it and can visually reorder a scheduled-task session name in the session list — a Trojan-Source-style attack (CVE-2021-42574). Strip them alongside the existing terminal-control-sequence pass, matching core's stripDisplayControlChars canonical set. Adds a test built from code points so the test file itself carries no reordering controls. * fix(scheduled-tasks): close review findings — rehydrate deadlock, manual-run recording, shared helpers, tests Addresses the review findings on the per-task-session work: - keepalive rehydrate no longer awaits a non-abortable loadSession after its timeout. A genuinely hung load would pin its worker and, with enough hangs, wedge the whole boot sweep (Promise.all never settles) so later task sessions never rehydrated. The worker now records the timeout as failed and pulls the next queued session; the background load is left to settle. Rewrote the test that pinned the old "hold the slot" behavior into a no-wedge regression guard. - web-shell manual run drops its pre-admission timeout. sendPrompt isn't abortable, so rejecting on the timer while the send was still in flight let a LATE admission execute an UNRECORDED run the user could retry into a duplicate. The run is now tied to admission (accepted prompts are always recorded); the "session never becomes active" phase stays bounded by the switch timeout in runTaskManually. - extract collectBoundSessionIds() shared by the heartbeat + rehydrate passes (was duplicated) and isBoundTask() in the lifecycle module (was the lone `sessionId !== undefined` check vs. the strict one used everywhere else). - spell the nextDurableFireMs cache-key separator as `\x00` rather than a literal NUL byte, so cronScheduler.ts no longer reads as binary to ripgrep. - add App.test coverage for the manual-run orchestration (admission-resolve, cancel/error reject, immediate fire, supersede, switch timeout) and a keepalive test that a disabled task gets no heartbeat and no revive. * fix(web-shell): "create via chat" opens a fresh session in scheduled tasks The scheduled-tasks "Create via chat" button switched to the chat view but stayed on the CURRENT session, piling the task-creation conversation onto whatever the user was already doing. It now starts a new session first (createNewSession) and jumps to it before priming the composer, so task creation gets its own chat. Covered by a new App.test case asserting clearSession() is called. * fix(scheduled-tasks): address follow-up review findings - keepalive rehydrate: guard the onError callback with try/catch. If it threw (e.g. stderr EPIPE during log rotation) the rejection escaped loadOne, failed its worker, and short-circuited Promise.all — stranding every other queued session. - cronScheduler catch-up: use the strict `typeof sessionId === 'string' && length > 0` bound-check instead of `!== undefined`, matching every other "is bound?" site. - server rehydration: log the outer defense-in-depth catch instead of swallowing it, so an unexpected throw isn't a silent "tasks never fire". - session-name sanitizer: also strip the standalone Bidi_Control marks U+061C / U+200E / U+200F, not just the override/isolate ranges. - scheduled-tasks dialog: when a consumed one-shot then fails to deliver, show a specific "deleted but never ran — recreate it" error instead of the generic "run failed" that hid the deletion. Kept the deliberate consume-first ordering. * fix(web-shell): don't prime the composer when "create via chat" can't start a new session onCreateViaChat's deferred composer-priming ran unconditionally: if createNewSession() failed, the task-starter text was dropped into the CURRENT session (only onSessionIdChange was gated on success). Gate all post-create side effects on `created`, matching handleMissingSessionNewSession. Adds an App.test failure-path case (new session fails → composer not primed). --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
9ee8546a60
|
fix(shell): avoid Unix pager default on Windows (#6390)
* fix(shell): avoid Unix pager default on Windows * fix(shell): clear inherited pager env on Windows * docs(shell): clarify platform-specific pager default * fix(shell): normalize pager env handling * fix(shell): preserve git pager fallback behavior * test(shell): stabilize pager env coverage |
||
|
|
067cfbba62
|
docs: consolidate design docs and plans under docs/ (#6417)
Design docs and implementation plans were scattered across .qwen/design, .qwen/plans, and docs/superpowers. The .qwen/ locations are git-ignored, so docs written there never got tracked, while docs/design already held the richer, version-controlled set. Consolidate everything under docs/design and docs/plans, relocate two stray root docs into docs/design, and repoint the references left dangling by the move (moved-doc cross-links and a few source comments). Also update AGENTS.md and the feat-dev skill so the documented workflow writes new design docs and plans to the tracked docs/ locations. Co-authored-by: DragonnZhang <dragonzhang1024@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4c884e47bd
|
fix(core): prevent KV-cache invalidation on tool_search by reordering reminderParts (#6420)
Reorder reminderParts in getInitialChatHistory() so stable parts (MCP instructions, skills snapshot, startup context) come first and volatile deferred-tools reminder is last — prefix-caching servers retain the KV-cache for the shared prefix, only the tail recomputes. Co-authored-by: Aleks-0 <aleks-0@users.noreply.github.com> |
||
|
|
46bbe76835
|
fix(core): align monitor limit parameter schemas (#6413)
* fix(core): align monitor limit parameter schemas Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> * test(core): cover monitor fractional limit validation Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> --------- Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> |
||
|
|
ce00e932ae
|
fix(core): allow rewind after compressed history (#6358)
* fix(core): allow rewind after compressed history * fix(core): separate compressed prefix rewind handling * fix(core): handle rewind after restored startup context * test(core): cover compressed rewind sentinel mismatch * fix(cli): align rewind mapping after compression |
||
|
|
132801b739
|
feat(core): add maxSubAgents setting to limit parallel sub-agent count (#6354)
* feat(core): add maxSubAgents setting to limit parallel sub-agent count Adds a `maxSubAgents` configuration option that limits the number of sub-agents running in parallel. Excess agents are queued without timeout countdown until a slot becomes available. Closes #5176 * fix(core): apply sub-agent concurrency cap to foreground runs * fix(core): narrow sub-agent concurrency scope * fix(core): clarify invalidated slot reservations * fix(core): correct sub-agent slot accounting * fix(core): drain silent cancellation waiters * test(core): cover background slot reservation paths |
||
|
|
ff317d61cf
|
perf(core): Add session start profiler (#6349)
* perf(core): Add session start profiler Add an opt-in internal profiler for GeminiClient.startChat so session initialization can be broken down by bounded stages before choosing the next #6312 optimization. The profiler writes best-effort JSONL records only when QWEN_CODE_PROFILE_SESSION_START=1 and avoids sensitive values such as prompts, paths, session IDs, hook output, model responses, and tool names. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Keep session profiler finish best-effort Wrap session-start profiler finish metadata collection in the same best-effort boundary as record writes, and cover repeat finish plus sync failure handling in tests. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Cover profiler review suggestions Deduplicate startChat profile finalization attributes and add coverage for repeated stage duration accumulation. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Cover profiler failure paths Strengthen session-start profiler tests for first-failure tracking and startChat sync-stage failure finalization. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Harden session profiler output Restrict session-start profiler JSONL output permissions and add review-requested tests for optional fields and first-stage warm failures. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Reuse session profiler env constant Use the profiler env constant in the JSONL test so the test cannot drift from the runtime gate. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Cover profiler timing edge cases Add coverage for fractional session profiler rounding and the absence of session context application timing when SessionStart returns no additional context. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Cover non-zero profiler counts Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): cover disabled session profile env values Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
06cd7ce13f
|
feat(cli): add --project and --global flags to /model for per-project model persistence (#6060)
* feat(cli): add --project and --global flags to /model for per-project model persistence Add scope control to the /model command so users can persist model selections to either project-level or user-level settings independently. - /model --project: persist to workspace .qwen/settings.json - /model --global: persist to user ~/.qwen/settings.json - /model (no flag): unchanged behavior (backward compatible) - Model dialog title shows scope: 'Select Model (this project)' / 'Select Model (global)' - Completion and argumentHint updated with new flags - Full i18n support for zh/en Closes #6052 Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): add missing zh-TW translations for /model scope flags Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): address PR review — scope flags, subcommand persistScope, titles, tests - parseScopeFlags: use (?:^|\s) instead of \b for --flag matching (\b fails because - is not a word character) - Completion: strip all flags to isolate model prefix, supports any order - Subcommand dialogs (fast/voice/vision) now propagate persistScope - slashCommandProcessor forwards persistScope for all subcommand cases - ModelDialog title combines subcommand mode + scope label e.g. 'Select Fast Model (this project)' - Subcommand confirmations show scope suffix (project/global) - Extract persistScopeSpread() helper to reduce duplication - Add 9 tests covering scope flags, dialog returns, confirmations - Add i18n keys for scope suffix labels in zh/en/zh-TW Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): use Partial<Config> & {[key:string]:unknown} to fix index signature TS error Replace Record<string,unknown> with Partial<Config> & {[key:string]:unknown} to satisfy TS4111 index signature access rule in the CI build. Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): add scope suffix to ModelDialog history items Address review comment: historyManager.addItem for voice/fast/vision/main model selections now shows scope indicator like ' (this project)' or ' (global)', consistent with CLI direct-set confirmations. Affected: handleModelSwitchSuccess (main), handleSelect (voice/fast/vision) Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): wrap scopeSuffix in t() and unify wording with ModelDialog - scopeSuffix in modelCommand.ts now uses t(' (this project)') / t(' (global)') instead of hardcoded English strings, matching ModelDialog.tsx wording - Main model confirmation uses shared scopeSuffix instead of separate i18n keys, eliminating 'Model: {{model}} (project)' duplication - Remove unused i18n keys from en/zh/zh-TW locales - Update tests to expect '(this project)' wording Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): address code review feedback — scope validation, i18n, tests - Reject inline prompt + scope flag combination with clear error (#1) - Add mutual exclusivity check for --project and --global (#5) - Verify setValue scope parameter in tests + add --global test (#2) - Extract scopeSuffix to shared variable, remove duplication (#3) - Remove dead i18n keys 'Select Model (this project)' / '(global)' (#4) - Fix scopeSuffix placement on model line not API key line (#8) - Add fr.js / ja.js translations for scope keys (#10) - Remove unused export ModelDialogPersistScope (#6) - Wrap non-interactive help text in t() with new flags (#7) - Fix argumentHint grouping to show mode vs scope flags (#11) Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): reject --project when workspace is untrusted Reject --project scope flag before direct persistence or opening ModelDialog when settings.isTrusted is false. Workspace settings are ignored on merge in that state, so the save would silently not take effect. Also mirrors the guard in ModelDialog.tsx resolvePersistScope() to fall back to user scope when the dialog is opened with --project on an untrusted folder. Default mock settings now includes isTrusted: true. Signed-off-by: Alex <alex.tech.lab@outlook.com> --------- Signed-off-by: Alex <alex.tech.lab@outlook.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
1ee9780223
|
fix(core): Gate large PDF text extraction (#6409)
* fix(core): Gate large PDF text extraction Prevent text-only PDF fallback from injecting full large-document extraction results into the prompt. Large attachment reads now become short references, direct no-pages reads return a short file-too-large error, and page-range extraction is token guarded. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Stabilize large PDF reference test Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Address PDF budget review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Stabilize PDF read-file test Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Allow page-range reads for huge PDFs Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Clarify PDF text truncation contract Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Address PDF review follow-ups Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): Cover multi-page PDF guidance Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Harden paged PDF extraction guards Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Restore authoritative PDF page-count gate Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Keep large PDF references independent of pdftotext Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Narrow dense PDF retry guidance Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
db8e3448e3
|
fix(cli): smoother streaming table rendering (#6345)
* fix(cli): smoother streaming table rendering Follow-up to the streaming table hold-back, on its own branch so the cue-removal PR (#6340) can land undisturbed. Makes a live table stream predictably instead of jittering, flashing, or hanging. - Atomic rows: hold a frontier row back until it has ALL its columns. A multi-column row passes through intermediate states that are themselves valid rows with fewer cells (`| a |`, `| a | b |` toward `| a | b | c |`), so the old hold-back let it fill in cell by cell. Now the whole row (border + every cell) appears in one step. - Widths track the current rows (no freeze): a wider row redraws the whole table once; a narrower row changes nothing (widths are a max over all rows, so they only ever grow). Redraw-on-wider only, never per token. - Bias the streaming preview to the horizontal format: while a table is the live frontier it only falls back to the vertical `label: value` list when the terminal is genuinely too narrow, not because an early row wraps tall. This stops a table from briefly rendering as a vertical list and then flipping to a horizontal table (a visible jump). - Hold a forming table back until it is recognizable: a header (and any partial separator) is trimmed while pending until a separator matching the header's column count arrives, so the header no longer streams in char by char as raw `| a | b |` text before snapping into a box. Fenced code-block content is left untouched. - Draw the empty header box as soon as the table is recognized, before the first row completes, so the table area does not sit blank (no box, no cue) and look like a hang if generation stalls in that window. A zero-row box omits the header/body divider so it reads as a clean header, not an empty row. Only the live frontier table is affected; completed and committed tables use the normal logic. 211 tests pass (MarkdownDisplay + TableRenderer). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): guard the two remaining zero-row / non-table edge cases Review follow-up (two [Critical] findings). - TableRenderer: the maxLineWidth safety check is a second path to the vertical format, unguarded for zero-row tables. On a very narrow terminal a zero-row streaming header box would fall through it and render an empty string — the box vanishes. Skip that fallback when there are no rows so the header stays visible even if it slightly overflows. - MarkdownDisplay: the pre-loop header hold-back trimmed ANY trailing run of pipe-leading lines. When the first line is not a complete `| … |` row, headerCells was 0 and the run was trimmed anyway — so non-table pipe text (an un-fenced shell pipeline `| grep foo`, pipe-prefixed log output) would vanish from the live preview until commit. Only hold back when the first pipe-line is a plausible table header (a complete row). Tests cover both. 215 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): hold a multi-column header mid-type without hiding pipe text The previous commit (restricting the header hold-back to a complete `| … |` row, to stop non-table pipe text from vanishing) reintroduced the cell-by- cell header flash: while a header is typed (`| Alpha`, `| Alpha | Bet`, …) it is not yet a complete row, so it rendered as raw text. Discriminate by column count instead of closed-ness: a table header has ≥2 columns; a single-pipe line (shell pipeline `| grep foo`, pipe-prefixed log) has one cell. Count cells on the first line whether or not it is closed, and hold the run only when it has ≥2 columns and no matching separator yet. So a multi-column header held mid-type no longer flashes, while single-pipe non- table text still renders (the earlier [Critical] fix stands). A header still typing its very first cell is indistinguishable from a single-pipe line, so it shows briefly until the second column appears — the narrowest flash possible without hiding real pipe text. 217 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): make table format decision consistent, not streaming-biased The horizontal-vs-vertical bias (force a live table horizontal while streaming) backfired for tables that genuinely belong in the vertical `label: value` format — a wide table with many columns of long, wrapping text. It rendered horizontal (tall, clamped, looking stuck) while it was the streaming frontier, then flipped to vertical the moment it stopped being the frontier (the next block started) or committed — a visible format flip, and worse than the vertical-list flash it was meant to avoid. Drop the streaming bias: the horizontal-vs-vertical decision is now the same while pending and once committed, so a table never flips format between the two. Removes the now-unused isStreaming / isStreamingFrontier plumbing. Known residual (pre-existing, not from this change): because column widths track content (redraw-on-wider), a borderline table's wrapped-row height can still cross the vertical threshold mid-stream. Fully stabilizing that needs a content-independent format decision — a separate change. 217 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(cli): note the redraw-on-wider format-oscillation trade-off Document the accepted limitation next to the horizontal-vs-vertical decision: because column widths track content (redraw-on-wider), a table with very long cell text sitting right at MAX_ROW_LINES can still oscillate format while streaming. Only extreme wide/long-text tables hit it; the alternatives (content-independent decision, or frozen widths) each cost more than the residual is worth. Comment-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): count held-back header columns like the table detector The streaming hold-back counted header columns on the full line with empty cells filtered out, while the main table detector strips the outer pipes and splits without filtering. For a header with an empty-named column like `| A || B |` the two disagreed (2 vs 3), so the hold-back never found the matching 3-column separator and hid the table for the whole stream. Count columns the same way in both places. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): release multi-cell non-table pipe content during streaming The streaming hold-back keeps a run of pipe-lines back until a matching separator arrives, so a real multi-column header does not flash in cell by cell. But multi-cell non-table pipe content — a shell pipeline (`| grep foo | wc -l`), a log excerpt (`| 200 | OK | GET /x`), an ASCII-art border — also has >=2 cells, so it was held for the entire stream and only appeared on commit. A markdown table's separator is the line immediately after the header, so once a line follows the header and does not even begin like a separator (optional pipe, optional colon, then a dash), the run is decided: not a forming table. Release it. A lone header still being typed (no line after it yet) is still held, so the no-flash behavior is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): anchor the vertical-format decision to the first row (no flip) The horizontal-vs-vertical choice used maxRowLines measured over EVERY row, so a table that started horizontal (short first row) flipped to vertical the moment a later, taller-wrapping row streamed in — a visible mid-stream format change. Measure only the header + the first data row instead. The first row is representative for the common case, so the format is decided once and stays put as rows append. Column widths still track all rows (redraw-on-wider is unchanged); only the format choice is anchored. Trade-off: a table whose first row is short but a later row wraps very tall stays a (taller) horizontal grid rather than flipping to vertical — rare, and preferable to a visible flip. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): release a dash-led data row from the streaming hold-back The "could this pipe run still become a table?" check treated any line after the header that merely started with a dash as a possible separator, so an options table whose first data cell begins with a flag — `| --verbose | … |` — was held back for the whole stream. Use tableSeparatorRegex instead: it still matches a partial separator being typed (`|--`) so a real header is held until its separator lands, but rejects a dash-led data cell (trailing letters), which now renders live. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): defer a streaming table until its first row (no empty-box flip) A recognized table with no complete data row yet was drawn immediately as an empty header box. A zero-row table can only render horizontally (the vertical fallback needs rows), so once a long first row landed the box flipped to the vertical label:value format — a visible format change that cannot be avoided by looking at the header alone (column names are short; width comes from the values). Defer the table while pending until its first row completes, so it first appears already in its final format with no flip. Cost: the table area stays blank while the header + first row stream (the pre-loop trim already hid the header text, so this only extends that blank). Committed tables always have rows, so their behavior is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): address review — code-fence tracking, held-back edge cases, committed format Five review findings: - [Critical] The pre-loop hold-back's code-fence check used a naive boolean toggle that ignored fence char/length, so a nested fence (```` with an inner ```) mis-closed and a real code line like `| A | B |` was held back and vanished while streaming. Track the open fence's delimiter and validate the close (same char, >= length), mirroring the main parser. - A COMPLETE separator whose column count already differs from the header can never match, so release the pipe run instead of holding it for the whole stream (the main parser treats it as text). - The end-of-content table flush now uses the same `tableRows.length > 0` guard as the mid-content handler, so a degenerate zero-row table behaves the same whichever way it ends — no EOF-vs-mid asymmetry. - TableRenderer's first-row-only maxRowLines (no-flip) applied to committed tables too; a committed short-first-row + tall-later-row table wrongly stayed horizontal. Gate on a new `isPending` prop: measure the first row only while streaming, every row once committed (most readable, no flip concern). - Renamed the test block that claimed a nonexistent `isStreaming` prop; added committed-vs-streaming format tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): don't flip a completed mid-content table's format at commit A table closed by a following line is complete even while the message keeps streaming, but it was still rendered with the first-row-only format anchor — so a short-first-row + tall-later-row mid-content table showed horizontal and then flipped to vertical the moment the message committed. Split the two concerns that were both riding on `isPending`: - the height clamp still tracks whether the MESSAGE is streaming (so a mid-content table stays bounded and the estimator's clamped cost can't under-estimate the render); - the format anchor now tracks whether THIS TABLE is the streaming frontier. The mid-content flush passes isFrontier={false} → all rows measured → final format now; only the end-of-content (frontier) table anchors to the first row. Renamed TableRenderer's format-anchor prop to `isStreaming` (it is not the message-level pending flag). Added mid-content and tilde-fence tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): don't hold a pipe line inside an open $$ math block The streaming table hold-back tracked code fences so a `| A | B |` code line would render, but not display-math (`$$ … $$`) blocks. The main parser pushes math content verbatim (never as a table), so a `| a | b |` norm/matrix line at the frontier of an open math block was treated as a forming table and blanked until the block closed. Track math fences in the trim's fence scan too, mirroring the main parser's precedence (code block wins, then math), and skip the hold-back while inside one. Addresses the low-confidence review observation on #6345. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
686326863a
|
fix(web-shell): polish scheduled task timeline UI (#6386)
* feat(web-shell): mark scheduled task turns in timeline * fix(web-shell): confine locate flash to message content * fix(web-shell): flash parallel agent locate target * fix(web-shell): keep scheduled marker source optional * fix(web-shell): omit default scheduled timeline flag * fix(web-shell): repair scheduled timeline UI conflict * fix(web-shell): remove stale shell output prop --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
80340fb73f
|
fix(review): remove qwen-code-specific core-infra gate from bundled /review (#6412)
The bundled /review skill is a general command that runs against arbitrary
repositories (and cross-repo PRs), but a previous change baked qwen-code's own
"core infrastructure is maintainer-only" governance into the shipped prompt:
hardcoded packages/core and packages/*/src/{auth,providers,models,config,tools,services}
paths, a 500+ line hard block, and an authorAssociation-based maintainer check.
Those path names are generic — src/auth, src/config, src/tools, src/services are
common across monorepos — so an external contributor's large PR to an unrelated
repo would be hard-blocked as "must be maintainer-initiated" under a policy that
repo never adopted.
Remove the gate and its escalate-flag plumbing (Steps 1, 6, and 7) from the
bundled skill, along with the matching DESIGN.md rationale and the user-doc
section. qwen-code's maintainer-only policy stays documented in AGENTS.md for
this repo. The Issue Fidelity / root-cause ownership agent (Agent 0) is a
universal review principle and is left unchanged.
Co-authored-by: dragon <dragon@U-2Q53JQG9-0233.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
f41e95ac18
|
feat(web-shell): add Session Overview panel and in-window split view (#6400)
* feat(web-shell): add Session Overview panel and in-window split view
Add a large-screen "Session Overview" mission-control panel and an
in-window split view so users can monitor and drive multiple daemon
sessions at once.
- SessionOverviewPanel: ranked live cards (needs-approval -> running ->
idle) merging the workspace session list with the detail=full status
report. Multi-select opens the selected sessions as a split view in
the current tab ("Open in split") or in a new browser tab ("Open in
new tab", via a ?split=a,b URL).
- SplitView + ChatPane: one DaemonWorkspaceProvider hosting N
DaemonSessionProvider panes, each a self-contained interactive chat
(transcript, composer, streaming, tool/ask approvals). Browser focus
scopes the keyboard per pane, so panes never contend over approvals.
- Sidebar entry points gated to large screens; the split view's Back
returns to the Session Overview.
* refactor(web-shell): address review feedback on the session overview / split view
- SessionOverviewPanel: prune the selection Set when a session leaves the list
(so a reappearing session isn't silently reselected) and make select-all use
the intersection rather than prev.size.
- Extract isAskUserPermission into a shared util so App.tsx and ChatPane.tsx no
longer keep verbatim copies that can drift.
- SplitView: dismiss the "add session" picker on Escape or a click outside it.
- Tests: MAX_PANES cap, popup-blocked path, checkbox-selects-without-navigating,
stale-selection pruning, and a direct test for the extracted util.
* fix(web-shell): address /review findings on the split view
- ToolApproval: add a `keyboardActive` prop; split panes pass false so global
Enter/Escape/digit shortcuts can't confirm the wrong session's approval, and
the outer session's approval overlay is no longer rendered behind the split
(where it would keep its global shortcuts while hidden).
- ChatPane: defer the composer commit until sendPrompt resolves, so a rejected
prompt (transcript loading / disconnected / turn active) preserves the draft
instead of silently dropping it.
- SplitView: include a per-mount nonce in each pane's clientId so two tabs
opening the same split don't share a client id — which suppressOwnUserEcho
would treat as a self-echo and drop from the transcript.
- SessionOverviewPanel: cap the split selection to MAX_SPLIT_PANES before
building the ?split= URL or opening the in-window split, with a hint when more
are selected; also dismiss the split picker on Escape / click-outside.
- Tests covering each.
* fix(web-shell): address second /review round on the split view
- SplitView: wrap each pane in its own ErrorBoundary, so a render crash in one
pane (malformed block, unexpected tool shape) shows an inline fallback with a
close action instead of white-screening the whole split.
- splitUrl / overview: carry the daemon token into the new-tab split URL's
fragment. The current tab has already stripped the token from its URL, so a
token-auth (`serve --open`) deployment would otherwise open the split tab
unauthenticated. The token rides the hash (never sent to the server / logs).
- Tests: per-pane error isolation, token-in-fragment (and none without a token),
and the overview polling effects (interval fires, document.hidden skips, and
the in-flight guard prevents overlapping polls).
* fix(web-shell): hide the outer chat under the split and share app-level contexts
- App: hide (display:none) + aria-hide the outer chat subtree whenever
mainView !== 'chat', not only when a panel is open. Previously the outer
chat/composer/toolbar stayed reachable by keyboard/AT behind the full-page
split (it was only covered visually). State is preserved (node stays mounted).
- App: wrap SplitView in the app-level WebShellCustomizationProvider and
CompactModeContext so split panes render markdown / tool-headers / thinking
the same way the single-session chat does. Todo contexts stay chat-only —
they belong to the outer session, not the panes.
* refactor(web-shell): address review suggestions — coverage, dedup, split UX
- ToolApproval: add a dedicated test on the real component that the global
keyboard shortcut is armed by default and NOT armed when keyboardActive=false
(the cross-pane approval safety mechanism).
- SplitView: auto-exit to the Session Overview when the last pane is closed
(guarded so an initial empty seed doesn't bounce straight back out).
- ChatPane: add tests for the cancel action, the empty/whitespace submit guard,
and error routing to the onError prop.
- Extract the shared session-list page size + organization feature flag into
constants/sessions.ts, used by the overview, split view, and sidebar, so the
values can't drift between the three.
* fix(web-shell): surface outer approval + failed refresh in overview/split
- Split view: when the outer (main) session is waiting on an approval
that's hidden behind the split, show a non-blocking notice banner with
a "Go to it" button that returns to the chat where the approval lives.
- Auto-close the split (like the overview panel) when the viewport shrinks
below the large-screen breakpoint, so users aren't stranded.
- Session Overview: surface a failed refresh inline (keeping the last-good
cards) instead of silently swallowing it once cards are on screen.
- Tests: status-report poll cadence, picker dismiss (Escape / outside /
inside click), inline refresh-failure banner.
* fix(web-shell): sever window.opener on split tab; tighten hidden-chat test
- openSelectedInNewTab now clears win.opener (the split tab carries a
daemon token in its URL fragment) to prevent reverse tabnabbing, matching
the existing bug-report window.open path.
- Strengthen the split-view App test so a missing outer-chat subtree fails
instead of passing vacuously through an optional chain.
* fix(web-shell): split-view focus/stability/robustness follow-ups
- Refocus the composer after a shrink-driven split close so keyboard users
aren't dropped onto <body> (skips when an approval or panel takes over).
- Stabilize SplitView onExit via useCallback so its last-pane-close effect
doesn't re-fire on every App re-render.
- ChatPane: surface a per-pane connection-loss banner instead of silently
showing stale messages when a pane's daemon connection drops.
- ChatPane: anchor the streaming timer to the active turn's start (last user
message timestamp) so a pane opened mid-turn shows real elapsed time.
- Tests: split auto-close on shrink, outer-approval split notice + return-to-
chat, connection banner, and streaming-timer anchoring.
|
||
|
|
245defbb01
|
fix(core): gate image payload replacement behind threshold (#6380)
* fix(core): reduce multimodal history payload size * fix(core): use kebab-case image payload filenames * fix(core): address image payload review blockers * fix(core): preserve current request image payloads * ci: disable implicit actionlint pyflakes integration * fix(core): reattach recent unique image payloads * fix(core): preserve referenced image payloads * fix(core): tolerate partial config mocks in MCP discovery * fix(core): preserve current images during recovery * fix(core): gate image payload replacement behind threshold The always-on image payload replacement introduced by PR #6045 replaced ALL historical images with text references on every request, causing users' old screenshots to be reattached and triggering infinite fix loops when the model mistook stale buggy screenshots for current state. Replace the always-on approach with a threshold-gated design: - Below 20 images (configurable): zero transformation, images stay in-place in history - At or above 20: in-place replace historical images with text references, reattach only the most recent 3 unique images - Replacement is persistent (mutates this.history), so the count resets and won't re-trigger until 20 new images accumulate - Current user request images are protected via skipContent Also lower DEFAULT_SCREENSHOT_TRIGGER_THRESHOLD from 50 to 20 to align with the new image payload threshold. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
6352d97173
|
fix(memory): don't advance AutoMemory cursor when extractor makes zero tool calls (#6398)
Fixes #6311 The extract cursor previously advanced unconditionally after the forked extractor agent reported 'completed', even when it made zero real tool calls (e.g. a small/local model hallucinating a bash command instead of calling write_file). This silently and permanently skipped those history messages from being reprocessed. Also fixes extractionAgentPlanner.ts using filesTouched (attempted paths, unconfirmed) instead of filesWritten (confirmed successful writes) when deriving touchedTopics, matching the pattern already used in remember.ts. touchedTopics.length > 0 alone is not sufficient to gate the cursor advance: a legitimate 'nothing durable to save' outcome also produces an empty touchedTopics array and would otherwise be treated the same as a hallucinated run. A new hasToolActivity signal (derived from filesTouched, which includes read-only calls like read_file) distinguishes 'agent engaged with the task and found nothing new to save' (legitimate noop, cursor still advances) from 'agent made zero tool calls at all' (hallucination, cursor held for retry). |
||
|
|
70220fb281
|
feat(cli): Add Phase 2a workspace foundation (#6410)
* feat(cli): Add Phase 2a workspace foundation Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Clarify registry reload capability Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Reject valueless repeated workspace args Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Fallback for empty workspace fast path Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Address workspace foundation suggestions Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Cover registry injection happy paths Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Tighten workspace foundation guardrails Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Cover injected client MCP registry path Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
be7e874fd1
|
Handle missing web-shell sessions without redirecting (#6357)
* fix(web-shell): handle missing session routes * chore(web-shell): clarify missing session route handling * fix(web-shell): address missing session review follow-up * fix(web-shell): address missing session review issues * test(web-shell): cover missing session status handling * fix(webui): handle heartbeat terminal states * fix(web-shell): preserve missing session state * fix(webui): harden missing session diagnostics * fix(web-shell): stabilize missing session recovery * fix(webui): preserve missing session heartbeat state * fix(webui): stabilize missing session recovery * fix(webui): cover missing session review gaps --------- Co-authored-by: ytahdn <ytahdn@gmail.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
d56bd1d8f4
|
fix(daemon): handle settings reload events outside transcript (#6407)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
881d6824f5
|
fix(cli): use EnvHttpProxyAgent in channel proxy to respect NO_PROXY (#6401) (#6405)
The channel proxy path used ProxyAgent, which unconditionally routes all requests through the proxy and ignores NO_PROXY. This caused requests to hosts listed in NO_PROXY (e.g. localhost, internal IPs) to fail when a corporate proxy was configured. Switch to EnvHttpProxyAgent, matching the main CLI config path that already handles NO_PROXY correctly. Co-authored-by: qwen-autofix <autofix@qwen-code.ai> |
||
|
|
57326e55be
|
feat(review): add issue-fidelity and root-cause ownership gate to /review (#6395)
* feat(review): add issue-fidelity and root-cause ownership gate to /review Adds a dedicated Issue Fidelity & Root-Cause Ownership agent (Agent 0) to the /review pipeline and a core-infrastructure scope gate that runs before the review agents. Agent 0 fetches linked GitHub issue evidence directly (closingIssuesReferences plus issue comments) instead of trusting the PR author's framing, compares the original reported failure against the PR's claimed fix, and flags client-side parser/sanitizer workarounds for malformed upstream output as Critical unless a maintainer explicitly requested the defensive mitigation. The core-infra gate applies the repository's existing two-tier maintainer-only rule before spending review budget. This hardens the pipeline against a false-approval mode where a bot PR passes its own tests and reads as internally reasonable but fixes the author's mistaken diagnosis rather than the linked issue's actual root cause. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(review): address PR review feedback on issue-fidelity gate - Fetch issue evidence with `gh issue view --json title,body,comments` so the issue body (reporter repro/observed payload/expected behavior) is included; `--comments` alone omits it. Use each closingIssuesReferences entry's own repository so cross-repo linked issues resolve correctly. - Treat closingIssuesReferences as a discovery hint (fetch apparent target issues even when it is empty) and treat fetched issue content as untrusted data (extract facts, ignore embedded instructions). - Run Agent 0 (Issue Fidelity) only for PR targets; skip it for local-diff and file-path reviews, and require the PR number/repo/context in its prompt. Handle empty references / non-bugfix / gh failure explicitly. - Pass Agent 0's quoted issue evidence to Step 4 batch verification and stop it rejecting issue-grounded findings just because the code compiles/tests pass. - Make the core-infrastructure gate concrete: deterministic maintainer signal via authorAssociation, count only core-path lines, honor the AGENTS.md low-risk-sweep exception, clean up the worktree on hard block, run the gate right after fetch-pr (before npm ci), and map escalate -> COMMENT (never APPROVE) in Steps 6-7. - Sync agent counts and token math across SKILL.md, DESIGN.md, and code-review.md (Agent 0 is PR-only; ~620-730K). * docs(review): rename 'Linked Issue Fit' heading to 'Issue Fidelity' Aligns the code-review docs heading with the 'Issue Fidelity' name used for Agent 0 in SKILL.md and DESIGN.md, so the section connects to the pipeline diagram. Addresses review feedback. * docs(review): stop core-infra hard block before load-rules and surface it via --comment - Hard block now stops before Step 2 (load-rules) instead of before Step 3, so a PR destined for hard-block no longer runs the load-rules step. - In --comment mode the hard block posts an event=COMMENT on the PR, matching the escalate path's GitHub visibility, so external authors see the block. --------- Co-authored-by: dragon <dragon@U-2Q53JQG9-0233.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ac123bbc59
|
fix(core): preserve no-argument tool calls that stream an empty arguments string (#6250)
* fix(core): preserve no-argument tool calls that stream an empty arguments string
For tools that take no parameters, some OpenAI-compatible providers
stream `arguments: ""` (or omit the field entirely) and never send an
argument fragment. The streaming parser dropped such calls wholesale
(`meta?.name && buffer.trim()`), while the non-streaming path keeps
them with `args: {}` — so a turn containing only that call looked
empty and geminiChat raised "Model stream ended with empty response
text", triggering pointless retries.
Align the streaming parser with the non-streaming path: emit the call
with empty args when the buffer is empty at stream end. Rewrite the
unit test that encoded the drop, and add regression coverage at parser
and converter chunk level.
* fix(core): use name metadata as slot-occupancy signal for no-argument tool calls
Follow-up to review feedback: after empty buffers became a legal end
state for no-argument tool calls, three parser methods still used
buffer.trim() to decide whether an index slot was occupied. A provider
reusing indices could then silently overwrite a completed no-argument
call (addChunk collision guard, findNextAvailableIndex) or append stray
continuation chunks to it (findMostRecentIncompleteIndex).
Switch the occupancy signal in all three places to the name metadata,
keeping the JSON-completeness check for non-empty buffers. Add
regression tests for both corruption paths and update the stale
getCompletedToolCalls JSDoc.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(core): collapse non-object argument parses at emit and lock canonical empty-opener shape
Review follow-up on the no-ID continuation routing at addChunk. Mid-stream,
an empty buffer with name metadata is formally undecidable between "completed
no-argument call" and "canonical opener awaiting its first argument fragment"
(every OpenAI-compatible provider opens with arguments:"" and streams
fragments ID-less at the same index). Routing must favor the canonical shape,
so the guard stays; a new test pins that shape, which the suite previously
did not cover.
The corruption concern from review is instead bounded at emit time: a buffer
polluted by a stray fragment can parse or repair to a non-object value, which
now collapses to {} so a polluted no-argument call still emits empty args.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(core): add debug logging for empty-buffer emission and non-object argument collapse
Review follow-up: a stray fragment that happens to parse as a valid JSON
object is indistinguishable from real arguments at emit time, so log both
the non-object collapse and empty-buffer emissions to aid diagnosis.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(core): extend replay guard to opener-shaped replays of no-argument tool calls
Review follow-up: after empty buffers became a legal completed state, a
replayed opener (duplicate ID, #5107 lineage) could overwrite a completed
no-argument call's name metadata, since the replay guard only engaged on
non-empty buffers.
Swallowing every known-ID chunk at that state would drop ID-bearing
argument fragments for providers whose opener streams empty arguments, so
the guard uses the protocol shape as discriminator: a chunk carrying a
name but no argument content is an opener replay and is ignored; a chunk
with argument content is a continuation and appends. Regression tests
cover both directions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(core): cover null/array argument collapse and multi-slot relocation scan
Review follow-up: pin the null and array branches of the emit-time
non-object collapse, and exercise findNextAvailableIndex scanning past
multiple occupied no-argument slots during collision relocation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: FiaShi <FiaShi@fiashideMacBook-Air.local>
Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
0f98842ff2
|
fix(web-shell): refine tool detail cards (#6399)
Co-authored-by: ytahdn <ytahdn@gmail.com> |