fix(core): guide agent to pivot to read-only tools when plan mode blocks - #6
Closed
Alex-ai-future wants to merge 1156 commits into
Closed
fix(core): guide agent to pivot to read-only tools when plan mode blocks#6Alex-ai-future wants to merge 1156 commits into
Alex-ai-future wants to merge 1156 commits into
Conversation
Add POSIX /tmp to ACP local read fallback roots without changing read_file's default permission behavior. Also add QWEN_ACP_LOCAL_READ_ROOTS as an append-only absolute-path override for ACP fallback reads. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(core): default unknown context windows to 200k * fix(core): only stamp known model context windows in resolver - Use knownTokenLimit() in the env-var resolver fallback so unknown models keep contextWindowSize undefined instead of being labeled 'auto-detected from model' with the generic default - Add resolver tests: known limit differing from the default (gpt-4o), unknown model stays undefined, settings value not overridden - Recalibrate the config.getWarnings default-window test for the 200K fallback - Sync the vscode-ide-companion copy of DEFAULT_TOKEN_LIMIT to 200K
…QwenLM#6388) * feat(web-shell): add token-usage analytics dashboard to Daemon Status Add a "统计 / Usage" tab to the Daemon Status page: a Today/7D/30D period toggle over the selected range's token totals and input/output/cache-read breakdown, a 12-month token heatmap (per-day tokens + cache-read tooltip, localized month labels), per-model token share, skill-call counts, and daily token/session charts. Backend: a new read-only GET /usage/dashboard daemon API backed by a core usage-dashboard service that aggregates the durable local usage history (cross-project ~/.qwen), reusing loadUsageHistory + aggregateUsage. Skill counts are threaded through the shared usage pipeline. No new instrumentation — every metric is read from data qwen-code already persists. * fix(web-shell): address usage-dashboard review feedback - cap `aggregateUsage` topSkills at 25 like topTools, so the aggregate and dashboard payload stay bounded - fix a DST drift in the heatmap grid: advance the day/month cursor by calendar day (setDate) instead of a fixed `i * MS_PER_DAY` offset - cache the loaded history once (range-independent) so toggling Today/7D/30D re-aggregates from a single disk read; split a pure `buildUsageDashboard(records, opts)` out of `loadUsageDashboard` - drop the unused per-day streak computation and the dead `daemon.usage.streak` i18n key - add debug logging to the dashboard builder and a direct `aggregateUsage`-skills unit test * fix(usage-dashboard): make the dashboard load read-only + fix cache coalescing - Make the daemon dashboard side-effect free: `loadUsageHistory` gains a `persistRebuild` option, and the route passes `persistRebuild: false`, so serving a GET never writes to `~/.qwen`. The transcript-rebuild fallback previously persisted rebuilt records (including an in-progress session), violating the read-only contract. - Fix cache coalescing on the slow path: a pending history load is now reused regardless of age (the TTL starts at settlement), so a request arriving after the TTL while the load is still pending no longer kicks off a second full load. - Tests: read-only rebuild writes nothing, `metricsToUsageRecord` copies `SessionMetrics.skills`, and a pending load is shared past the TTL.
* feat(cli): add Phase 1 workspace runtime registry Introduce the internal single-workspace runtime registry for qwen serve and wire the primary runtime through the existing server assembly without changing route schemas. Also migrate daemon log and telemetry identity to daemon-scoped values, keep workspace hash as metadata, and reject repeated explicit --workspace inputs until multi-workspace serve is enabled. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6394) Memoize daemon telemetry workspace hashes and let runQwenServe honestly accept yargs workspace array inputs while keeping internal ServeOptions single-workspace. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…guish feat from refactor (QwenLM#6369) * Initial plan * fix(triage): exclude test files from core module size gate and distinguish feat from refactor - Add anti-hallucination rule to SKILL.md preventing invented blocking policies - Stage 0 size calculation now excludes test files (*.test.ts, *.spec.ts, __tests__/) - Only production logic lines count toward the 500-line threshold - feat-type PRs touching core escalate instead of hard-blocking - Add soft large-PR advisory (non-blocking) for >1000 production lines - Update AGENTS.md to match refined policy - Clarify conventional commit matching patterns for feat/refactor detection Closes QwenLM#6365 * fix(triage): clarify core gate escalation paths * fix(triage): define generated schema exclusions * fix(triage): report core diff size in gate template * fix(triage): clarify core gate review flow --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: yiliang114 <effortyiliang@gmail.com> Co-authored-by: 易良 <1204183885@qq.com>
…enLM#6359) * fix(cli): keep model picker entries contiguous * fix(cli): account for the error box when capping model picker rows The model list's row budget didn't reserve space for the inline error message shown after a failed switch, so it could still overflow a short terminal in that state. Also cover the capping formula's untested branches (floor at very small heights, the two-row description path, the undefined-height fallback) and the DescriptiveRadioButtonSelect ReactNode description path introduced by the same change. * fix(cli): pad error-row estimate for wrapped error text errorMessageRows only counted explicit newlines, undercounting rows when the error Text wraps on narrow terminals. Add a small buffer and tighten the regression test's assertion to the exact expected value. * fix(cli): show scroll arrows and document the model dialog row budget Short terminals can now cap the model list well below its old worst case of 10, hiding most entries with no indicator that the list scrolls (unlike ThemeDialog, ApprovalModeDialog, and ArenaStartDialog, which already show scroll arrows). Enable them here too, and reserve the 2 extra chrome rows they add. Also document the fixed-rows budget so future layout changes know to keep it in sync. * fix(cli): drop model picker scroll arrows when they would crowd out entries The scroll arrows are two always-rendered chrome rows, so on dialogs too short to fit them plus a single option row they pushed the option rows past the dialog's clipped height — the picker showed arrows, title, and footer but no entries. Hide the arrows in that case and spend their rows on the list instead. Verified with an E2E height sweep (rows 14-34): at least one entry is now visible at every height and windows stay contiguous, with arrows still shown wherever they fit. * fix(cli): remove model picker scroll arrows to reclaim rows for entries The ▲/▼ indicators are two always-rendered chrome rows, and in a height-capped dialog those rows are the scarcest resource — enabling them cost two visible entries at every constrained height and required extra logic to avoid crowding out the list entirely on very short dialogs. Remove them and restore the 14-row chrome budget: the entry numbering already shows where the visible window sits in the list, and the footer hint covers navigation. Supersedes the earlier change that enabled the arrows. * test(cli): cover the max-item clamp for tall terminals
* fix(autofix): run verification before committing * ci(autofix): trigger review addressing on feedback * fix(autofix): narrow npm command allowlist * fix(autofix): address review workflow guards * fix(autofix): keep review addressing on scheduled sweep
…LM#5629) * feat(core): surface PreToolUse hook 'ask' as a TUI confirmation A PreToolUse hook returning permissionDecision:'ask' was treated the same as 'deny'. The hook fires in the execution phase (_executeToolCallBody), after the confirmation flow in _schedule has finished, so an 'ask' could only block the tool as EXECUTION_DENIED instead of prompting the user. Bounce the tool from the execution phase back to awaiting_approval when a hook asks: build a synthetic 'info' confirmation whose onConfirm routes through handleConfirmationResponse (ProceedOnce re-executes, Cancel cancels). PreToolUse keeps its "before execution" timing — only the 'ask' branch is new; 'denied'/'stop' keep deny-as-error. A non-interactive CLI or background agent cannot prompt, so 'ask' falls back to deny there. The re-execution after approval skips both the hook re-fire (no infinite re-ask loop) and the non-idempotent path-unescape prelude. A walk-away abort sets a terminal status so the turn cannot hang, and the tool span survives the bounce so it is finalized exactly once. Tests: add coverage for ask->awaiting_approval, approve->execute-once (no re-ask loop), decline->cancelled, non-interactive/background deny, walk-away abort, single span finalize, and no double path-unescape. * fix(core): handle PreToolUse 'ask' bounce edge cases from review Round 1 review of QwenLM#5629 surfaced edge cases in the bounce mechanism: - Multi-tool batch hang: a bounced tool approved while a sibling was still executing stayed stuck in 'scheduled'. attemptExecutionOfScheduledCalls now loops, re-checking for newly-scheduled bounce-approved calls after each batch drains. - Orphaned hook events: the post-approval re-execution generated a fresh tool_use_id, leaving PreToolUse(old)/PostToolUse(new) unpaired. Preserve and reuse the original id across the bounce. - ModifyWithEditor double-unescape: request.args is unescaped in place before the hook fires, so the ModifyWithEditor branch must skip its own unescape for a bounced tool (it would double-strip escaped metacharacters). - Missing signal.aborted re-check before bouncing: mirror the confirmation-phase guard so an aborted signal falls through to deny instead of flashing a confirmation nobody can answer. Tests: multi-tool-hang regression (RED before the loop fix), non-interactive STREAM_JSON and Zed bounce paths, and span-finalize assertions on the walk-away abort test. * fix(core): keep PreToolUse 'ask' gate when a sibling is auto-approved Round 2 review: autoApproveCompatiblePendingTools auto-approved every awaiting_approval tool when a sibling was approved with ProceedAlways — including tools bounced by a PreToolUse 'ask'. The bounced tool would be auto-approved and re-executed with the hook skipped (isPostAskReexecution), silently defeating the hook's confirmation gate. Exclude bounced callIds from the auto-approve filter so a hook 'ask' always requires explicit confirmation. Test: a sibling's ProceedAlways no longer auto-approves a bounced ask (RED before the filter guard). * fix(cli): preserve hook ask prompts on approval mode change * fix(core): handle PreToolUse ask edge cases * fix(core): cancel scheduled calls during ask abort drain --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
QwenLM#6371) When a file is accessed via a symlinked path (e.g., in git worktrees or monorepos with symlinked directories), conditional rules and skills keyed on the real path would fail to activate. Add resolveSymlinkAwareRelativePaths() that returns both the original and realpath-resolved relative paths, so glob patterns match either form. Resolve both the file path and projectRoot via realpath to handle macOS /private/tmp prefix normalization correctly. Make matchAndConsume() async in both ConditionalRulesRegistry and SkillActivationRegistry to support the realpath I/O. Fixes QwenLM#6356 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: ytahdn <ytahdn@gmail.com>
…ents string (QwenLM#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, QwenLM#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>
…view (QwenLM#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>
…wenLM#6401) (QwenLM#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>
…#6407) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* 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>
…wenLM#6411) Co-authored-by: Claude <noreply@anthropic.com>
* 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>
…o tool calls (QwenLM#6398) Fixes QwenLM#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).
* 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 QwenLM#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>
…wenLM#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.
…review (QwenLM#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>
* 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>
* fix(cli): smoother streaming table rendering Follow-up to the streaming table hold-back, on its own branch so the cue-removal PR (QwenLM#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 QwenLM#6345. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* 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>
… model persistence (QwenLM#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 QwenLM#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 (QwenLM#8) - Add fr.js / ja.js translations for scope keys (QwenLM#10) - Remove unused export ModelDialogPersistScope (#6) - Wrap non-interactive help text in t() with new flags (QwenLM#7) - Fix argumentHint grouping to show mode vs scope flags (QwenLM#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>
* 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 QwenLM#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 (QwenLM#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6349) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#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>
* fix(core): bound glob result collection * fix(core): lower glob collection cap * fix(core): add Claude Opus 4.6-4.8 token limits * test(core): update Claude Opus context expectation --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
…LM#6635) * feat(cli): group daemon channel workers by workspace (phase 4b) Multi-workspace `qwen serve --channel` now runs one channel worker per owning workspace instead of a single primary-bound worker. Each worker binds to its workspace's directory, daemon-workspace env marker, and effective env overlay. Channels are grouped implicitly by their configured working directory: a channel belongs to the registered workspace its resolved cwd matches, mirroring the worker's own workspace validation. Unknown, ambiguous, or untrusted targets fail fast at startup. The pidfile and daemon status grow an additive per-workspace worker list while keeping the existing single-worker fields for older readers; single-workspace daemons stay byte-identical to before. `--channel all` stays primary-only. Refs QwenLM#6378 * fix(cli): harden multi-workspace channel workers Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): close listener after channel worker startup failure Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): restore grouped channel webhooks Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): mount runtime before channel workers start Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6635) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): strengthen channel worker edge coverage Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * chore(cli): address channel review suggestions Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
) * feat(hooks): add MessageDisplay hook for mid-turn streaming Fires repeatedly as the assistant reply streams, before Stop (which only fires once at the end of the turn). Fire-and-forget, cumulative text payload, debounced (~200ms) except for the unconditional final firing. Fires from the single streaming loop in client.ts shared by the terminal UI and ACP paths. Fixes QwenLM#6488 * fix(hooks): address MessageDisplay review feedback - Chain fire-and-forget MessageDisplay requests per message_id instead of firing them fully unbounded, so a slow hook command can't pile up concurrent processes. - Gate the final flush on non-empty displayed_text and !signal.aborted, matching the adjacent Stop hook's guard. - Document why the final flush intentionally re-sends the last debounced text (is_final itself is new information). - Simplify the debounce constant's JSDoc to drop the competitor comparison. - Add tests for the mid-stream debounced flush and the rejected-request warn path. * test(hooks): drain microtasks before asserting on chained MessageDisplay calls fireMessageDisplayHook now chains per-message_id through a promise (see previous commit), so the final flush's actual messageBus.request() call lands a few microtask ticks after the generator itself finishes — the mid-stream-flush test needs to let that chain settle before asserting. * fix(hooks): flush MessageDisplay is_final on every for-await exit path The three early `return turn` paths inside the streaming loop (always-on loop-detection safety, heuristic loop detection, and the stream Error event) exited before the final MessageDisplay flush, which only sat after the loop ended normally. Hook scripts relying on is_final: true to know when to flush never received it when a turn ended via loop detection or an API error. Extracts the flush into a shared closure and calls it from all four exits (the three early returns plus the normal fall-through), instead of only the one at the bottom of the loop. Adds regression tests for all three previously missed exits, plus the two guard-coverage tests requested in review (abort suppresses the flush, a tool-call-only turn with no Content events does not fire a vacuous empty-text event). Addresses the outstanding critical review comment and the follow-up test coverage suggestion on PR QwenLM#6489. * fix(hooks): fire MessageDisplay on the ACP surface, coalesce delivery, drain is_final before turn end Addresses the three findings from the local verification report on QwenLM#6489: - ACP/qwen serve (Finding 1): the delivery logic now lives in a shared MessageDisplayDispatcher (packages/core), and Session.ts wires it into all four raw-stream loops (main prompt, Stop-hook continuation, cron tick, background notification) — these surfaces consume GeminiChat's stream directly and never enter GeminiClient.sendMessageStream, so they need their own fire sites. The daemon no longer advertises an event it never emits. - Slow-hook backlog (Finding 2): the per-message promise chain is replaced by coalescing delivery — at most one in-flight request plus one pending payload per message; newer flushes overwrite the pending slot, which is lossless because displayed_text is cumulative, and is_final is sticky. A slow hook now sees fewer, newer payloads instead of an ever-growing queue of stale ones. - Headless is_final drop (Finding 3): finish() resolves only once every enqueued payload has actually been delivered, and every exit out of the streaming loops awaits it (early returns, normal fall-through, and the enclosing finally for uncaught exceptions), so a short-lived -p process can no longer exit with the final payload still queued. As a consequence, is_final delivery now strictly precedes the Stop hook rather than racing it. Also: the failure log line carries the message_id, finish() is idempotent, the review-requested tests are added (mid-stream and final firings share one message_id; isFinal as the sole flush reason), and hooks.md gains a delivery-semantics contract covering coalescing, the drain guarantee, no is_final on cancellation, provisional displayed_text, and multiple messages per tool-using turn. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(hooks): bound MessageDisplay drain wait, fix test gaps flagged in review finish() now gives up waiting on drain after 5s (MESSAGE_DISPLAY_DRAIN_TIMEOUT_MS) instead of blocking turn teardown for up to the full 60s hook timeout, per the re-verification's S1 finding. Delivery keeps running in the background past the timeout; only the caller's wait is bounded. Also: add the config.ts bridge test for MessageDisplay field extraction (S5), and add the missing MessageDisplay/InstructionsLoaded entries to acpAgent.test.ts's HookEventName mock (S6). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(hooks): dispatch MessageDisplay is_final alongside stale deliveries, share one drain budget Round-3 review findings on QwenLM#6489: - finish() no longer queues the is_final payload behind an in-flight mid-stream delivery: the pending slot's supersession argument applies to the in-flight slot too, so the final payload is dispatched immediately, alongside the stale delivery if one is still running. is_final is handed to the hook the moment the message ends — before Stop — on every surface, and can no longer be dropped by a short-lived process exiting with it still queued (Finding 1). - The bounded drain wait is memoized: every finish() call (explicit, finally, or concurrent) shares one promise and one timer, so the teardown ceiling is MESSAGE_DISPLAY_DRAIN_TIMEOUT_MS itself, not a multiple of it (Finding 2). - hooks.md delivery semantics rewritten to match the shipped behavior, including the headless orphaned-hook caveat and the unspecified completion order between an overlapped stale execution and the final one (Finding 3). - The dispatcher mirrors its warnings to console.warn itself (stderr on headless/ACP, ink patchConsole in the TUI) in addition to the injected debug-file sink, so hitting the drain timeout is visible by default (Finding 4). - A superseded mid-stream delivery that fails after the final was dispatched no longer warns; failures during streaming still do. - New tests: finish() twice while delivery is in flight (the exact client.ts sequence), concurrent finish() calls sharing one budget, is_final overtaking a held mid-stream delivery, and drain resolving on the final delivery alone. * refactor(core): consolidate MessageDisplay finish() calls, dedupe test spy setup client.ts: wrap the turn.run() streaming loop in try/finally so messageDisplay.finish() fires once instead of at each of the three early-return sites plus the post-loop path -- matching the pattern the four raw-stream loops in Session.ts already use for the same dispatcher. message-display-dispatcher.test.ts: centralize the console.warn spy setup/teardown in beforeEach/afterEach instead of five repeated per-test try/finally blocks. No behavior change: full client.test.ts (246/246) and the message-display-buffer/dispatcher suites (24/24) pass unchanged. * docs(hooks): clarify MessageDisplay cancellation timing (round-4 nit) * test(hooks): cover the 3 untested MessageDisplay dispatch sites, fix cancellation doc wording Adds MessageDisplay is_final coverage for the Stop-hook continuation loop, the in-session cron fire, and the background-notification loop, each with a normal-completion and an abort case. Adds three MessageDisplayDispatcher edge-case tests: a delivery settling just before the drain timeout, an abort arriving after a drain wait has already started, and addChunk called after abort but before finish(). Rewords the cancellation-timing doc bullet to state the actual criterion (abort signal state when finish() runs) rather than an approximation of it. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
…bles (QwenLM#6530) * feat(web-shell): add cell value dialog on double-click in markdown tables Double-clicking a table cell opens a modal dialog showing the cell's full content with copy and close actions. The dialog clears any active selection or row details on open, supports Escape to dismiss, and click-outside to close. Adds EN/ZH i18n keys and four unit tests covering open, copy, dismiss, and state-clearing behaviour. * fix(web-shell): improve table cell dialog interactions * fix(web-shell): portal table cell dialog * fix(web-shell): block shortcuts behind cell dialog --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* fix(memory): refresh instructions after remember * fix(memory): make ACP remember refresh best-effort * fix(memory): isolate interactive remember refresh failures * fix(memory): refresh after managed memory writes * test(memory): cover interactive refresh wiring * fix(memory): harden managed write refresh * test(memory): cover refresh fallback * fix(memory): avoid duplicate refresh races * test(memory): return promise from refresh mock --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* feat(serve): expose read-only untrusted session catalogs Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(cli): address session catalog review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6717) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6717) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6717) 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>
* fix(core): make goal evaluations lifecycle-safe * fix(core): reject invalid goal judge schema * fix(core): preserve goal judge result compatibility * test(core): cover goal iteration boundary * fix(core): cap background goal deferrals * fix(core): address goal evaluation review blockers --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* fix(core): preserve managed memory during microcompaction Refs QwenLM#6487 * test(core): cover managed memory read errors --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* feat(serve): persist dynamic workspace registrations Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6716) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6716) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6716) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6716) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (QwenLM#6716) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
… workspace sessions (QwenLM#6737) POST /session/:id/model and /session/:id/approval-mode used withMutableSession, which rejects non-primary workspace sessions with a Phase 2a "primary-only" 400. Creating a session in a newly registered (non-primary) workspace in Web Shell surfaced "Set model failed" / "Set approval mode failed" error toasts. Switch both routes to withOwnerMutableSession + runtime.bridge, resolving the session's owning workspace runtime — the same pattern POST /session/:id/prompt already uses. Each WorkspaceRuntime owns a full AcpSessionBridge, so the mutation lands on the correct workspace (and approval-mode persist targets that workspace's own settings). Primary sessions are unchanged: the primary runtime's bridge is the same object as the closed-over primary bridge. Also fold the initial approval mode into the Web Shell create request (POST /session already applies it via spawnOrAttach) so a new session applies its mode atomically at spawn: one fewer round-trip, and fail-closed on the approval setting — a mode that can't be applied aborts creation instead of silently running in a different mode than requested. The model stays a best-effort follow-up because creation only accepts a modelServiceId, not the composer's plain modelId (that follow-up now works on non-primary workspaces too, via the route change above).
…6537) * feat(web-shell): render composer references in user messages * refactor(web-shell): consolidate composer tag utilities * fix(web-shell): leave custom references as text * fix(web-shell): avoid ambiguous reference chips * fix(web-shell): thread composer tag icons to messages * feat(web-shell): render user references from annotations * fix(web-shell): include inline tags in input annotations * fix(web-shell): remove duplicate composer tag icon option * fix(web-shell): forward plan prompt annotations * test(web-shell): cover composer annotation edge cases * fix(web-shell): forward split pane prompt annotations * fix(web-shell): guard malformed input annotations --------- Co-authored-by: zhanghuapeng.zhp <zhanghuapeng.zhp@alibaba-inc.com> Co-authored-by: 易良 <1204183885@qq.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* fix(core): tolerate repeated invalid model streams * fix(core): preserve invalid stream retry invariants * fix(core): use transient stream retry budget * fix(core): cap continuation protocol leak retries * fix(core): keep invalid stream retry budgets independent * fix(core): separate continuation stream retry budgets --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* fix(mcp): recover HTTP OAuth after 401 * fix(core): bound MCP OAuth recovery state
* feat(channels): add lazy session route recovery * fix(channels): harden session route persistence * fix(channels): drop malformed eager routes * fix(channels): preserve durable routes on session death * feat(cli): restore daemon channel routes lazily * fix(channels): invalidate stale route operations * fix(channels): close route lifecycle microtask gaps * fix(channels): release route invalidation metadata * fix(channels): discard invalidated daemon sessions * fix(channels): defer orphan session cleanup * fix(channels): scope orphan cleanup to bindings * fix(cli): forward daemon session discard * fix(channels): detach stale daemon clients * fix(channels): reject stale sessions promptly * test(channels): cover merged session arguments * fix(channels): update session cleanup test mock * test(channels): update Telegram session cleanup mock * fix(channels): release mismatched daemon sessions * fix(channels): release replaced daemon sessions * fix(channels): release dropped daemon sessions * fix(channels): guard lazy route recovery --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* fix(web-shell): avoid duplicate inline tag tooltips * fix(web-shell): preserve inline tooltip fallback --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
* fix(core): ignore goal judge thought parts * fix(core): guard null goal judge parts
…ksDialog (QwenLM#6748) PR QwenLM#6537 consolidated composerTagIcons.ts into utils/composerTag.ts but PR QwenLM#6589 was branched before that and re-introduced the old import path, breaking the Vite build.
Refs QwenLM#3225 Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
…6725) * feat(web-shell): show current git branch in composer toolbar * fix(web-shell): address git branch indicator review * test(web-shell): cover git branch review suggestions * test(web-shell): fix chat editor test lint --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…mands (QwenLM#6628) * feat(core): add configurable default timeout for foreground shell commands Foreground shell commands started by the agent time out after a hardcoded 120s (DEFAULT_FOREGROUND_TIMEOUT_MS). A per-call `timeout` param can raise that for a single command, but there is no way to change the default for a project or session, so users repeatedly watch long-running commands fail at the 2-minute mark. Add a `tools.shell.defaultTimeoutMs` setting that feeds the existing timeout resolution. Precedence is now: per-call `timeout` param > setting > built-in default. When the setting is unset, behavior is unchanged; a value of 0 disables the timeout, matching the existing per-call semantics. Fixes QwenLM#5838 * fix(core): add mock getShellDefaultTimeoutMs + bound defaultTimeoutMs Address review on QwenLM#6628: - Add getShellDefaultTimeoutMs to mock configs in coreToolScheduler.test.ts and toAutoClassifierInput.test.ts (ShellTool construction now reads it). - Add minimum: 0 / maximum: 600000 to the defaultTimeoutMs setting so a negative value can't reach AbortSignal.timeout(); regenerate schema. * chore(core): polish shell defaultTimeoutMs per review - shell.ts: debug-log the resolved foreground timeout (per-call vs configured default vs built-in) for observability - settingsSchema.ts: use type 'integer' for tools.shell.defaultTimeoutMs to match sibling visionBridgeTimeoutMs; regenerate settings.schema.json - config.test.ts: add loadCliConfig test asserting tools.shell.defaultTimeoutMs maps to Config.getShellDefaultTimeoutMs() * fix(core): validate shell defaultTimeoutMs and fix disabled-timeout hint Address review on the configurable foreground shell timeout: - Config: validate shellDefaultTimeoutMs at construction, mirroring visionBridgeTimeoutMs, but allow 0 (disables the timeout). Negative, fractional, or out-of-range values now coerce to undefined instead of reaching AbortSignal.timeout() via a hand-edited settings.json that bypasses schema validation. - settingsSchema: mark tools.shell.defaultTimeoutMs requiresRestart, since Config.shellDefaultTimeoutMs is private readonly with no setter, so a mid-session change cannot take effect. - shell: when the timeout is disabled (effectiveTimeout === 0), suppress the long-run backgrounding hint instead of firing it on every command over ~1s via the longRunThresholdFor floor. - shell: correct the precedence comment; 0 disables only at the settings/default level, as the per-call timeout param rejects <= 0. Add coverage for negative/fractional coercion to the built-in default and for 0 disabling the timeout without emitting the spurious hint. --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
…n narrow screens (QwenLM#6753) The Git branch chip kept max-width: 180px on narrow screens while the mode/model buttons collapse to icon-only, so inside the wrapping .toolbarLeft it claimed roughly half the row and pushed mode/model onto a second line — making the composer taller (QwenLM#6725). Within the existing @container (max-width: 699px) block, keep the leading controls on one row (flex-wrap: nowrap) and let the branch chip yield space first, truncating via its existing ellipsis down to just the icon if needed (max-width: 140px; flex-shrink: 1). The mode/model buttons stay fixed-size. Desktop (composer > 699px) is unchanged.
* feat(sdk): expose transport and query options in both SDKs
Consolidated PR covering pure SDK-side option additions:
- fork_session (--fork-session)
- max_tool_calls (--max-tool-calls)
- max_subagent_depth (--max-subagent-depth)
- agents (via initialize control request)
- include_directories (--include-directories)
- extra_args (pass-through CLI flags)
- extensions (--extensions)
- allowed_mcp_server_names (--allowed-mcp-server-names)
- mcp_servers (Python SDK, via initialize control request)
- fallback_model (--fallback-model, max 3)
- proxy (--proxy, deprecated)
- sandbox (--sandbox)
- safe_mode (--safe-mode)
- insecure (--insecure)
- worktree (--worktree)
- disabled_slash_commands (--disabled-slash-commands)
All options implemented in both Python SDK and TypeScript SDK
with validation and unit tests.
* fix(sdk): expand extraArgs blocklist and add TS SDK tests
- Expand reserved CLI flags blocklist from 3 to 34 flags, covering all
SDK-managed options and security-sensitive flags (--model, --auth-type,
--approval-mode, --insecure, --dangerously-skip-permissions, etc.)
- Add Zod refine validation for extraArgs in TS SDK (previously no validation)
- Update extraArgs JSDoc to document security implications
- Add 12 ProcessTransport tests for new CLI argument building
- Add queryOptionsSchema.test.ts with 20 validation tests
- Add createQuery.test.ts option passthrough test for all new fields
- Add Python parametrized tests for expanded blocklist
* fix(sdk): address review feedback for consolidated options
Security fixes:
- Fix --flag=value bypass: split on = before checking reserved flags
- Add missing dangerous flags: --yolo/-y, --openai-base-url, --openai-api-key,
--mcp-config, --prompt, --add-dir, --input-file, --json-schema/fd/file
- Remove ghost flags (--dangerously-skip-permissions, --allow-dangerously-skip-permissions)
Bug fixes:
- Fix Python mcp_servers key: snake_case -> camelCase (mcpServers)
- Remove duplicate agents declarations in Zod schema and types.ts
- Add maxToolCalls range validation (.int().min(-1)) in Zod schema
- Fix agents validation cross-SDK: reject empty strings in TS (matching Python)
Tests:
- Add --flag=value bypass tests (both SDKs)
- Add tests for new dangerous flags
- Add maxToolCalls range validation tests
- Add agents empty-string rejection test
* fix(sdk): add short flag aliases, fix zod validator, add forkSession validation
- Add short flag aliases (-m, -p, -i, -s, -e, -o, -c, -r) to reserved
CLI flags blocklist in both Python and TS SDKs to prevent blocklist
bypass via short flags
- Fix z.custom validator for agents: move error message from && chain
to 2nd argument so Zod produces the descriptive error on failure
- Fix TS2345: coerce split('=')[0] with ?? '' for noUncheckedIndexedAccess
- Add forkSession prerequisite validation: requires resume to be set,
matching CLI behavior that rejects --fork-session without --resume
- Remove dead agents field from TransportOptions (agents flow through
initialize payload, not transport CLI args)
- Add tests for short flags, forkSession validation in both SDKs
* fix(sdk): add --no-* negation flags, comma validation, fork session ID fix
- Add --no-sandbox, --no-safe-mode, --no-insecure, --no-worktree,
--sandbox-image, --sandbox-session-id to reserved CLI flags blocklist
in both SDKs to prevent yargs boolean negation bypass
- Add per-element comma validation for comma-joined list fields
(includeDirectories, extensions, allowedMcpServerNames,
fallbackModel, disabledSlashCommands) to prevent CLI comma-split
injection
- Add min(1) validation for extraArgs items to reject empty strings
- Fix fork_session validation to also accept continue_session (not just
resume), matching CLI behavior
- Allow session_id with resume when fork_session is True
- Fix fork session ID mismatch: generate new UUID for forked session
instead of reusing resume (source) session ID, so getSessionId()
returns the correct forked session ID
* fix(sdk): add fork session ID test assertions and mcp_servers validation
- Add assertions to forkSession test verifying sessionId is a new UUID
different from the resume value
- Add test for forkSession with explicit sessionId
- Add structural validation for mcp_servers in Python SDK to reject
non-mapping configs before sending to CLI
* fix: fork session ID discarded by Query constructor
Query.ts:97 used `options.resume ?? options.sessionId` which always
picked resume when forkSession was true, ignoring the new fork UUID
generated in createQuery.ts. Now uses fork UUID when forkSession is true.
Python SDK: query() now generates a fresh UUID for fork_session instead
of reusing the resume (source) session ID. _session_id_locked is False
for fork sessions, allowing the CLI to correct the session ID via
control responses.
* fix: mypy type error in fork_session session_id annotation
Add explicit `str | None` type annotation to session_id variable
to resolve mypy error where fork branch inferred `str` but else
branch assigns `str | None`.
* test: address review suggestions for test coverage and validation
- Add comma validation negative tests for all 5 list fields (both SDKs)
- Add maxSubagentDepth boundary tests (0, 101, 1, 100) for TS SDK
- Add from_mapping tests for all new fields and default values (Python)
- Add ProcessTransport negative test verifying flags absent when unset
- Remove --no-worktree dead code from RESERVED_CLI_FLAGS (both SDKs)
- Add --fork-session and other new flags to reserved-flags test list
- Add continue field to TS schema to match TransportOptions type
- Fix forkSession refine to accept resume OR continue (matching Python)
---------
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
) Qwen3 can return `<think>` blocks in `content` instead of `reasoning_content`, but the default OpenAI-compatible provider never enabled the existing tagged thinking parser on the response path. This wires that parser back in only for Qwen3 model names, keeping the safer GLM revert intact while restoring the intended fallback for Qwen3 responses. Constraint: Must not reintroduce the DashScope GLM buffering regression reverted in QwenLM#6248 Rejected: Enable tagged-thinking parsing for every default provider response | broader blast radius without maintainer direction Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep response-side tagged-thinking gating aligned with the Qwen3 request-side reasoning mirroring check Tested: `npm run typecheck --workspace=packages/core`; `npm run test --workspace=packages/core -- src/core/openaiContentGenerator/provider/default.test.ts`; `npm run test --workspace=packages/core -- src/core/openaiContentGenerator/converter.test.ts -t 'OpenAI -> Gemini tagged thinking content'` Not-tested: End-to-end live DashScope/Qwen API streaming against a real provider Related: QwenLM#6666
* feat(web-shell): make session sidebar configurable * test(web-shell): cover sidebar host controls * fix(web-shell): improve sidebar menu accessibility * fix(web-shell): restore sidebar menu focus
When a non-read-only tool is blocked by plan mode, the error message tells the agent to immediately call exit_plan_mode, causing premature exit before gathering enough context. The system prompt also lacks guidance on what to do when a tool is blocked. Fix: Add 'Do NOT retry this tool' and 'Pivot to read-only alternatives' to the error message, and add a 'When a Tool is Blocked by Plan Mode' section to the system prompt. The agent should first gather equivalent information via read-only tools, then call exit_plan_mode with a complete plan. Signed-off-by: Alex <alex.tech.lab@outlook.com>
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
Changes the plan mode blocked error message and system prompt to guide the agent to pivot to read-only alternatives instead of immediately exiting plan mode. When a non-read-only tool is blocked, the agent is now told "Do NOT retry this tool" and instructed to gather equivalent context via read-only tools first, then call
exit_plan_modewith a complete plan.Why it's needed
After the plan mode response format fix, the error message still implied the agent should immediately call
exit_plan_modeto unblock. This creates a negative feedback loop: the agent receives a blocked tool error, reads "Call exit_plan_mode to exit plan mode and execute this tool," and exits prematurely without gathering enough context to form a useful plan. The system prompt also lacked any guidance on what to do when a tool is blocked.Reviewer Test Plan
How to verify
npm install,write_file)cd packages/core && npx vitest run src/core/coreToolScheduler.test.ts -t "plan mode"cd packages/core && npx vitest run src/core/prompts.test.ts -t "getPlanModeSystemReminder"Evidence (Before & After)
N/A
Tested on
Risk & Scope
Linked Issues
Closes QwenLM#6763
中文说明
这个 PR 做了什么
修改了 plan mode 阻塞时的错误消息和系统提示,引导 agent 先转向只读工具收集信息,而不是立即退出 plan mode。当非只读工具被阻塞时,agent 现在会收到 "Do NOT retry this tool" 的提示,并被指示先通过只读工具收集等效上下文,然后用完整计划调用
exit_plan_mode。为什么需要
在 plan mode 响应格式修复之后,错误消息仍然暗示 agent 应该立即调用
exit_plan_mode来解除阻塞。这导致负反馈循环:agent 收到阻塞错误后,看到 "Call exit_plan_mode to exit plan mode and execute this tool",于是在收集到足够上下文之前就过早退出。系统提示也缺少关于工具被阻塞时该怎么做的指导。Reviewer 测试计划
如何验证
npm install、write_file)cd packages/core && npx vitest run src/core/coreToolScheduler.test.ts -t "plan mode"cd packages/core && npx vitest run src/core/prompts.test.ts -t "getPlanModeSystemReminder"证据(Before & After)
N/A
测试平台
风险与范围
关联 Issue
Closes QwenLM#6763