mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-25 16:44:36 +00:00
8858 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
22bb5e8b9f
|
feat(core): require an explicit user opt-in before the model launches a workflow (#9806)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* feat(core): require an explicit user opt-in before the model launches a workflow
The Workflow tool description carried judgement heuristics for orchestrating
well, but no rule about when not to orchestrate at all. Read on its own the
prose is encouragement, and a run that can dispatch up to the per-run agent
cap is a large spend to enter on inference rather than on a request.
Prepend a gate above the existing guidance: do not call the tool unless the
user asked for multi-agent orchestration, with the five forms that count as
asking under this project's own triggers -- the `workflow` keyword, the
user's own words, a skill or slash command, a named saved workflow reached
through `workflow('<name>')` or `scriptPath`, and a resume. Upstream's
`ultracode` marker is deliberately not among them: it does not exist here,
and naming it would enumerate a trigger no user can pull.
The fallback path is the load-bearing half. Without an offer-and-ask route
the model reads a refusal rule as "refuse", and a user who would have said
yes never gets asked, so the text ends by telling it to say what a workflow
would fan out over and let the user decide.
The agent cap is interpolated from `DEFAULT_MAX_AGENTS_PER_RUN` rather than
pasted, matching the rest of the description.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDuRWMhKbqEYZXbbLhn3en
* chore: keep agent-core.ts as it is on main
The merge commit's pre-commit hook ran prettier over every file the merge
staged, not just the ones this branch changes, and reformatted a type union
in agent-core.ts. main's copy does not satisfy the repo's pinned prettier
3.6.1, so the hook produced a real diff in a file this PR has no business
touching. Restore main's bytes to keep the PR scoped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDuRWMhKbqEYZXbbLhn3en
* test(core): pin workflow opt-in contract
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
|
||
|
|
bf123a375a
|
fix(goal): converge the three continuation prompts on one guarded contract (#9834)
* refactor(goal): render Goal continuation prompts from one core renderer The prompt sent when `runtime.finishTurn` schedules another Goal turn was assembled independently in three hosts: the TUI's inline array in `useGeminiStream`, and a `buildGoalContinuationParts` in each of the ACP session and the non-interactive CLI. Three copies of the same four shared lines have already drifted -- the TUI carries the anti-spoofing guard lines but no objective, while ACP and non-interactive carry the runtime continuation context but no guard lines. Upcoming work adds further variants (an "objective was edited" announcement and a budget wind-down prompt). With the text living in three places, every new variant means three edits, which is precisely how the current drift was produced. This moves assembly into `packages/core/src/goals/goal-continuation-prompt.ts`, where a variant is a case in one function and the shared prefix exists once. The two `buildGoalContinuationParts` helpers keep their names and signatures and simply delegate. This is a pure refactor: no prompt text changes. Each host still emits a byte-identical string to the one it emitted before. The existing drift is preserved deliberately and is left for a separate, behavior-changing follow-up. The new unit test pins the complete rendered string for both variants with and without verifier feedback, so any future edit to a line surfaces as a test diff; the existing host tests pass unmodified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(goal): tighten continuation renderer contract * test(goal): cover verifier feedback hosts * refactor(goal): hoist Goal continuation parts builder into core (#9581) * fix(goal): converge the three continuation prompts on one guarded contract Every automatic Goal turn now renders the same prompt in every host: the runtime-supplied goalId, revision and objective as an escaped JSON data block, framed as untrusted task data, under both anti-spoofing guard lines, followed by a line stating the block supersedes any earlier objective in the conversation. Before this change the drift ran the wrong way. ACP and non-interactive interpolated the raw objective into a synthetic user-role turn carrying neither guard line; the TUI carried both guard lines but dropped the objective, so the host that guarded most gave up information and the two that guarded least were the exposed ones. None of the three escaped the objective, so objective text shaped like a tag could break out of the surrounding prompt. The prompt input collapses to a single flat shape, so the variant discriminant and its unreachable-default arm are gone. `<`, `>` and `&` are escaped inside the serialized JSON so an objective cannot close the data block or open one of its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
ac109c9dd3
|
fix(core): relax uniqueItems in function schemas (#9869)
* fix(core): relax uniqueItems in function schemas * fix(core): preserve schema map keys when relaxing tools --------- Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> |
||
|
|
02f5214939
|
test(cli): skip unreadable-ledger run-ledger test under root (#9913)
* test(cli): skip unreadable-ledger run-ledger test under root The test simulates an unreadable ledger with chmod 000, but root bypasses permission bits, so the read succeeds and the append goes through, failing the test on every root run. Skip it there since the property is untestable without DAC enforcement. Fixes #9909 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): extend unreadable-ledger run-ledger skip to win32 --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
1fffa5108d
|
fix(acp-bridge): Disable permission timeout by default (#9933)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* fix(acp-bridge): disable permission timeout by default Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * chore: regenerate settings schema Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(acp-bridge): fix stale timeout comment Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
65c2bb01c0
|
fix(ci): narrow serve-ab's self-hosted wipe to the A/B checkout dirs (#9228)
* fix(ci): narrow serve-ab's self-hosted wipe to the A/B checkout dirs 'Wipe stale workspace before checkout' deleted the whole shared workspace including the root .git, forcing the next job on that runner (e.g. a fetch-depth: 0 review job) to re-download the full ~900 MB of history from github.com. On the ECS pool's slow link that stalls checkouts for 20+ minutes and the fetches drop mid-pack often enough to read as hung runners (2026-08-15: 20 orphaned tmp_pack files, ~6 GB, across 10 runners; one checkout re-downloaded 890 MB in 19m45s). serve-ab only builds inside its own head/ and base/ checkouts and never reads the workspace root, so removing just those two dirs keeps the anti-bleed guarantee without destroying the shared object store. The ci-runner-routing pin now asserts the narrow scope and fails on a whole-workspace wipe regression. * test(ci): derive serve-ab wipe pin from the checkout paths (#9228) Address review suggestions: the wipe targets are now derived from the actions/checkout steps, and the wipe must be exactly one executed (non-comment) rm line covering exactly those paths. Renamed checkout paths, appended whole-workspace wipes, and commented-out or echo'd rms now all fail the suite, while the reverted find-form still does. * test(ci): pin the serve-ab wipe's full executed script and step order (#9228) * fix(ci): match serve-ab's wipe step name and pin to its narrowed scope (#9228) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): exclude the shared root .git from serve-ab's self-hosted wipe (#9228) Replace the head/base-only rm with the exclusion-based find suggested by the R5-3 review: everything is wiped except the shared root .git, which closes the recurring materializer-coverage class (R4-4/R5-1/R5-2/R5-3) at the layer that owns it — anything landed outside .git is removed by the next run's wipe, no matter how it was materialized. This removes a strict subset of what main's whole-workspace wipe removes, so it cannot regress base behavior, while keeping the .git whose destruction forced ~900 MB re-fetches on the ECS pool's slow link. The pin now also locks the execution chain (shell wrapper, no continue-on-error, no BASH_ENV at step/job/workflow level) per R5-4, and the obsolete clone-coverage scan is deleted. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): keep only a real .git and scrub its exec state in serve-ab wipe (#9228) * fix(ci): follow a symlinked workspace root in the serve-ab wipe (#9228) * fix(ci): defang the worktreeConfig bypass and anchor the serve-ab wipe scrub (#9228) The kept-.git wipe tail had two measured holes: extensions.worktreeConfig activates .git/config.worktree, a second local file that git config --local neither lists nor unsets, so a planted core.hooksPath survived the allowlist sweep; and after the heal unlinks a symlinked workspace root the step's CWD still is the link's target, so the CWD-discovered scrub wrote outside the workspace. Add qwen-triage's hardened defang pair (delete config.worktree, unset the extension) and anchor every git call to $WS/.git. Both findings reproduced locally before the fix; both suites pin the new lines and are mutation-tested. --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
f7b936630f
|
fix(core): reject malformed Anthropic tool arguments (#9013)
* fix(core): reject malformed Anthropic tool arguments Validate streamed tool input as protocol JSON and release tool calls atomically only after a trustworthy message stop. This prevents truncated arguments or open parallel tool blocks from leaking executable calls into retry consumers. * fix(core): preserve max-token recovery for tool truncation * fix(core): preserve interrupted Anthropic tool calls * fix(core): recover empty max-token responses * refactor(core): share stream retry predicates * Merge main into tlysanhuo/fix-anthropic-tool-json --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: yiliang114 <effortyiliang@gmail.com> |
||
|
|
95bdd46241
|
fix(config): accept output.format "stream-json" in the settings schema (#8966)
* fix(config): accept output.format "stream-json" in the settings schema The runtime already reads and honors output.format: "stream-json" from settings.json (normalizeOutputFormat -> OutputFormat.STREAM_JSON), and it is a documented --output-format choice, but the settings schema listed only text and json. The VS Code companion applies that schema to every .qwen/settings.json, so it flagged a valid, working config as invalid. Add stream-json to the source schema and regenerate the shipped settings.schema.json. Same schema/runtime drift class as #8752. Closes #8965 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(config): bind output.format schema values to OutputFormat and document stream-json Apply the review's non-blocking suggestions: - Schema options now use the OutputFormat enum constants the runtime's normalizeOutputFormat accepts, so the settings schema cannot silently drift from core. - The full enum is pinned in the test (toEqual, sibling-test pattern) instead of a toContain probe. - The format description — schema, regenerated VS Code schema, and the settings reference table — now notes that stream-json makes runs started with a prompt non-interactive (headless), and the docs table lists stream-json as a possible value. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01US2APQw84vvQZZ4pZaKtzn * test(cli): add OutputFormat to core mock factories that reach settingsSchema settingsSchema.ts now reads OutputFormat at module load, so the two test files that mock @qwen-code/qwen-code-core with a hand-built factory and transitively import it need the enum in the mock, matching how they already mock ApprovalMode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(config): derive the output.format test pin from the enum and document the argv-only gates Address the round-2 review: - The test pins the schema options against Object.values(OutputFormat), so a format added in core fails the test until the schema and the regenerated JSON follow; the schema comment now states exactly that instead of overpromising drift protection from the binding alone. - The description, regenerated schema, and docs table note that flags validated at argv parse time (--include-partial-messages, --input-format stream-json) still require the explicit --output-format stream-json flag, since those yargs checks run before settings are loaded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(cli): cover settings-driven stream-json output and name the flag in the docs note Address the round-3 suggestions: a config test now exercises output.format stream-json arriving from settings through loadCliConfig, and the docs table names the --output-format stream-json flag the argv-time checks require, matching the schema description. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(cli): pin argv-over-settings output format precedence with differing values The existing precedence test used the same value on both sides, so an inverted merge passed the suite. The new case sets settings stream-json against argv text and asserts text wins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(test): match the enum-pin comment to the order-sensitive assertion Apply the maintainer review nits: the comment now says array-derived, order included, which is what toEqual checks, and the precedence test drops a comment that restated its name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2dbe806204
|
docs(sdk): fix query timeout example signature (#9867)
Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
c216584e01
|
docs(core): fix goal judge timeout unit (#9861)
Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> |
||
|
|
703bb7a5cf
|
docs(core): fix read file paging default comment (#9863)
Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
772670ddb5
|
feat(skills): add find-simplifications sweep skill (#9384)
* feat(skills): add find-simplifications sweep skill Adds a repo-scoped skill for a periodic, evidence-first sweep of the codebase for surface that no longer has a consumer: dead files and components, orphaned locale keys, exports nothing calls, scaffolding whose feature left. It fills a gap between two things that already exist. The bundled /simplify is anchored on a diff, so it cannot see surface that accumulated across releases; /repo-hygiene targets defects and explicitly bans "cleaner / more modern / more consistent" edits. Neither covers code that is correct but that nothing needs. The design is deliberately conservative. A run's deliverable is a comment on a tracking issue, not a pull request, and a PR follows only for a candidate a maintainer has said yes to, one candidate at a time. Everything reachable from the core package's exports map, every settings key, and every protocol shape is report-only, because no grep inside this repository can prove those have no consumer. Three documents: the charter, boundaries and recurring-run design in SKILL.md; the survey phase, taxonomy, proof protocol and worked examples in references/survey.md; the landing checklist, verification table and CI blind spots in references/land.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): correct find-simplifications review findings (#9384) * fix(skills): correct find-simplifications review findings, round 4 (#9384) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(skills): correct find-simplifications review findings, round 5 (#9384) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(skills): close the two out-of-repo reachability holes in the proof protocol R6-1: the Landable catch-all row decided landability by in-repo import reachability alone, so surfaces whose consumers live outside the repo passed all eight gates with a complete-looking evidence package. Two measured entrances: `packages/zed-extension` is a store-shipped `extension.toml` plus LICENSE/SVG/README with no in-repo importer, and `.github/` holds 52 workflow files with 0 in-repo `uses: ./.github/workflows/…` references — a live one such as `docs-page-action.yml` cleared every gate. Both are now excluded from the catch-all row and carry their own Report-only rows, alongside `packages/vscode-ide-companion` and `packages/chrome-extension`, which sit in the same family and survived only through incidental grep hits. R6-2: proof step 3 — the protocol's only published-surface gate — enumerated core/audio-capture/channels only, stopping two packages short of the Territory table's own "out-of-repo consumers" row. It now covers `packages/sdk-*` and `packages/acp-bridge` (`release-sdk.yml:297` npm-publishes sdk-typescript with `--access public`; `release-sdk-python.yml:346` ships to PyPI; `release-sdk-java.yml:206` deploys to Maven), and names the Territory table as the authoritative list so the two cannot drift apart again. Every citation re-measured on this branch: zed-extension holds exactly four files, `.github/workflows` holds 52 files with 0 self-referencing `uses:`, and all three release workflows publish at the cited lines. * docs(skills): carve out the three out-of-repo-consumer surfaces, and fetch before landing R7-1: the Landable catch-all row decided landability by in-repo import reachability alone, so three surfaces whose consumers live outside the repo fell into it with no Report-only row of their own: - `packages/webui` — npm-published under its own name (`publishConfig.access: public`, no `private` flag), so registry consumers are real; - `packages/core/vendor/**` — listed in `packages/core/package.json` `files` and copied into the CLI bundle by `scripts/copy_bundle_assets.js:284-304`. Its only runtime consumer, `getBuiltinRipgrep()` (`packages/core/src/utils/ripgrepUtils.ts:164-175`), assembles the path from segments, so a grep for the literal path returns zero hits and the consumer proof passes vacuously — while the `rg` binaries have zero import edges by definition, making them prime slice-3 whole-file-orphan candidates; - `packages/web-shell` — copied into the bundle (`copy_bundle_assets.js:389-397`) and served to browsers by `qwen serve`; `scripts/prepare-package.js` gates the release on its dist. Add a Report-only row for each, name them in the catch-all row's exclusion list so the two cannot disagree, and add the `@qwen-code/qwen-code` tarball itself to the rationale paragraph's published-surface enumeration. R7-2: the land phase never fetched before cutting the branch, so §1's re-verification could re-certify the survey's own base. `git checkout -b` succeeds against a stale cached ref exactly as `git worktree add` does — the failure mode `references/survey.md` § 0 already documents and guards with `git fetch origin || exit 1`, which §1's "steps 2 through 5" cross-reference excludes. Since assent routinely arrives days or weeks after the survey, that drift is the design norm: a consumer that landed on main in the interim stays invisible, every step re-passes, and the squash-merge breaks it. Require the fetch in the branch precondition, and say in §1 that the branch base is current `origin/main` only because of it. Docs-only change (3 markdown files, no code); mutation verification does not apply. Every factual claim added was re-verified against the tree: `npm` manifests for the publish flags, `copy_bundle_assets.js` for the bundle copies, `ripgrepUtils.ts` for the segment-assembled path. `prettier --check` clean (the Territory table re-padded on write). * fix(skills): correct find-simplifications review findings, round 9 (#9384) * fix(skills): correct find-simplifications review findings, rounds 10-11 (#9384) * fix(skills): correct find-simplifications review findings, round 12 (#9384) * fix(skills): correct find-simplifications review findings, round 13 (#9384) * fix(skills): correct find-simplifications review findings, round 14 (#9384) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(skills): correct find-simplifications review findings, round 15 (#9384) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
24db7f6ef2
|
feat(review): say when the approach, not the patch, is the open question (#9340)
* feat(review): say when the approach, not the patch, is the open question Every finding /review emits is anchored to a `file:line` in the current diff. That is what a finding is — and it means a review can report where an approach leaks, but never that a different approach would retire all of the leaks at once. Measured: one change to `extractAndStripMeta` took three attempts across two PRs. #9097 (3 rounds, 18 findings) added a timeout to the vm call; #9136 (6 rounds, 56 findings) moved the walk inside the vm and ended up spawning a child process per call, growing 228 -> 920 source diff lines. #9325 landed it in one commit by not evaluating the literal at all. All 74 findings were individually correct, and every one of them went away with the mechanism. The signal was already there and filed as the wrong kind of thing: `did not converge within the reverse-audit round cap` appeared four times across the two PRs, as a coverage gap — "we did not finish looking" — rather than as a conclusion about the change. Nothing was responsible for reading it as "stop patching". Add one advisory paragraph, and one clause on the terminal verdict line, when a non-Approve round is past the round threshold AND its source diff has grown at least 3x since the review first measured it. This round's round-cap stop rides along as corroborating text when present; it is never a trigger on its own. It is deliberately not a finding. Findings are what the autofix loop consumes, and that loop patching each finding in turn is the pattern being interrupted — a finding here would be fixed rather than read. It addresses the human deciding what happens next, so it is a body paragraph and a verdict-line clause, it adds no cap, and it never moves the event. The baseline is a baseline, not the previous round's size: 228 -> 920 across six rounds is ~1.3x per round, which no per-round delta would notice, but 4.0x cumulatively. `Ledger.src0` records the first measurement and is carried forward unchanged, so a diff that later shrinks cannot rewrite its own baseline. It is the one marker field that survives truncation — the ruling that withholds an anchor from a partial finding list does not extend to a measurement of the diff. Known limits, documented rather than papered over: it cannot see across pull requests, so the three-attempt shape that motivated it would have fired only on a second forgeable persisted counter; and it is retroactively blank, staying silent until a PR has posted two rounds after this ships. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(review): suppress approach signal for downgraded approvals * fix(review): measure approach growth over full diff * fix(review): validate approach signal evidence * fix(review): pin approach-signal boundaries and validator coverage Round-5 review findings: boundary tests for the round threshold, growth factor, and source-diff floor; the round-cap corroborating clause and its zh rendering; src0 survival through the pr-context persist seam and the incremental marker carry-forward; artifact validator refusal/absence tests for approachSignal; design doc firing list names the pre-cap verdict. * fix(review): clamp the approach signal's round at the ledger cap (R9-1) The signal computed its displayed round with an unclamped `prevRound + 1` while the ledger marker stamp and the deferred-suggestions clause both clamp with `Math.min(prevRound + 1, LEDGER_MAX_ROUND)`. `parseLedger` accepts `round == LEDGER_MAX_ROUND`, so a side file at the cap is representable and carries forward: one composed body announced "⚠️ Round 10001" beside a marker stamping `"round":10000`, and the terminal verdict line printed 10001 too — the doc comment in this same diff claims all three consumers cannot disagree "at the cap included". The new test pins the cap for the third consumer, mirroring the existing deferred-clause cap test; mutation-verified that reverting the clamp turns it red with `round: 10001`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
d128998779
|
fix(core): accept quiet post-tool-result completions after retry exhaustion (#9196)
* fix(core): accept quiet post-tool-result completions after retry exhaustion (#9026) Models that legitimately end a turn silently after a tool result (valid finish reason, no visible content) trip the NO_TOOL_RESULT_PROGRESS guard; the #7039 retry budget gives them four identical retries and then the error aborts the whole run — the dominant failure mode across ~390 headless rollouts on GPT/Gemini-family models (291/96 hits vs 0 on Qwen). Keep the retry-first behavior and, once the budget is exhausted, accept the quiet completion instead of re-throwing: the MAX_TOKENS variant stays fatal, a placeholder keeps user/model alternation well-formed when the accepted turn produced nothing, and the JSONL record carries the same text as history. Explicit per-attempt one-shot flag; continuation streams share the same acceptance through the shared retry wrapper. * fix(core): scope quiet completion retry flag per attempt * test(core): pin quiet completion retry edges * fix(core): close round-1 findings on quiet-completion acceptance (#9026) - consume the one-shot acceptance where the retry outcome is rescheduled, not where the attempt starts: re-arm the flag in the rate-limit / transport-replay / transport-continuation / reactive-compression continue branches whenever either invalid-stream budget is exhausted, and arm on any budget-exhausting invalid-stream type (the accept gate independently requires a tool-result continuation with no visible progress and a non-MAX_TOKENS/SAFETY/RECITATION finish) - keep SAFETY/RECITATION-blocked quiet turns fatal: content-filtered continuations (OpenAI content_filter / Anthropic mapped to SAFETY) must not end silently with no user-visible signal - record accepted quiet turns from one source: hoist acceptedTurnParts and build the JSONL record from the same parts history keeps, so an accepted turn whose only parts are non-text (inlineData/fileData) no longer desyncs transcript from history on --resume - fatal-MAX_TOKENS test: attach the rejection assertion before advancing fake timers so the file's test command no longer exits 1 on an unhandled rejection - regression tests: armed attempt surviving a transport replay, a 429, and a mixed final error type; inlineData-only accepted turn recording; SAFETY quiet turns staying fatal * fix(core): close round-3 findings on quiet-completion acceptance (#9026) R1-1: the invalid-stream retry branch now rearms the one-shot quiet acceptance like the other four rescheduling paths, so an armed attempt that fails with PROTOCOL_TAG_LEAK no longer strands the acceptance. Arming is keyed to the transient bucket only: quiet completions surface as a transient-type error, so a tag-leak-only budget exhaustion can no longer arm prematurely and bypass the #7039 retry-first invariant. R2: the accept gate keeps the whole content-filter finish-reason family fatal (SAFETY/RECITATION/BLOCKLIST/PROHIBITED_CONTENT/SPII and the IMAGE_* variants), mirroring mapGeminiFinishReasonToOpenAI's content_filter grouping; the constants move into genai-compat's FinishReason alongside MAX_TOKENS. Also drops the dead contentText placeholder write (acceptedTurnParts is the single source for the JSONL record and history push), parameterizes expectStreamExhaustion so the fatal tests reuse it (clearing the vitest/valid-expect lint failures), hoists chatWithRecorder to the shared scope, pins RECITATION via it.each, and adds regression tests for the tag-leak rearm, tag-leak-only no-arm, and the armed-continuation rearm site. * fix(core): keep IMAGE_OTHER fatal in the quiet-completion gate (#9026) mapGeminiFinishReasonToOpenAI also groups IMAGE_OTHER into content_filter; include it in CONTENT_FILTER_FINISH_REASONS so a native-route IMAGE_OTHER block cannot be silently accepted either. * fix(core): map Anthropic refusal stop_reason into the content-filter family (#9026) R6-1: mapAnthropicFinishReasonToGemini had no entry for Anthropic's refusal stop_reason, so it fell through to FINISH_REASON_UNSPECIFIED — defined (passes the NO_FINISH_REASON throw), not MAX_TOKENS, and not in CONTENT_FILTER_FINISH_REASONS. Once the retry budget was spent, the armed attempt therefore accepted a provider refusal as a quiet "(empty content)" completion, masking the safety decision the gate was written to keep fatal. Map refusal to SAFETY alongside content_filter so the existing content-filter family keeps it fatal. Regression tests: converter refusal-to-SAFETY mapping (mutation- verified) and a geminiChat armed-attempt test asserting NO_TOOL_RESULT_PROGRESS exhaustion with no placeholder turn recorded. * fix(core): fail closed on unknown quiet finish reasons --------- Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com> |
||
|
|
d1cfd87683
|
feat(review): promote language-pitfall and wrapper/proxy checks out of Agent 1a (#9805)
* feat(review): promote language-pitfall and wrapper/proxy checks out of Agent 1a (#9788) Split the two checks folded into Agent 1a's line-by-line brief into dedicated Step 3A roles at high effort: Agent 1d (language-pitfall scan, always) and Agent 1e (wrapper/proxy routing, rostered when the plan's wrapperSignal is true — a capture-time vocabulary heuristic that fails safe: only an explicit false keeps it out, so version-skewed plans still owe the check). The roster, check-coverage and agent-prompt all read the gate from the plan, so a run that skips either agent is named. Briefs, SKILL.md, and the user-facing code-review doc updated; 1a keeps its walk minus the two clauses. * fix(review): address round-1 feedback on the 1d/1e split (#9805) * fix(review): address round-2 feedback on the 1d/1e split (#9805) * fix(review): address round-3 feedback on the 1d/1e split (#9805) * fix(review): address round-4 feedback on the 1d/1e split (#9805) * fix(review): address round-5 feedback on the 1d/1e split (#9805) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
37cedea5b2
|
feat(computer-use): replace built-in tools with bundled skill (#9856) | ||
|
|
3892ca32ca
|
fix(core): map OpenAI-compatible finish_reason case-insensitively (#9884)
OpenAI-compatible gateways that front Gemini backends can return the Gemini-native finish_reason spelling in uppercase (`STOP`, `MAX_TOKENS`). The previous case-sensitive lookup mapped these to FINISH_REASON_UNSPECIFIED, silently disabling MAX_TOKENS truncation recovery. Fold the reason to lowercase before lookup and add an explicit `max_tokens` alias for the Gemini-native spelling. Resolves #9882 |
||
|
|
6a21c436d6
|
test(core): align lazy generator vertex test (#9888)
Use an interface-supported method to exercise lazy Vertex initialization after the content generator interface was narrowed. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> |
||
|
|
b5aec6691e
|
fix(serve): Canonicalize Live task bridge session IDs (#9819)
* fix(serve): Canonicalize Live task bridge session IDs Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(serve): Pin canonical Live owner lookup Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): harden mixed-case live session routing Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
b4a54a9f05
|
fix(ci): move undeletable workspace residue aside instead of leaving it (#9868)
A leftover the pre-checkout sweep cannot delete does not just fail the job that finds it — it poisons the checkout of every later job scheduled onto that runner. Measured on run 32621267802: residue from a review probe (`.qwen/tmp/review-pr-9748-scratch-verify-…/probe-ws/.qwen/tmp/review-pr-666`) survived on one shared-pool member, and two unrelated PRs then died at Checkout with the same EACCES on the same path. The sweep's own steps had reported success: their ladder ends at a warning, so a workspace it cannot repair is handed to actions/checkout unchanged. The recovery the ladder was missing is a rename. Unlinking an entry needs write permission on the directory holding it — exactly what foreign-owned residue denies on a pool member without passwordless sudo — while renaming needs it only on the two parents, and the workspace root is always the runner's own. So a tree that defeats rm, chmod, and chown still moves out of the way, and the checkout finds nothing to trip on. The destination sits next to the workspace so the rename cannot cross a filesystem and degrade into copy-then-unlink, and the warning names it, because the tree still needs a human. Reproduced in a Linux container with real foreign ownership (residue owned by root, workspace by the runner user, sudo unavailable): before, the step warns `leaked .qwen`, the tree stays, and a checkout-style wipe fails with `Permission denied`; after, the tree is quarantined, the wipe succeeds, and the warning points at where it went. |
||
|
|
43d46be912
|
refactor(core): shrink the content generator interface (#9676)
* refactor(core): shrink content generator interface * refactor(core): remove orphaned request-tokenizer estimator cluster Removing countTokens from both providers deleted the last production consumers of RequestTokenEstimator. Delete the orphaned cluster: requestTokenizer.ts (330), imageTokenizer.ts (534), types.ts (36), the directory barrel (11), and both test files (608 lines). Also drop the inert vi.mock of requestTokenizer.js left in client.test.ts and the stale dimension-extractor cross-reference in review/lib/assets.ts. textTokenizer.ts and supportedImageFormats.ts stay: converter.ts, pdf.ts, and fileUtils.ts still consume them and the core barrel re-exports them. * docs(design): sync lazy-google-genai-loading record with shrunk interface countTokens and useSummarizedThinking no longer exist on ContentGenerator, so the design record for the lazy-wrapper architecture must not keep advertising them: list the three remaining shared async operations, drop the useSummarizedThinking sentence and the summarized-thinking item from the consumer audit and Verification section, and add a dated note recording the interface shrink from PR #9676. * ci: record cd-cua-driver.yml size growth in .size-baseline Same latent main-side violation as fixed in #9682: #9587 grew the workflow without a baseline update; record the new size as the check message directs (precedent #9747). * docs: finish scrubbing tokenizer references after estimator-cluster removal Follow-up to 0ee17632c7/1871bb5b81 (review round 2): - supportedImageFormats.ts header and getSupportedImageFormatsString doc no longer describe a tokenizer decode/metadata-extraction stage; the list is now documented as the vision-input acceptance list, with token accounting noted as the flat DEFAULT_IMAGE_TOKEN_ESTIMATE. - web-shell-image-drag-and-drop.md's BMP rationale no longer claims ImageTokenizer parses BMP dimensions; dated sync note added stating BMP support rests on SUPPORTED_IMAGE_MIME_TYPES plus converter passthrough since PR #9676. * docs: drop tokenizer from the BMP test-plan line Follow-up to 18f08c0924: the test plan still required converter/tokenizer focused tests for image paths; the image-tokenizer estimator cluster was removed in PR #9676 (text tokenizer is unaffected and out of scope here). |
||
|
|
6420d69d14
|
test(acp-cron): kill whole process tree on cleanup to stop ENOTEMPTY flakes (#9815)
* test(acp-cron): kill whole process tree on cleanup to stop ENOTEMPTY flakes The recurring cron job under QWEN_CODE_TEST_CRON_FAST can drop a fresh file into the fake QWEN_HOME while rmSync walks it, failing cleanup with ENOTEMPTY. The direct agent.kill() only signals the CLI process; grandchildren spawned by the CLI keep running and can still write. Spawn the CLI in its own process group and signal the group during cleanup (taskkill /T on Windows). Also widen the rmSync retry window. Same flake tracked by #9766, #9643 and #9555. * test(acp-cron): kill whole process tree on cleanup to stop ENOTEMPTY flakes The recurring cron job under QWEN_CODE_TEST_CRON_FAST can drop a fresh file into the fake QWEN_HOME while rmSync walks it, failing cleanup with ENOTEMPTY. The direct agent.kill() only signals the CLI process; grandchildren spawned by the CLI keep running and can still write. Spawn the CLI in its own process group and signal the group during cleanup (taskkill /T on Windows). Replace the widened rmSync built-in retries with fresh-walk retry attempts. Same flake tracked by #9766, #9643 and #9555. |
||
|
|
e0d933b23e
|
refactor(core): make derived Config ownership explicit (#8100)
Some checks failed
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (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
* refactor(core): define derived config ownership * docs(core): align derived config ownership scope |
||
|
|
27285a5243
|
refactor: centralize approval mode contracts (#9796)
* refactor: centralize approval mode contracts * fix: align Python SDK import grouping * test: restore approval mode exports in CLI mocks * fix: close approval mode drift gaps * test(cli): preserve core exports in serve mocks * fix: close approval mode review gaps * test(cli): complete permission request fixture * test(sdk): match approval mode route * test(approval): close review coverage gaps * test(sdk): cover approval mode global scope path |
||
|
|
a60cbbc54a
|
refactor(core): make utils/ a leaf layer (#9778)
* refactor(core): make utils/ a leaf layer Eliminate every runtime (value) upward import from packages/core/src/utils production modules so utils/ can become a leaf layer with no runtime dependency on the rest of core. Two mechanisms, no behavior change: - Relocate domain-coupled modules out of utils/ into their owning module (agents, config, core, memory, services, tools), and move generic constants/types that live elsewhere into utils/. All `git mv` moves keep history; every import that pointed at a moved file is rewritten. - Extract the remaining value imports as small leaf modules inside utils/ (AuthType, isTool, ToolErrorType, DEFAULT_QWEN_MODEL) and re-export them from their original owners so cross-package consumers are unaffected. doesToolInvocationMatch moves into shell-utils, its only production consumer. Only type-only imports now cross the utils/ boundary. The two deferred inversions in debugLogger (Storage, getTraceContext) are stateful and left for a follow-up. * chore(core): enforce utils/ leaf layer with lint rule Add architecture/no-core-utils-upward-import, which flags runtime (value) imports that leave packages/core/src/utils. Type-only imports, sibling utils imports, and external package specifiers stay allowed; the two deferred debugLogger inversions (config/storage, telemetry/trace-context) are carried on an explicit allowlist. Enable the rule as an error on core sources and cover it with Linter-based tests. * fix(core): restore iconv-lite tree-shaking for sync-file-encoding The utils leaf-layer refactor moved sync-file-encoding from utils/ to services/, but the esbuild tree-shake plugin still matched the old ./utils/ specifier, so its sideEffects:false marker no longer applied and the ACP startup closure regained a static iconv-lite import. Point the onResolve filter at the new ./services/ path. * fix(ci): catch stale integration imports earlier * fix(core): close utils boundary review gaps * fix(core): close self-reference boundary gaps * ci: re-trigger after self-hosted runner checkout EACCES |
||
|
|
5b3830b46c
|
feat(vscode-ide-companion): adopt WebShell transcript as the default timeline (#9719)
* feat(vscode-ide-companion): reuse WebShell transcript UI behind experimental flag
Bridge ACP session/update notifications into the shared SDK daemon transcript reducer and render the result with the WebShell transcript component, gated on qwen-code.experimental.webShellTranscript (default off).
The WebShell renderer and its heavy transitive dependencies (echarts, mermaid, shiki, codemirror, katex) are lazily loaded via esbuild code splitting, so the default configuration keeps the ~700KB webview bundle unchanged.
* fix(vscode-ide-companion): grant wasm-unsafe-eval for shiki WASM when WebShell transcript enabled
* feat(vscode-ide-companion): adopt WebShell transcript as default timeline
Drop the experimental flag and the legacy MessageList renderer. The companion timeline now always renders through the shared WebShell transcript component, fed by ACP session/update notifications via the SDK daemon transcript reducer (lazy loaded through esbuild code splitting).
The flag-gated wiring is removed: the qwen-code.experimental.webShellTranscript setting, the conditional CSP/body attribute in WebViewContent, and the legacy MessageList path in App.tsx (~850 lines). The webview CSP now grants wasm-unsafe-eval unconditionally for Shiki's Oniguruma WASM.
* fix(vscode-ide-companion): reset WebShell transcript state on session switch
The experimental useAcpTranscript hook only consumed transcriptUpdate
messages, so its reducer state survived session boundaries. When the
extension switched sessions it kept the webview mounted and replayed the
newly-selected session through ACP, causing the previous session's blocks
to merge with the new replay (e.g. user text "alpha" from session A leaked
into session B as "alphabeta").
Reset both the reducer state and the rendered blocks on the same
boundaries the legacy message flow uses: qwenSessionSwitched (sent before
the ACP replay of the selected session) and conversationCleared (new
session). Adds a regression test that replays two sessions with a switch
between them.
* fix(vscode-ide-companion): harden WebShell transcript session boundaries
- reset the transcript on `conversationLoaded` too, closing the same
cross-session leak the previous commit fixed for `qwenSessionSwitched`
and `conversationCleared` (agent reconnect posts only this boundary)
- track the active session id and drop late `transcriptUpdate` frames
whose `sessionId` no longer matches, so a previous session's trailing
frames cannot contaminate the next session's timeline
- seed the transcript from cached messages carried by
`qwenSessionSwitched` so offline restores and load-failure fallbacks
render their history instead of a blank timeline
- dispatch `assistant.done` on `streamEnd`/`sessionLoadComplete` so the
final assistant/thought block of a turn (or history replay) does not
stay `streaming: true` forever
* fix(vscode-ide-companion): adopt live ACP session id after load-failure fallback
* fix(vscode-ide-companion): echo user prompt into WebShell transcript
* fix(vscode-ide-companion): keep WebShell transcript expanded and clear of the composer
* fix(vscode-ide-companion): surface local error and interrupt notices in the transcript area
* fix(vscode-ide-companion): restore file-link opening from the WebShell transcript
* fix(vscode-ide-companion): restore contributed copy commands for the WebShell transcript
* fix(vscode-ide-companion): add localOnly marker to TextMessage state type
* fix(vscode-ide-companion): restore /insight progress card and report link in the transcript UI
* fix(vscode-ide-companion): finalize in-flight tools on timeout and pin session-switch seeding guard
Map streamEnd reasons timeout/session_expired onto the reducer's error reason so abandoned mid-tool turns no longer spin forever (ceuI). Add qwenSessionSwitched cases with no messages field and an empty cache array; the no-messages case fails when the seeding guard is forced true, pinning its false side (ceuN).
* fix(vscode-ide-companion): remove unreachable editMessage backend and dead submit options
The user-message edit/rewind UI was dropped in the WebShell-transcript migration, leaving editTargetTurnIndex/onSubmitted options in useMessageSubmit and the full editMessage/rewind flow in SessionMessageHandler unreachable. Remove the dead options, the editMessage dispatch case, the rewind/snapshot flow with its recovery branches, and their tests (R1-8 direction b).
* fix(vscode-ide-companion): drop write-only loadingMessage bookkeeping
The waiting-message renderer was removed with the WebShell transcript migration and the user prompt is echoed into the timeline at send time (
|
||
|
|
9ef32c5f3c
|
fix(core): make team shutdown a leader-only tool (#9401)
* fix(core): make team shutdown a leader-only tool send_message carried an optional single-value enum `type: ['shutdown_request']` described as "structured message type for control flow". Models filled it while composing an ordinary report; the call was then rejected leader-only and the report content was discarded, leaving the teammate retrying a report the leader never received. Split control from content at the tool boundary rather than validating the field harder. `type` is removed from send_message entirely, and shutdown becomes request_shutdown, which createToolRegistry skips for subagent-context registries — so a teammate has no declaration for it and cannot emit the call at all, instead of emitting one and being refused. The mailbox wire format is unchanged: sendStructuredMessage still writes `type: 'shutdown_request'` with `from: LEADER_NAME`. Only the tool surface moved. Fixes #9276 * fix(ui): cover request_shutdown in tool display-name drift guards The new leader-only request_shutdown tool landed without entries in the drift-guarded display surfaces, breaking CI: - web-shell TOOL_DISPLAY_NAMES (toolFormatting.drift.test.ts) and the zh badge label in client/i18n.tsx (toolFormatting.test.ts parity) - cli toolDisplayName locale entries enforced by i18n/index.test.ts and check-i18n key parity (en identity / zh / zh-TW) * fix(core): close the leader-only hole the review found, and register the display name Three criticals from review, all real. The leader-only guarantee did not hold. "Enforced by absence" covers a registry that was *built* with forSubAgent, but runSingleDispatch's workflow fast path hands a subagent the parent leader's registry untouched — and that one does contain request_shutdown. So a workflow subagent could request a shutdown as the leader. The runtime guard now uses isSubagentLikeExecutionContext rather than isTeammate, which covers every subagent-like context instead of only a teammate identity. RequestShutdown had no toolDisplayName locale entry, failing the CLI i18n guard, and no entry in web-shell's manually synced TOOL_DISPLAY_NAMES, failing that package's drift guard. Both added, matching the locales that carry SendMessage. And the suggestions: a config test asserting the tool is present in a leader registry and absent from a forSubAgent one — the property everything rests on, which nothing checked; the deliberate 'ask' permission default pinned; the empty and whitespace recipient guard covered, since the schema admits both; and the dead requestShutdown mock left in send-message.test.ts's helper type removed. Verified: 534 config tests, 30 across request-shutdown and send-message. * fix(core): exclude request_shutdown from subagent and teammate tool sets * fix(i18n): translate the RequestShutdown tool display name in Catalan The entry was left as raw English while every sibling team tool is translated. --------- Co-authored-by: yiliang114 <jinjing.zzj@gmail.com> |
||
|
|
b2d0687213
|
feat(serve): add --open-with-auth (#9738)
* docs(serve): propose ephemeral auth for --open Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): address ephemeral auth review Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): clarify asset pre-check boundary Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): centralize token selection plan Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): make ephemeral auth opt in Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): align ineligible-browser handling with manual-URL fallback Browser-launch eligibility is a heuristic with common false negatives, so it is no longer a hard pre-listen gate: an ineligible environment warns (naming the tripped signal), starts the daemon, and prints the fragment-bearing manual URL, matching the launch-failure recovery. Also pin the generation breadcrumb with planned test assertions. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * feat(serve): add opt-in ephemeral auth for --open Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * feat(serve): replace ephemeral auth with --open-with-auth Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): clarify temporary token storage Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): clarify ephemeral token persistence Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#9738) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#9738) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
dbf7382c8f
|
fix(memory): scan uncapped when selecting forget candidates (#9530)
* fix(memory): scan uncapped when selecting forget candidates Recall moved to the uncapped scanner in #8716; forget did not. A document ranked past the 200-document cap could be recalled and injected into the prompt but never forgotten. Forget now scans uncapped, so its candidate universe matches recall's. The model-selection prompt renders every candidate, so it gets its own bound of 400: literal query matches first, then the most recently modified remainder. The heuristic fallback keeps scanning the full uncapped list. Indexer, status, and extraction stay capped on purpose, and the two design docs that recorded forget as capped now say otherwise. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(memory): give each scope its own share of the forget prompt Review round 1. The 400-candidate bound ranked both scopes into one recency budget, so a store whose project entries are all newer than its user entries seated no user memory at all. The capped scanners this replaced ran per scope, so each scope always had seats. That made an old user entry unselectable by the model while recall could still inject it, which is the same asymmetry the PR set out to close. Each scope now keeps a 200-candidate quota and whatever a smaller scope leaves is handed to the other. Within a scope, literal query matches rank first and both groups are ordered newest first, so truncation is deterministic instead of scan-order, and the bound logs when it drops candidates. Also from review: the query normalisation and match predicate are now shared with selectByHeuristic so the two cannot drift; the user scan gets the best-effort guard recall.ts and extractionAgentPlanner.ts already carry; and the docstring and design docs no longer claim an unconditional guarantee the bound does not provide. Three tests, each verified against the mutation it is meant to catch: global ranking drops the user ids, an ascending sort drops the newest filler, and handing the fallback the bounded list returns 400 of 450 matches. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(memory): bound the unconfirmed forget path and drop the silent scan guard Review round 2, all suggestions. MemoryManager.forget passed limit: MAX_SAFE_INTEGER and deletes without confirmation. With an uncapped scan and a heuristic fallback that substring matches the whole store, a one-character query matched nearly every entry in both scopes, where the capped scanners had held that same failure to one scan's worth of candidates. The limit is now the prompt bound, restoring the old ceiling. Round 1 added a best-effort catch on the user scan. That was wrong on two counts: scan.ts caps after reading and ordering the whole tree, so uncapping adds no read exposure to justify it, and swallowing the failure made forget report "no entries matched" for a scope it never read, then act on that answer by deleting. Reverted, with a comment saying why forget differs from recall here: a missed injection is recoverable, a missed deletion is not. normalizeForgetQuery now delegates to normalizeSummary so query matching and the post-selection re-match cannot drift apart, and one design-doc sentence no longer implies only semantic matches fall off the bound. Two tests, each verified against its mutation: the quota split is now exercised with both scopes over quota, where dropping it to 150 seats 250 project entries instead of 200; and the delete ceiling fails at 401 removals if the unbounded limit comes back. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(memory): split forget's deletion seats per scope, and decouple the ceiling Review round 3. The deletion ceiling added last round truncated the heuristic fallback in candidate order, and listIndexedForgetCandidates pushes every user entry ahead of every project entry. With 450 matching user entries and 50 matching project ones and the side query down, forget deleted 400 user entries, zero project ones, and reported success. That is the reachability asymmetry this PR exists to remove, moved into the delete path. The per-scope allocation the model prompt already used is now shared with the heuristic, so each scope keeps its share of the limit and a smaller scope's unused seats go to the other. The ceiling is also its own constant now rather than an alias of the prompt bound. Resizing the model prompt is a cost decision and resizing this is a blast-radius decision; sharing one constant let the first silently widen the second. Two tests, each checked against its mutation: the 450-user/50-project shape returns zero project matches under a plain slice, and oldest-first ranking inside a scope drops that scope's newest entry from the prompt. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(memory): pin the forget split at a small limit and the heuristic's own order Cross-review found both new tests mutation-survivable. Every case used a 400 limit, so hard-coding a 200 per-scope quota instead of deriving it from the budget still passed, and the recency case let the side query succeed, so it pinned the model prompt's ranking rather than selectByHeuristic's own comparator. One case at limit 5 with the side query failing covers both: it asserts the 3/2 split, which only holds if the quota comes from the budget, and that each scope contributes its newest entry, which fails if the comparator is reversed. Both mutants verified failing. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(memory): share the forget recency comparator and log a bound deletion Review round 4, both suggestions. The mtime comparator was the last thing the model path and the heuristic path each typed for themselves, after this branch had already hoisted the query normaliser, the match predicate and the per-scope allocator so the two could not drift. Each site has its own test, so a one-sided ordering change would have updated its own test, passed CI, and left the sibling stale. Now one definition. The deletion cap also bound silently. The prompt bound warns when it truncates; the path that actually deletes did not, so a forget that removed 400 of 500 matches reported success and left no record of why recall kept injecting the rest. It now says so. No test for the new warning: it is a debug log line, and asserting on it would pin the wording rather than the behaviour. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
c7c4dc80e0
|
feat(core): add the output-style layer to the system prompt (#9565)
* feat(core): add the output-style layer to the system prompt
Introduces output styles: a named prompt section that changes how the
agent reports its work, selected per session. This is the core half —
the registry, the four built-in styles, and the prompt wiring. Nothing
selects a style yet; the picker, the settings key, and the per-turn
reminder follow separately.
The style lands at the end of the stable `base` layer: after the mandates
it refines, and still ahead of every context/volatile layer, so the
prompt prefix stays cacheable for the whole session.
Two precedence rules, both following what the file already does for
QWEN_SYSTEM_IDENTITY_MD:
- A QWEN_SYSTEM_MD override wins. That file is a full, user-owned prompt;
layering our section onto it would defeat the override.
- The QWEN_WRITE_SYSTEM_MD dump stays a pure base prompt. A style sits on
top of a base, so baking it into the dump would apply it twice once
that file is fed back through QWEN_SYSTEM_MD.
`keepCodingInstructions: false` lets a style replace the base outright,
for styles that are not about software engineering at all. All four
built-ins keep it true — Proactive in particular changes how much you
plan and ask, not what you are allowed to do, and says so in its prompt
so it does not read as a second permission knob next to ApprovalMode.
Subagents and arena runs deliberately do not inherit the main session's
style: it would multiply Explanatory's insight blocks across every child,
and skew an arena comparison that is supposed to isolate the model.
* fix(core): narrow keepCodingInstructions to the section it names
`keepCodingInstructions: false` replaced the whole base prompt, which
took the safety rules down with the workflow guidance: a non-coding
style lost `# Executing actions with care`, the mandates, the tool
guidance and the tone section along with the part it meant to drop.
It now omits exactly one section — the software-engineering workflow
guidance, split out as `getSoftwareEngineeringTasksSection()`. Every
other section stays under every style. A style adjusts how work is
reported; it never switches off the rules for taking risky actions.
Two smaller corrections in the same area:
The identity sentence now points at the style when one is active
("responding according to your Output Style below") instead of claiming
the agent specializes in software engineering under a style that says
otherwise. A `QWEN_SYSTEM_IDENTITY_MD` override is still inserted
verbatim — that wording is distributor-owned and not ours to rewrite.
The style section is headed `# Output Style: <name>` rather than
`# <name> Style Active`. The heading is the contract a custom style file
will rely on once user and project styles load: the file body becomes
the prompt verbatim and the heading is what names it.
The per-turn reminder is now every style's, not just the two that spell
one out. `turnReminder` overrides the generic wording rather than
deciding whether a reminder exists at all — an Explanatory session
drifts back to terse answers as readily as a Concise one does.
`getOutputStyleTurnReminder()` renders the line the injection site will
use.
Because the style now feeds the base prompt rather than only being
appended to it, the QWEN_WRITE_SYSTEM_MD dump builds its own unstyled
copy, preserving the invariant that the dump is a reusable base.
The prompts.test.ts snapshots are unchanged, which is the evidence that
a session with no style selected still gets a byte-identical prompt.
* test(core): pin the output-style layer where review round 1 found it unpinned
Two coverage gaps from the r1 review, both confirmed by mutation probes:
R1-1 (prompts.test.ts): the `QWEN_SYSTEM_IDENTITY_MD` override combined with
an active output style was pinned by nothing — the identity-override tests
pass no style and the `outputStyle parameter` tests never stub the override.
Skipping the style append under an override kept all 105 tests green while
silently dropping the user's style for every deployment shipping an identity
override. The new case asserts the override text wins verbatim, the styled
identity sentence is skipped, and `# Output Style: Concise` still lands.
R1-2 (contextCommand.test.ts): the `config.getOutputStyle()` forwarding into
`getCoreSystemPrompt` was never exercised with a style active — every config
mock returned `undefined`. Deleting the argument kept the suite at 16/16
while `/context` undercounted system-prompt tokens by the style section. The
new case bills a `Concise` style and asserts the estimate grows by that
section.
Mutation verification: skipping the style append under an identity override
fails only the new prompts case (1 failed | 105 passed); deleting the
`config.getOutputStyle()` argument fails only the new contextCommand case
(1 failed | 16 passed).
* fix(core): align output style prompt consumers
* test(core): pin Learning output style modes
* test(core): pin Learning style in ACP prompts
* fix(core): align output-style doc comment and Concise description
---------
Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
|
||
|
|
f241c19ace
|
fix(core): support per-provider stream idle timeout (#9795) | ||
|
|
717ad101e2
|
feat(web-shell): add async submit preparation (#9802)
* feat(web-shell): add async submit preparation * fix(web-shell): keep failed-prompt retries out of the daemon retry path The failed-prompt retry reused `retry: true` only to skip prepareSubmit, but the flag is also forwarded to the daemon, whose retry branch skips recording the user message — the retried turn ran and was answered, yet its user prompt never reached the transcript. Skip preparation with an internal skipPrepareSubmit flag instead; the Ctrl+Y turn-error retry keeps `retry: true`, where the daemon semantics are correct. Also classify the retry payload from the prepared prompt so a host rewrite that changes slash-ness still arms the turn-error retry. Adds focused coverage for the resolvePreparedSubmit fallbacks, preparation-time staleness cancellation on both paths, prepareSubmit rejection, and the queued empty-prepared guard. * fix(web-shell): disarm stale retry state after slash-prepared submits * test(web-shell): pin slash-prepared disarm clears with attachment and stash variants Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
4f53c7d160
|
fix(core): route debug logs through sessionIdContext before global session (#9538)
* fix(core): route debug logs through sessionIdContext before global session In daemon/ACP mode one process hosts many sessions. Config creation overwrites the process-wide debug log session, so logs for session A were written to session B's file after B's Config was created. ACP sessions already wrap execution in sessionIdContext.run(sessionId). Make debugLogger.getActiveSession() consult that context before falling back to the global session, while keeping runWithDebugLogSession / runWithoutDebugLogSession overrides first. Fixes #9535. * fix(core): bind sessionIdContext in ACP control dispatch and refresh latest alias - Wrap AcpAgent.extMethod dispatch in sessionIdContext.run(sessionId, ...) when a sessionId parameter is present, so control-plane handlers like qwen/control/session/recap route debug logs to the targeted session. - Make debugLogger.getActiveSession() consult sessionIdContext before falling back to the process-wide session. - Refresh the 'latest' debug-log alias when the active writing session changes, so multi-session daemons don't leave it stale. - Add regression tests for control-plane dispatch binding and alias refresh. Relates to #9535. * refactor(core): harden debug logger alias and ACP session context binding - Gate AcpAgent.extMethod sessionIdContext.run on this.sessions.has() to avoid binding unsanitized caller-supplied strings. - Serialize latest-debug-alias updates via a module-level promise chain and key the dedup marker by debug-directory + sessionId. - Extract doUpdateLatestDebugLogAlias so marker/alias bookkeeping is centralized in updateLatestDebugLogAlias. - Strengthen regression tests: assert the run callback actually dispatches, and add a negative case for alias dedup. Relates to #9535. * fix(cli): use Config session id for async-context binding and cover review findings - Bind sessionIdContext to session.getConfig().getSessionId() in AcpAgent.extMethod so the context spelling matches Session.ts and avoids splitting legacy sessions across upper/lower-case debug log files (R3-1). - Wrap top-level session handlers (cancel, setSessionMode, unstable_setSessionModel, setSessionConfigOption) with sessionIdContext.run (R2-4). - Only bind extMethod dispatch for session-scoped methods, preventing global operations from being framed with a leaked session context (R4-1). - Add regression tests for Config-spelling binding, global-method allowlist, and serialized latest-alias updates (R3-4). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
01416830c9
|
docs(vscode): correct minimum VS Code version (#9852)
Align the companion README requirement with the extension manifest so users do not try unsupported VS Code releases. Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
4d3f9ff571
|
fix(core): allow hook-bounced workflow reapproval (#9547)
* fix(core): allow hook-bounced workflow reapproval * fix(core): release settled workflow approval sources * fix(core): reject stale workflow approvals Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): retry failed approval delivery Record approval incarnations only after synchronous event delivery succeeds, preserving retry recovery while pinning scheduler bounce identity. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): log approval delivery failures Expose failed approval event delivery while preserving retry-on-update behavior and consolidate the shared approval test setup. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): stabilize approval event delivery Share responder state across retries, reject stale incarnations, and bound automatic redelivery so transient listener failures cannot stall or duplicate approvals. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): isolate approval delivery per listener Retrying a partial approval delivery re-emitted the event to every listener, so a single failing listener both blocked healthy ones from ever seeing the request and re-delivered duplicates to listeners that already succeeded (forwardApproval/createApprovalHandler have no callId dedup). Push the isolation down to the emitter: iterate rawListeners, catch per listener, and retry only the listeners that actually threw. Surface retry exhaustion via console.error so it is visible outside debug sessions, and drop the dead awaitingByCallId guard (a same-tick snapshot compared against itself could never differ). Cover thrower-before-healthy, healthy-before-thrower, exhaustion reporting, and both clearApprovalDeliveries teardown paths (abort and all-tool-calls-complete). --------- Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> |
||
|
|
014b903bf5
|
fix(daemon): Bound conditional-close refusal holds (#9820)
* fix(daemon): Bound active-work close refusal holds Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#9820) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
3fd059368b
|
fix(core): invalidate token counts recorded for a switched model route (#9506)
* fix(core): invalidate token counts recorded for a switched model route `/model` switches rebuild the content generator but keep the same GeminiChat instance, so API-reported prompt/output token counts from the previous route survived and anchored admission, output clamping, and compression decisions for a different serialization (#9454). Attribute the counts to the route that produced them (Config.getModelRouteIdentity) and invalidate them on a route change so all safety decisions fall back to the history-walk estimate. Closes #9454 * fix(core): keep token counts scoped to producing route * fix(core): close round-2 route-scoping findings (#9454) - C1: client.test.ts session-token-limit gate test now seeds the chat mock's getLastPromptTokenCount (the gate's new source) instead of relying on the telemetry stub alone. - C2: goal-turn-integration.test.ts passes the routeKey positional added to processStreamResponse and widens the local cast. - S1: fix stale rationale comment — no session token-limit gate reads the telemetry mirror anymore; UI context counters and compression banners do. - S2: pin tryCompress's entry invalidation with a focused test; manual /compress reaches it without sendMessageStream's entry reset. - S3: direct config tests for getModelRouteIdentity — call stability, model@<sha-prefix> shape, and the guard keeping the registry baseUrl out of non-active model identities. - R2-3: sendMessageStream now resolves the request route first and invalidates counts against IT (deriving the key from the actual model param on the non-exact branch), so an active-route count can no longer anchor an exact `\0` route's output clamp; regression test added. * fix(core): close round-3 route-scoping findings (#9454) - R3-1: hard-rescue rollback now restores tokenCountsRouteKey alongside the counts; tryCompress re-stamps the key to the active route mid-rescue, which left the resurrected request-route count riding the active key past the next entry invalidation (regression test: active-route read after failed rescue must not inherit the override count). - R3-2(2): invalidation zeroes the telemetry cached-content mirror together with the prompt mirror so /context stops rendering a foreign cached count beside a zeroed prompt count; the mirror is documented as best-effort display state between a switch and the next guarded read (R3-2(1)). - R3-4: collapse the requestRouteKey ternary into one getModelRouteIdentity call (the non-exact arm passed exactly the default parameter value). - R3-5: resolve the active-route default lazily after the zero-count fast path instead of eagerly in the default parameter. - R1-3: add the missing compressFast route-invalidation test (third entrypoint; mutation-verified). * fix(core): preserve route-scoped token guards (#9454) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): resolve full-turn route selectors at the session token limit gate (#9454) * fix(core): stamp fallback-served token counts under the request route (#9454) * fix(core): retain route-scoped token counts across route crossings (#9454) * fix(core): keep route-scoped token counts consistent through compression (#9454) * fix(core): keep rescued output counts and compression stamps route-scoped (#9454) --------- Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
3d96e54641
|
chore(ci): refresh the core issue-owner pool (#9808) | ||
|
|
aac9606f78
|
fix(cli): skip terminal redraw optimizer on WSL/ConPTY (#7897)
* fix(cli): skip terminal redraw optimizer on WSL/ConPTY and enable sync output on Windows Terminal (#7634) The streaming text repetition bug on WSL + Windows Terminal is caused by the terminal redraw optimizer batching cursor-up sequences, which ConPTY processes differently from individual per-line erases. The cursor lands at the wrong row, causing each new frame to overlap remnants of the previous one. Two fixes: 1. Skip the redraw optimizer when WSL (WSL_DISTRO_NAME / WSL_INTEROP) or Windows Terminal (WT_SESSION) is detected, falling back to Ink's original per-line erase sequences that ConPTY handles correctly. 2. Enable synchronized output (DEC mode 2026) for Windows Terminal, which has supported it since v1.6, making frame updates atomic and masking any residual cursor positioning issues. Fixes #7634 * fix: add WSL_INTEROP test and clear env vars in beforeEach to fix test fragility Address review feedback on #7897: - P1: clear WSL/Windows Terminal env vars in beforeEach so existing optimizer tests don't silently break when run inside WSL - P2: add dedicated test for WSL_INTEROP detection * review: address wenshao feedback on #7897 - Accept injectable env in installTerminalRedrawOptimizer (matches sibling terminalSupportsSynchronizedOutput), eliminating the need for beforeEach env-stubbing in test files - Add QWEN_CODE_LEGACY_ERASE_LINES=0 as a force-on escape hatch for WSL/Windows Terminal users whose terminals handle the batched sequences correctly - Collapse three near-identical WSL/WT skip tests into it.each - Correct Windows Terminal DEC 2026 support version: v1.18, not v1.6 - Move WT_SESSION check above the TERM declaration in terminalSupportsSynchronizedOutput so the term isn't declared before its only consumer - Add a table case asserting TMUX guard still wins over WT_SESSION - Pass explicit empty env to installTerminalRedrawOptimizer in the synchronizedOutput composition test so it doesn't depend on the runner's environment * fix(cli): narrow optimizer skip to WSL only, drop WT_SESSION Per wenshao's review: WT_SESSION is set on the Windows side and is not propagated into WSL shells without WSLENV, so it can never be the env var that fires for #7634. Remove WT_SESSION from the optimizer skip (WSL_DISTRO_NAME + WSL_INTEROP remain) and from the synchronized-output allowlist. The synchronized-output change for Windows Terminal belongs in its own PR once confirmed; bundling it into a WSL bug fix mixed two independent behavior changes. Also correct the comment: 'WSL or Windows Terminal' -> 'WSSL only', and remove the WT_SESSION test cases from both test files. * fix(cli): clean up WT_SESSION comment residue and pin its exclusion Per review: the drop of the WT_SESSION skip left stale comments and no test pinning the deliberate exclusion. Fix the force-enable comment (WSL only, not Windows Terminal), complete the truncated WT_SESSION rationale, and add a test asserting WT_SESSION alone does NOT trigger the skip (it is not propagated into WSL shells). Also stub QWEN_CODE_LEGACY_ERASE_LINES in beforeEach so the suite is isolated from a host that has the flag set. * refactor(cli): extract shared isWsl(env) into terminal-env util WSL detection was inlined in terminalRedrawOptimizer (this PR) and duplicated as a private helper in voice-availability. Extract a single isWsl(env) into ui/utils/terminal-env.ts and use it from both sites so the marker set cannot drift. Requested by maintainer in #7897 reviews. * fix(ui): add license header and gate WSL_INTEROP in voice preflight Round-3 review: terminal-env.ts shipped without the @license header every sibling carries; and the voice-side isWsl migration was inert under the test probe because voice-availability.test.ts only exercised the WSL_DISTRO_NAME marker. Add the header and cover WSL_INTEROP via it.each. #7897 * docs(ui): note the separate core-side WSL check in terminal-env Round-4 review: the extraction comment claimed the marker set cannot drift, but ripgrepUtils.wslTimeout() in packages/core keeps its own narrower WSL_INTEROP-only check because core cannot import from cli. Document the exception so a future maintainer greps both sites. #7897 * docs(cli): document QWEN_CODE_LEGACY_ERASE_LINES escape hatch Round-5 review (R5-1): isWsl(env) relies solely on env markers, which env-scrubbing launchers (sudo, env -i) strip - so the #7634 skip never fires in those contexts. Document the launch-time =1 fallback and note it must be passed at launch because sudo drops the flag too. Also closes the round-2 R2-3 gap (the flag was previously undocumented). #7897 * refactor(cli): move isWsl to core and apply maintainer review polish wenshao's manual review suggested moving the shared WSL marker check to packages/core so cli can import it (core cannot import from cli), while ripgrepUtils.wslTimeout() keeps its deliberately narrower predicate. Also: - Sharpen the ConPTY divergence comment with the concrete sequences the optimizer emits (CSI 1 B cursor-down, CSI n A multi-count) that Ink's native erase path never does. - Replace the beforeEach vi.stubEnv test fixture with explicit empty-env arguments (truer 'not on WSL' fixture, no host-env dependency). - Note the env parameter exists for testability. - Trim the moved file's doc block to durable facts and tighten the docs row wording. #7897 * test(cli): pin the env default-parameter seam in redraw optimizer Round-7 review: the production call path (installTerminalRedrawOptimizer with no env arg) was never exercised - every test passed env explicitly, so a mutation to the = process.env default (e.g. = {}) would pass green while silently disabling the WSL skip and =1 escape hatch in production. Add a hermetic test that stubs WSL_DISTRO_NAME and asserts the no-arg call skips the optimizer. #7897 * test(cli): restore afterEach env cleanup for default-seam test Round-8 review: placing vi.unstubAllEnvs() as the last statement in the default-seam test body meant a failing expect (the exact regression the test pins) would skip the cleanup and leak WSL_DISTRO_NAME=Ubuntu into process.env for the rest of the file. Move the cleanup back into the describe-level afterEach so it runs even on assertion failure. #7897 * test(cli): close the two minor coverage gaps from chiga0's review Maintainer chiga0 approved the PR but noted two minor test gaps: - The default-seam test only stubbed WSL_DISTRO_NAME, so a host-set QWEN_CODE_LEGACY_ERASE_LINES=1 would pass it for the wrong reason; stub the flag too so the assertion depends only on the WSL marker. - The tri-state flag has no case for a non-standard truthy value; add one pinning that 'garbage' falls through to the platform default (WSL skip). #7897 * docs(cli): correct 'only path' claim in ConPTY divergence comment Round-11 review: the comment asserted the optimizer is the ONLY path emitting CSI 1 B / CSI n A, but the repo's patched ink build also emits both sequence classes on its cursor-positioning path (buildCursorSuffix / buildReturnToBottom, reachable via BaseTextInput.setCursorPosition). Skipping the optimizer on WSL does not remove these from interactive input. Narrow the claim to the per-frame erase-and-redraw path. #7897 |
||
|
|
a8b822f5d2
|
feat(web-shell): expose agent task changes (#9637)
* feat(web-shell): expose agent task changes * fix(web-shell): deduplicate agent task callbacks * fix(web-shell): ignore agent task telemetry churn * fix(web-shell): skip immutable prompts in task fingerprint * fix(web-shell): type agent task fingerprint |
||
|
|
5cff52c6e8
|
fix(goal): count catalog previews in the unit their budget is written in (#9835)
Evidence catalog previews were cut to 240 characters while the budget they feed is 24,000 bytes. In UTF-8 those units differ by up to four times, so the guard held only for ASCII. A legal 32-claim checkpoint of Chinese claims serialized to roughly 29kB — over the cap on its own, before a single new record had been scanned — which marked the window truncated, and `truncated` switched compaction off. The one state compaction exists to resolve was the one state it refused to run in, so the Goal was stopped as `usage_limited`, the only status the reducer refuses to resume, with nothing left to salvage. An English Goal never reached that state; a Chinese one could not avoid it. Previews are now capped to 240 UTF-8 bytes on a code point boundary at the two points a catalog entry is built. Nothing changes for ASCII, where the two units already agreed; a CJK preview is shorter than before, which is the cost of the cap actually holding. With it, a full checkpoint is bounded well inside the catalog budget for every script, so a window can no longer start out truncated. A truncated window now compresses rather than stopping the Goal. Overflow means the budget is full and the newest evidence that did fit is exactly what a checkpoint folds into claims; the older evidence left behind is already covered by the previous checkpoint. Only a window that captured nothing at all has nothing to salvage, and that is the sole remaining path to `usage_limited` here. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
674afdf1a7
|
feat(core): show what a workflow will do, and scope the grant that approves it (#9837)
A user approving a workflow was shown `Confirm WorkflowTool` over `Run a workflow script (4127 chars)` — a character count standing in for arbitrary model-authored JavaScript that can fan out to the per-run agent cap, provision git worktrees and spend an uncapped token budget. The asymmetry was visible inside one run: the subagent approvals that same workflow bubbles up each got a full dialog. Override getConfirmationDetails to show what the script says it will do, read from `export const meta`: the workflow's name and description, its declared phases, the resolved args, and a bounded excerpt of the source. Meta is obtained through `extractAndStripMeta`, which parses rather than evaluates, so nothing model-authored runs before the user has approved anything. It throws on a malformed literal, so the call is wrapped: a script with a broken meta block stays approvable-or-rejectable rather than taking the dialog down with it. Everything displayed goes through `stripAnsiAndControl` first. The screen ships with the preview rather than after it, because until now nothing was displayed and so nothing could be spoofed — a preview without it is what would open the hole. Single-line fields are flattened, which is what we want for a `meta.name` spanning three lines. The script excerpt is sanitized per line instead: `\n` is a C0 control character, so the naive call would collapse the script into one unreadable line. Scope the grant on the same object. An inline `script` is fresh source every time, so it can never be pre-approved: `hideAlwaysAllow` removes the option, and an empty `permissionRules` stops `injectPermissionRulesIfMissing` from supplying the bare tool name, which `buildPermissionRules` documents as matching every invocation of the tool. A `scriptPath` names a file the user chose, so it stays pre-approvable but scoped to that path via a `key:value` param matcher. The rule is built with the same helpers the matcher uses, and the test asserts it behaviourally -- a rule that reads plausibly but never matches would make "always allow" silently do nothing. Finally, move the token-cost warning ahead of the spend it warns about. It previously appeared only on the success path, i.e. after the run, and never at all when the run failed. Claude-Session: https://claude.ai/code/session_01MDuRWMhKbqEYZXbbLhn3en Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a2e458deef
|
feat(auth): add Kimi (Moonshot AI) as a built-in third-party provider (#9814)
Adds a Moonshot preset to the /auth Third-party Providers menu, offering the international and China API endpoints and seeding the current Kimi model catalog. Moonshot speaks the OpenAI protocol, so this is a declarative preset with no new mechanism and no change to the provider type. Model metadata follows Moonshot's published capabilities. K3 is marked thinking-mandatory: its API exposes a reasoning-effort knob but no way to turn thinking off, so a disable shape must never reach the wire. The two code models and K2.6 keep thinking toggleable, and all four accept image and video input, which the K2.6 guide states explicitly. Registers the new credential env key everywhere a provider key has to appear: the no-AK CI gate and its pinned assertion list, and the telemetry provider mapping, both by env key and by request hostname so Kimi traffic is attributed rather than reported as unknown. The three first-run docs that enumerate built-in providers are brought back into agreement, which also picks up entries that were already stale. Closes #9197 |
||
|
|
747dbf00c2
|
feat(web-shell): compact agent activity summaries (#9657)
* feat(web-shell): compact agent activity summaries * fix(web-shell): preserve merged todo timing * fix(web-shell): address compact summary review * test(web-shell): cover compact review edge cases * fix(web-shell): make the folded thought header row fully clickable * fix(web-shell): guard folded thought toggle against popover clicks (#9657) * test(web-shell): cover thinking header hit area and parallel-agent thoughts (#9657) --------- Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
03bcfe44b5
|
refactor(review): split SKILL.md into a core body plus verdict-gated reference files (#9804)
* refactor(review): split SKILL.md into core body plus verdict-gated references (#9787) The bundled review skill's SKILL.md (381,984 bytes, ~95k tokens) was injected whole on every run, although large stretches are conditional territory a given run never touches. Split it into a core body plus reference files the orchestrator reads on demand, gated on the parse-args verdict it already holds: - references/posting.md — whole Step 7 (authorisation gate, presubmit, anchors, submit, 422/head-drift recovery, publish-assets). Loaded only when posting is live (comment.effective or a same-session post request; PR + high only). Its compose-state field list relocates verbatim to Step 6's Verdict section, because a report-only run still writes that state for compose-review without ever loading posting.md. - references/persistence.md — whole Step 8 (tail batching, report, artifact registration, incremental cache). Loaded before Step 8 on every run except cross-repo lightweight mode. - references/aone.md — the self-contained Aone blocks of Step 1 (clone and two-host rules, a1-backed surface, the five submit failure shapes, dedup shape notes). Loaded before match-remote when the host/meta says Aone. The split moves whole steps; incident-backed rules stay with the step they guard. The write prohibition and the posting gates remain in the injected core so they bind runs that never load a file. No enterprise.md: the GHE host notes are sentences woven into universal paragraphs, and extracting them would strip rules from steps that remain in core. Injected prompt: 381,125 -> 304,427 body bytes. Typical non-posting runs (local/file/PR, any effort) save ~58 KB (~15%); lightweight runs ~77 KB (~20%); posting runs load posting.md back and save only the Aone block. The issue's "roughly a third" estimate is unreachable under its own whole-step guardrail — Steps 1 and 6 dominate the core and interleaving forbids fragmenting them; Step 5 / Step 3C effort-gated splits are the natural follow-up. Drive-by, verified against #9627's revert-guard test and the a1 implementation: three stale sentences still claiming comment-status "has no Aone backing" are aligned with the a1-backed behavior that landed in #9627. Tests: SKILL.test.ts revert guards now govern the full corpus (SKILL.md + references), with new pins for the gates, the core-retained invariants and the no-duplication invariant; run-skill-parity reads the corpus oracle; bundled-skills integration pins the shipped reference files. Verified by build + bundle, all review-skill suites, and a real-model E2E run of the split skill (verdict-gated reads observed: persistence.md loaded before Step 8, posting.md and aone.md correctly skipped). * fix(review): drop uninterpolated template tokens from skill references (#9804) * test(review): guard stems oracle by persistence.md, pin gate clauses to bullets (#9804) * fix(review): close Step 7 reference doc gaps from reverse audit (#9804) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
a7128d30c3
|
fix(core): isolate fork cache readers by session (#9471)
* fix(core): isolate fork cache readers by session * fix(core): skip foreign cache extraction * perf(core): skip foreign cache before memory IO * test(core): provide session id in extraction fixture --------- Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com> |
||
|
|
deed96e733
|
fix(core): avoid ghost shutdown pending state (#9550)
* fix(core): avoid ghost shutdown pending state * fix(core): gate assignments during shutdown delivery Keep teammates out of task assignment while shutdown delivery is pending, with rollback on mailbox failure. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): preserve concurrent shutdown state Track in-flight and delivered shutdown requests independently so a failed retry cannot reopen assignment gates owned by another request. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): settle resolved shutdown writes Keep response resolution separate from mailbox delivery so late writes cannot re-arm a completed shutdown generation. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): separate shutdown marker ownership Track delivered requests independently from test-only markers so failed writes clean up without dropping pending successful deliveries. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): track shutdown response ownership Represent delivered shutdown requests as reservable tokens so concurrent writes and responses conserve pending state without stale generations or duplicate aborts. Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com * fix(core): deliver every stacked shutdown request A busy teammate that received two shutdown requests only ever saw the first: one idle flush read all unread shutdown messages but enqueued only shutdowns[0], leaving the rest marked-read and dropped. Each write still minted a delivered token, so the un-delivered request's token could never be consumed by a response and hasShutdownWork stayed true forever, permanently gating task assignment for a live teammate. Enqueue every shutdown returned by a single flush so token count and delivered-request count stay balanced. Add coverage for stacked delivery, markShutdownRequested idempotence, the responsesInFlight gating window, and abort-on-approve when a MESSAGE_SENT listener throws. Extract the repeated gated-mailbox-write scaffolding into one helper and reset the shared mailbox mocks per test. * chore(core): log shutdown ledger state transitions Add debug logging at the shutdown state-machine transitions (token mint, response settlement, ledger drain) so a stuck shutdown-pending teammate can be diagnosed from logs without reading private fields. Addresses reviewer suggestion on #9550. --------- Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> |
||
|
|
75fb40d832
|
refactor(goal): render Goal continuation prompts from one core renderer (#9581)
* refactor(goal): render Goal continuation prompts from one core renderer The prompt sent when `runtime.finishTurn` schedules another Goal turn was assembled independently in three hosts: the TUI's inline array in `useGeminiStream`, and a `buildGoalContinuationParts` in each of the ACP session and the non-interactive CLI. Three copies of the same four shared lines have already drifted -- the TUI carries the anti-spoofing guard lines but no objective, while ACP and non-interactive carry the runtime continuation context but no guard lines. Upcoming work adds further variants (an "objective was edited" announcement and a budget wind-down prompt). With the text living in three places, every new variant means three edits, which is precisely how the current drift was produced. This moves assembly into `packages/core/src/goals/goal-continuation-prompt.ts`, where a variant is a case in one function and the shared prefix exists once. The two `buildGoalContinuationParts` helpers keep their names and signatures and simply delegate. This is a pure refactor: no prompt text changes. Each host still emits a byte-identical string to the one it emitted before. The existing drift is preserved deliberately and is left for a separate, behavior-changing follow-up. The new unit test pins the complete rendered string for both variants with and without verifier feedback, so any future edit to a line surfaces as a test diff; the existing host tests pass unmodified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(goal): tighten continuation renderer contract * test(goal): cover verifier feedback hosts * refactor(goal): hoist Goal continuation parts builder into core (#9581) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
00461ab275
|
fix(core): mark agent launch failures as failed tool calls (#9519)
* fix(core): mark agent launch failures as failed tool calls The subagent-not-found path and failWorktreeProvisioning() returned a ToolResult with llmContent and a failed returnDisplay but no error field. The scheduler records a failure only when error is set (coreToolScheduler keys off toolResult.error), so a launch that never ran was counted as a successful agent call: no error-formatted model response, no failure-path hooks, and optimistic success telemetry. Set error consistently with buildSpawnBlockedResult in the same file, which already documents this convention. The full guidance text (the available-subagents list, the specific provisioning reason) goes into error.message because the failure path forwards only that to the model. returnDisplay is unchanged, so terminal output is identical. Fixes #9509 * fix(core): mark agent launch failures * test(core): cover preserved agent worktree errors * test(core): keep preserved worktree error test foreground * test(core): fail subagent via creation seam in preserved-worktree test |
||
|
|
20e076f8f3
|
fix(core): surface nested sub-agent approvals under background parents (#9793)
* fix(core): surface nested sub-agent approvals under background parents A tool call needing confirmation inside a nested sub-agent (launched by a background agent or fork) was neither surfaced nor denied: the prompt- avoidance policy was stamped on an Object.create wrapper while the rebuilt tool registry binds to agentConfig, so nested schedulers resolved Config.prototype's false and believed they could prompt; and the nested invocation's emitter was never bridged, so TOOL_WAITING_APPROVAL fired with no listener and the call waited forever - the enclosing agent hung silently. - Stamp getShouldAvoidPermissionPrompts on agentConfig itself (launch and resume paths) so nested launches inherit the real policy through their config prototype chains: hang-forever becomes an explicit deny when bubbling is off. - Bridge nested foreground launches' approval events onto the nearest backgrounded running ancestor's Background-tasks entry (walking the registry's parentAgentId lineage), so they park where the ancestor's own approvals go and the user can answer them from the dialog. - Mark bridged approvals with the nested runtime's subagentId (declared by the bridge caller via nestedSource - runtime ids and registry ids use different suffixes so comparing them cannot work) and show the waiter in the Background tasks dialog. Fixes #9782 * chore(i18n): add 'from nested agent' locale entries * fix(core): harden nested sub-agent approval bridging (#9793) * fix(core): tighten nested approval bridging per review round 2 (#9793) - addPendingApproval returns a discriminated result ('parked' | 'duplicate' | 'unavailable') mirroring the workflow registry's parkPendingApproval, so the bridge drops re-emitted events without re-running the dedup scan on the caller side - log a debug line when a re-emitted approval event is dropped, so "approval never appeared" sessions can tell a drop from a lost event - extract stampBackgroundPromptPolicy next to createApprovalModeOverride; both the launch and resume paths now share one stamp + rationale - gate the nested approval bridge on the inherited prompt-avoidance policy like the sibling bridges, instead of wiring a dead subscription under auto-denying ancestors - refresh two JSDoc examples that still cited the deleted Object.create wrapper * fix(core): key parked background approvals on runtime identity (#9793) Generated tool-call ids (`call_qwen_N`) are only unique per conversation, so two nested sub-agents whose first id-less call needs confirmation both arrive under the same callId on their shared background ancestor entry. The parked-approval queue keyed identity on callId alone: the second runtime's prompt was dropped as a "duplicate" with nothing in the dialog to answer, and a resolution or TOOL_RESULT for one runtime could clear another runtime's parked prompt. Key identity on (subagentId, callId) — the same composite the workflow run registry already uses — across addPendingApproval's duplicate predicate, resolvePendingApproval, and clearPendingApproval. Own approvals stay unstamped, so same-call re-emissions from the entry's own runtime still dedupe while different runtimes never collide. * fix(core): attribute nested approval logs and pin composite-key dedup (#9793) * fix(core): attribute remaining approval failures and pin own-vs-nested collision tests (#9793) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |