mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-27 01:23:52 +00:00
1324 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0a3d7bb5c1
|
feat(acp): Protect against repeated tool execution failures (#8469)
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
* feat(acp): protect repeated tool execution failures Add a conservative prompt-local guard for repeated typed ACP tool execution failures, with shadow/warn/enforce rollout modes, privacy-safe telemetry, and coverage for the final execution outcome contract. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(acp): harden repeated tool failure guard rollout Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(acp): address repeated failure guard review Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(acp): improve repeated failure guard recall Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(acp): clarify review and rollout gates --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
a810f7e16c
|
fix(serve): Make session restore timeouts safe and observable (#8691)
* fix(serve): make session restore timeouts safe Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): restore missing core mock exports in the ACP worktree suite The restore-tracing change added `extractDaemonTraceContext` and `withDaemonSpan` to `acpAgent.ts`, but `acpAgent.worktree.test.ts` replaces `@qwen-code/qwen-code-core` with a full mock factory that never listed them. `loadSession` then failed on an undefined export, taking all three cases down and producing teardown rejections from the half-built agent. The sibling suite was updated; this one was missed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): bound and disambiguate the abandoned restore lifecycle Four follow-ups from review of the restore timeout work. A startup budget may now raise the restore budget but never lower it. Taking an explicitly configured `initializeTimeoutMs` as the restore fallback meant a deployment that tightened its child-initialize check still inherited a sub-default restore deadline — exactly the failure this change exists to remove. An explicit `sessionRestoreTimeoutMs` still wins outright, including below the default, for deployments that want restore to fail fast. Validation now names the field actually at fault. A restore fenced behind a timed-out predecessor is no longer reported as an ordinary in-flight restore. It carries `reason: awaiting_abandoned_cleanup` and a retry hint of one restore budget (capped at 120s) instead of the ordinary 5 seconds, because the fence cannot clear until the non-cancellable ACP request settles and a 5-second cadence just spins the caller against a 409 it cannot resolve. Whether a channel is condemned is now derived rather than sticky. A timeout recorded `emptyReapPending` permanently, so any channel that had ever seen one was guaranteed to be reaped once its remaining work drained, forcing a cold respawn even when the late restore had landed and closed cleanly. The reap condition is now computed from an outstanding `unsettledAbandonedRestores` set, quarantine, or an ordinary pending empty reap; real settlement clears the entry and hands the channel back to the configured idle policy. Abandonment no longer retains ownership without bound. One further restore budget after the deadline, a still-unsettled restore marks the channel `restoreSettlementOverdue`: existing sessions and workspace control keep working, but fresh session work is refused so the channel can drain, since closing the transport is the only lever that releases a permanently hung request. Releasing capacity while hidden work runs would allow unbounded oversubscription, and force-killing a channel with live siblings would reintroduce the failure this work removes, so neither is done. Fresh-admission blocking is now scanned across alive channels rather than tracked in a single reference, so a second condemned channel cannot silently displace the first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): keep the abandoned restore lifecycle off ids it no longer owns Two correctness gaps in the abandoned-restore machinery introduced by this PR, both reported by automated review and both confirmed by mutation testing (each new test fails when its fix is reverted). A caller-supplied `sessionId` is used verbatim by the agent, but `spawnOrAttach` never consulted `inFlightRestores`. A fresh spawn could therefore take an id that a restore still owns, in either lifecycle phase. The consequences were silent: `abandonedRestoreIds` suppresses session updates, guardrail events, and child notifications, so the new session would have registered successfully and then emitted nothing; and a late `settleAbandonedRestore` would have closed and tombstoned it out from under its owner. Such a spawn is now rejected with the same `RestoreInProgressError` and reason the restore path uses, so the caller gets the correct retry hint for whichever phase is holding the id. The cleanup path is guarded independently, because the request-level check only covers the id the caller asked for and a session registers under the id the child returns. An abandoned restore never reaches `createSessionEntry` — the deadline rejects before registration — so any live entry under that id belongs to someone else. Cleanup now detects that and returns without closing or tombstoning, releasing its own bookkeeping instead. The notification fence has no TTL and was only cleared by `markRestoreInFlight`, which covers a subsequent restore and nothing else. `createSessionEntry` now clears it for every registration route, so a legitimate owner of the id is never handed a session that silently drops everything the child sends it. Also tightens two tests that could not observe the values they pin. The SDK default restore timeout admitted any value in (30s, 70s]; it is now split at the exact boundary, so collapsing the default onto the 60s server budget — which would make the client abort race the daemon's own deadline and cost the caller its structured 504 — fails. And the advertised-budget propagation from capabilities through to the SDK call had no live-path assertion; dropping the capabilities argument at the real call site left every existing test green. The `as never` casts are replaced with typed `DaemonCapabilities` values so a field rename fails typecheck. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): let a condemned channel drain without its wedged child Merging main's active-work close protocol (#8588) into this PR's abandoned restore bound produced a deadlock that neither side has on its own, and the conflict resolution was committed without running tests. `maybeCloseIdleSession` now routes through `confirmChildUnheld`, which asks the child whether it still holds work before closing a session nobody is attached to. That is right in general and wrong for a channel this PR has already condemned. `restoreSettlementOverdue` and quarantine exist precisely because the child stopped being answerable, and their whole premise is that visible work drains so the channel can be reaped — closing the transport is the only thing that can release a restore we cannot cancel. Making that drain depend on a round trip to the wedged child inverts it: a child stuck in a non-cancellable restore is exactly the one that cannot reply inside `ACTIVE_WORK_CLOSE_TIMEOUT_MS`, so the sessions never close, the channel never drains, the reap never fires, and the bound never takes effect. A channel condemned by the restore lifecycle now skips the round trip and proceeds to local teardown. Nothing is attached to the session by then — `maybeCloseIdleSession` gates on that — and the sibling-safety invariant is untouched: this closes sessions whose clients have already left, it does not force-kill a channel that still has live ones. The regression test drives an overdue channel whose child never answers the close-if-unheld probe and asserts the detach still reaps it. Reverting the guard reproduces the deadlock as a test timeout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(serve): pin the restore-timeout contract the review found unasserted Automated review identified eleven places where the restore-timeout work's behavior was correct but unpinned — each with a mutation that ships green. Every fix below was verified the same way: apply the mutation, watch the new assertion fail, revert, watch it pass. The timeout path's telemetry had no coverage at all, which is the sharpest gap given that observability is what this work exists to deliver. A shared recorder now asserts the public timeout result and its kill_empty-vs- fence_shared signal, the late arrival, and the cleanup outcome for both the closed and quarantined cases. The deadline timer's cancellation on a successful restore was likewise unpinned: deleting both `clearTimeout` calls kept the whole suite green, while in production the stale timer fires one budget after a successful restore and abandons a live session — fencing its frames, closing its event bus, and emitting a spurious timeout. A success-path test now advances past the deadline and asserts no second public result. Three more bridge assertions proved less than they claimed: the concurrent- restore case never checked that the abandoned restore settles, the workspace-control case never checked that the deferred reap eventually fires, and the resolver never pinned the accepting side of the MAX boundary (a `>` to `>=` mutation rejects the largest legal delay at boot). The workspace-control case also needed a positive channel idle budget, since with the default zero the idle-timer kill substitutes for the reap junction under test; its assertions are rewritten around the derived reap semantics rather than the sticky flag they predate. Outside the bridge: the scheduled-task timeout wiring had no test, so deleting the arguments silently fell back to the helpers' own defaults; the cold restore path never asserted that `live_restore_ms` is absent; the SDK's per-request validation and its over-ceiling clamp were untested; the WebUI watchdog test jumped straight to its own value, staying green for any watchdog at or below it, including the 30s attach value that would recreate the original symptom in the browser; and the two new known error types were unexercised, so dropping either would relabel every restore-timeout and quarantine error as unknown. Two review items are deliberately not taken here and are recorded in the design doc's non-goals instead: transcript materialization is still not separately attributable from `config_setup`, which needs instrumentation inside the core session loader that P1/P2 restructures anyway, and sibling event-loop latency during a large restore remains unmeasured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): bound the condemned-channel close and complete the fence contract Second automated review round, on the code the first round produced. One Critical and twelve suggestions; all verified by mutation before and after. **The Critical is a regression I introduced.** Letting a condemned channel skip the bounded hold probe routed it into `closeSessionImpl`, whose agent close is unbounded when it throws on failure — so the fix traded a bounded wait on a wedged child for an unbounded one. A settlement-overdue channel with an unresponsive child would hang `detachClient` forever, strand the session in `closing`, never drain, never reap, and 503 every new session until restart: strictly worse than before. `CloseSessionOpts` now carries an `agentCloseTimeoutMs` that the condemned path sets, so a hang lands in the existing unknown-outcome recovery, which kills the channel — the teardown the drain was waiting for. The earlier test missed this because its fake child still answered the plain close; it now answers nothing at all, and asserts the detach itself returns. **The fence was invisible on the transports clients actually use.** `toRpcError` had no `RestoreInProgressError` case, so over acp-http and acp-ws — which SDK negotiation prefers over REST — the fence degraded to an opaque internal 500 with no code, reason, or hint, and the backoff contract this work documents was impossible to honor. **Two retry hints still advertised five seconds for states that outlive a budget.** The restore 504 creates the fence, and quarantine lasts until the channel drains; a fresh-id caller never reaches the 409 that carries the real hint, so its header was the only signal it got. Both now derive from the budget through one shared clamp helper, which also replaces the formula that was inlined in the bridge and gives the documented 5-120s bounds a test. **A spawn collision reported an operation the caller never issued**, naming the restore owner's action as both the active and the requested one and telling the caller to retry an endpoint it never called. The rest: five places still described the initialize-timeout fallback as a plain chain rather than raise-only, contradicting sibling docs shipped in this same PR; the design doc omitted the retry-hint clamp; the protocol reference omitted the new spawn emission site; the error taxonomy omitted `restore_settlement_overdue`, which matters because its audience is monitoring. Test-only gaps: the dynamic 409 had no HTTP-layer coverage, the 120-second cap was unpinned, and the SDK's precedence of an explicit global timeout over the advertised budget was pinned only branch-by-branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): preserve restore session ownership handoff Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
bf84caf173
|
feat: add Local Control pairing to CLI and Desktop (#8727)
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 / 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
E2E Tests / channel-plugin E2E (nightly) (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
npm cache producer / Save npm cache (push) Has been cancelled
* feat(cli): add Local Control pairing * fix(cli): address Local Control review feedback * fix(cli): allow Local Control loopback origin * feat(desktop): add Local Control pairing * fix(local-control): bound unauthenticated connections * test(desktop): allow Windows proxy cleanup * test(desktop): avoid socket cleanup timing * fix(desktop): surface Local Control status * fix(desktop): simplify Local Control window * fix(desktop): harden Local Control pairing * fix(desktop): bind Mac wake lock to app |
||
|
|
60458f5e37
|
fix(serve): Coordinate caller-supplied session IDs (#8415)
* fix(serve): coordinate caller-supplied session IDs Complete daemon-wide admission across REST, ACP, workspace generations, SDKs, and MCP. Closes #8411 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(serve): wire session bridges in hot-reload harness Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(serve): address review round for caller-supplied session IDs (#8415) Restore the observability and fail-loud guarantees flagged in review: log every session-id admission routing failure, name the live foreign owner workspace in restore conflicts, make the ACP dispatcher's admission dependency required so load/resume cannot run on a mount without one, and require mountAcpHttp hosts to inject the daemon-wide admission instead of silently building a weak fallback. Harden the SDK WS transport against environments without global fetch and against non-capabilities 200 envelopes, and align the design doc with the implemented restore-sharing and persistence-failure semantics. * fix(sdk): harden session ID capability fallback Preserve REST capability errors, fail closed on malformed envelopes, retain restore routing diagnostics, and align retry documentation. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(serve): normalize restored session IDs Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(session): preserve mixed-case legacy session access Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
39377fcff3
|
feat(daemon): add batch skill toggle API (#8664)
* feat(daemon): add batch skill toggle API * test(serve): update capability integration baseline * fix(daemon): apply skill batches atomically * test(daemon): pin Skill batch toggle contracts and fix docs examples * test(daemon): pin Skill batch toggle mutants flagged in review * test(daemon): cover Skill batch toggle edge cases * docs(daemon): clarify Skill batch toggle contract notes from review Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(daemon): pin Skill batch toggle cap semantics and SDK surface shape * test(daemon): pin Skill batch toggle mutants flagged in round-5 review --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
1a5d1c445e
|
fix(core): confirm read-only git commands when repo config executes programs (#8575) (#8645)
* fix(core): confirm read-only git commands when repo config executes programs (#8575) Whitelisted read-only git sub-commands (status, diff, log, show, ...) are auto-approved based purely on command text, but git can execute programs configured in the repository-local config while running them: diff.external, core.fsmonitor, core.pager / pager overrides, diff driver textconv, core.askpass, credential.helper, core.sshCommand, remote proxies, ext:: remote URLs, gpg.program. A planted .git/config could turn an auto-approved command into arbitrary code execution. Add a synchronous repo-local config probe (bounded stat walk + small file reads, fail-closed) shared by the AST and regex classifiers: when a git command would classify as read-only and the repo-local config reachable from the execution cwd contains program-executing keys, the verdict is downgraded so the command requires confirmation. Global/system config is deliberately out of scope (the user's own setup, not a cloned-repo attack surface). All permission entry points (shell tool, monitor tool, permission manager, memory-scoped agent policy) now pass the execution cwd to the classifier. Classifier APIs only gain an optional parameter; behavior without cwd is unchanged. * fix(core): close two probe gaps from review of #8575 - Speculation gate now receives the execution cwd: speculated shell calls bypass the permission flow, so evaluateToolCall passes cwd (and the shell directory arg, which takes precedence) into classifyShellCommandSafety. A speculated `git diff` in a repo with diff.external planted now hits the boundary instead of executing. - Probe reads `.git/config.worktree` of the main checkout too — with extensions.worktreeConfig enabled git reads it for the main worktree, so a key planted there no longer bypasses the probe. - plan-mode shell policy passes its effective cwd to the classifier for consistent classification (no execution hole there; consistency). - Document bare repos as out of scope. - Add end-to-end integration test driving the real probe + classifier through ShellToolInvocation.getDefaultPermission (no fs mocking). * fix(core): honor Git worktree config semantics * fix(core): fail closed on opaque git config constructs (#8575) Round-3 hardening of the config probe, closing bypasses found in local security review (all empirically reachable via attacker-written .git/config): - Section headers the minimal parser cannot interpret (e.g. `]` inside a quoted subsection) now fail closed instead of silently dropping the entries beneath them. - Inline `[section] key = value` lines are parsed instead of discarded. - Unparseable `.git` pointer files fail closed like unreadable ones. - include/includeIf entries are flagged rather than resolved: their targets can live outside `.git` (e.g. tracked working-tree files). - core.gitProxy added to the program-valued keys (git:// transport via whitelisted `git remote show`). - Document the cd-into-another-repo limitation in the module doc. - Add the missing PermissionManager cwd-threading contract test (dirty repo config → ask, clean → allow) and regression tests for each behavior above. * fix(core): track cd in git config probe; close filter/url bypasses (#8575) Round-4 hardening from the local security/correctness review — each item was empirically demonstrated against the prior head: - Compound commands now track cd/pushd/popd: statically resolvable targets move the probe's base directory (same-repo `cd subdir` stays read-only), unresolvable targets (`cd`, `cd -`, `cd $VAR`, `popd`, quoted/expanded targets) downgrade later git segments. Closes the `cd <dirty-repo> && git status` bypass in both the AST and regex classifiers, including tree-sitter's nested-list chains. - filter.<name>.clean/smudge/process flagged: `git diff` runs worktree content through the configured clean filter with no extra flags. - url.<base>.insteadOf rewrite targets starting with ext:: flagged (combined with protocol.ext.allow in the same file this executes on whitelisted `git remote show`). - Config reads are size-capped at 1 MiB and fail closed above it (DoS guard for the synchronous permission path). - Boolean pager overrides (pager.<cmd> = true/false) no longer flagged. - Added the missing wiring contract tests: PermissionManager config.getCwd() fallback, memory-scoped agent shell policy, plan-mode shell policy (including the directory-param override). * fix(core): provide getTargetDir in speculation test mocks (#8575) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): harden git-config exec probe against cd-tracking bypasses (#8575) Address review round 1 findings on the repository-local git config execution probe: - Track cd/pushd across every sibling-statement sequence (program, brace group, subshell body), not only `&&` lists; propagate the directory out of brace groups and redirected/negated wrappers. - Respect list-operator semantics: cd state no longer leaks across `||` or `&`, and non-`&&` sequential statements keep the prior directory in the safety equation. - Resolve cd targets strictly: skip flag arguments (`-P`/`-L`/`-e`/`--`), reject flag-only and multi-operand forms, accept only statically unquotable word/string/raw_string targets (no concatenation, ANSI-C quoting, backslash escapes, or expansions), and fail closed when the target is missing or not a directory. - Probe git discovery more faithfully: resolve symlinks (realpath), treat a directory that is itself a git directory (bare repos, submodule storage) as a repo, fail closed when the search-depth budget exhausts, decode config values and subsections the way git does (quoted-segment concatenation, escapes), and add diff.<driver>.command and core.alternateRefsCommand to the program-valued keys. - Scope fixes: fall back to the scoped execution root when the memory agent shell probe has no cwd; resolve compound-command defaults against the full command so a segment rule cannot override the cd-aware verdict; keep sub-commands after a directory change in the confirmation scope for both the shell and monitor tools. - Tests: regression coverage for every fix plus mutation-checked wiring tests; skip the chmod-based EACCES simulation on Windows/root; use a relative submodule gitdir pointer; parametrize filter clean/smudge/process. * fix(core): close round-2 review findings for git-config exec probe (#8645) * fix(core): flag deprecated dot-form git config sections in exec probe (#8645) * fix(core): probe core.hooksPath targets in git-config exec probe (#8645) core.hooksPath was listed in PROGRAM_VALUED_KEYS, so any repo with the key set — every husky/lefthook install, and the worktrees Qwen itself creates — downgraded all whitelisted read-only git commands to ask. The key names no program, it only redirects hook lookup, so resolve it the way git does (~ expansion, relative anchored at the worktree root) and probe the target directory for executable read-only-triggered hooks exactly like the default hooks directory. * fix(core): probe submodule storage configs in git-config exec probe (#8645) * fix(core): tighten git config safety checks * fix(core): address verification findings for git-config exec probe (#8645) * refactor(core): reset git config probe to issue scope * fix(core): use Git config semantics for read-only probes --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
1cbf2e8fc7
|
feat(ci): auto-assign issues to area owners from labels (#8668)
* feat(ci): auto-assign issues to area owners from labels Route labelled issues to a maintainer with push access, without putting issue text in the path of a write token. Assignment is a pure function of the issue's labels and a checked-in label -> owner map, evaluated by a standalone workflow on issues:labeled. No model runs in the assignment path and the script never reads issue title, body, or comments, so untrusted issue text cannot select an assignee. Owners come from CODEOWNERS (asserted by test) and every candidate is re-checked against the collaborator permission API before the write, so editing the map cannot grant access. Among eligible owners the least loaded wins, rotating by issue number to break ties. * fix(ci): decouple issue owner map from CODEOWNERS CODEOWNERS answers who owns a code path, which is narrower than who may be assigned an issue in an area: the repository has ~44 collaborators with push access against 7 CODEOWNERS entries, so the membership test would have rejected legitimate additions such as admins and maintainers who own no path. Drop that assertion and document the actual process for adding owners. The live collaborator permission check remains the boundary. Add the validation the map does need: duplicate owners would skew load balancing, and duplicate area names would silently shadow each other under first-match-wins. * fix(ci): keep issue ownership triggers disjoint * fix(ci): recheck issue owner assignment before write * test(ci): cover issue owner label recheck * docs(ci): Correct CODEOWNERS count in issue assignment rationale * fix(automation): preserve autofix issue ownership * feat(ci): widen core issue owner pool to active repository maintainers * feat(ci): add four more collaborators to core issue owner pool * fix(ci): tighten issue-owner map validation and sync trigger docs * fix(ci): tighten issue-owner assignment tests and login validation (#8668) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
33b124321c
|
feat(core): support Qoder plugin extensions (#8661)
* feat(core): support Qoder plugin extensions * fix(core): address Qoder extension review feedback * fix(core): handle annotated tags and unsafe parse errors * fix(core): harden Qoder conversion edge cases * fix(core): sanitize Qoder conversion inputs * fix(core): address Qoder extension round-3 review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): honor explicit marketplace selection over Qoder manifest * fix(core): preserve nested plugin update provenance --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
bb8f2c0129
|
fix(cli): scrub inherited loader env vars from daemon session subprocesses (#8663)
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
* fix(cli): scrub inherited loader env vars from daemon session subprocesses Daemon-mode sessions bound to one workspace inherited loader-affecting env vars (NODE_OPTIONS with dev-harness --import hooks, NODE_PATH, preload-class vars) from whatever shell launched the daemon, so subprocesses in another workspace resolved modules through the launching checkout's tree (fixes #8653). Scrub the loader subset of RELOAD_EXCLUDED_KEYS from process.env at the two process boundaries that host sessions: the daemon after freezing its boot env (the frozen copy keeps loader vars so dev-mode ACP children can still boot), and the ACP child after the relaunch/sandbox handoff (the respawned child re-scrubs itself). Fixes #8653 * fix(cli): reject loader env keys in initial .env load and log scrubs A trusted workspace's .env could re-populate the loader-key slots that scrubInheritedLoaderEnv() emptied in the daemon process, because canApplyParsedEnvKey applied RELOAD_EXCLUDED_KEYS only on reloads. Reject the loader subset on every .env application path so one workspace's loader hook cannot reach other workspaces' session subprocesses through the shared daemon env. Also make the scrub return the removed keys and emit a stderr breadcrumb naming them at both boundaries, so a session subprocess missing an inherited var can be traced back to the scrub. * fix(cli): reject loader env keys in serve fast path before env freeze * fix(cli): deny npm_config_node_options and report rejected loader keys Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): match loader env keys case-insensitively and report settings.env rejections * fix(cli): canonicalize loader env key spellings and scrub channel daemon workers npm maps non-leading underscores in npm_config_* keys onto hyphens, so npm_config_node-options injected NODE_OPTIONS exactly like npm_config_node_options while slipping past every loader gate and scrub. Canonicalize case and underscore/hyphen spelling on both sides of the loader-key membership test, covering .env loads, settings.env application, the serve fast path, and the inherited scrubs. Channel daemon workers are spawned with the daemon's pre-scrub base env but are not ACP children, so they never ran the self-scrub; mirror the ACP-child scrub at the worker entry so nothing a worker spawns inherits loader vars into another workspace. Scope the settings.env rejection warning per workspace so a multi-workspace daemon reports every workspace's rejection instead of deduping them all under one label, revert the unread loadServeFastPathEnvironment return value to void, and pin the buildRuntimeEnvironment settings.env gate and the consume-once stash reset with discriminating tests. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address review findings for loader env denylist - report rejected loader keys through the daemon log after boot (per-workspace .env loads were silent once boot stderr was gone) - accumulate serve fast-path rejected keys across loads instead of overwriting, dedupe, and use normalized env file paths for rejection sources - restore scrubbed inherited loader env vars on embedded runQwenServe close() - add regression tests for ENV scope coverage, reporter dedupe, fast-path accumulation, and post-boot daemon-log diagnostics - scope docs: top-level env rejection does not apply to mcpServers[].env / hooks[].env; document serve loader-scrub behavior * fix(tests): add writeStderrLineSafe to stdioHelpers mocks and sync env guard allowlist * fix(cli): scrub loader vars from the daemon base env and retighten the denylist The frozen daemonRuntimeBaseEnv was captured before the launch-env scrub, so daemon-spawned session processes still booted under the inherited loader — the child-side post-boot scrub ran after Node had already consumed NODE_OPTIONS. The base env is now scrubbed before the freeze (except under the DEV=true harness, whose .ts entries need the tsx loader), and close() restores the host's launch env from a pristine snapshot. Denylist scope now follows the injection-vs-search-path split: adds the npm config-file redirect keys, ZDOTDIR, and a BASH_FUNC_* prefix rule; moves ENV/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH back to their reload-only tier (mainstream toolchain compatibility); blocks QWEN_CLI_ENTRY and NODE_EXTRA_CA_CERTS from project .env files. The ACP-child scrub is gated on the daemon stamp (QWEN_CODE_SERVE) so direct editor ACP integrations keep the user's exported environment, and the daemon's per-workspace .env rejections are now reported from buildRuntimeEnvironment. * fix(cli): block DEV spoofing, case-insensitive env exclusions, serve boot env restore (#8663) Address round-6 review: DEV joins the hardcoded project-env exclusions so a workspace file cannot disable the daemon's loader-env scrub; the hardcoded tier is enforced case-insensitively (Windows env lookup is case-insensitive) via isHardcodedProjectEnvExclusion at every application gate; runQwenServe's catch restores the scrubbed launch env and detaches the rejection reporter when startup fails after the scrub. Tests gain the matching regressions, home-env hermeticity, source-scoped warning filters, and tmpdir cleanup; the unreachable reload delete-pass loader guard and its vacuous test are removed. * fix(cli): match the reload-excluded env tier case-insensitively too Round-6 follow-up: R6-3 named RELOAD_EXCLUDED_KEYS.has() among the gates a case variant slips, but the hardcoded-tier fix left the reload-only keys (QWEN_SERVER_TOKEN, PATH, HOME, TMPDIR, …) on exact-case matching. On Windows a lowercase twin names the same OS variable, so a mid-session settings.env/.env edit could still rotate the daemon token or move PATH through a case respelling. Fold the reload tier the same way and pin it with a reload-behavior regression test. Also note DEV in the settings.md exclusion docs. * test(cli): redirect HOME in environment.test.ts for full home-env hermeticity The source-scoped warning filters fixed the warning-count assertions, but the process.env assertions (e.g. 'never applies entrypoint or trust-anchor keys') still read state a real home .env can pollute: home scope deliberately bypasses the hardcoded exclusions, so a dev machine with QWEN_CLI_ENTRY in ~/.env applies it and fails the test while CI stays green. Redirect HOME/USERPROFILE to an empty temp dir in beforeEach — verified by running the suite with HOME pointed at a poisoned home. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
59b750fc4d
|
feat(serve): Expose active work state (#8588)
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
* feat(serve): expose active work state Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(serve): rebuild active-work reporting on channel-wide snapshots Reworks the active-work signal after review. Three changes of substance. Drops the 45s heartbeat watchdog entirely. It inferred "this channel is dead" from "one Session stopped reporting" and killed the whole channel, taking every Session on that process with it — including on a suspend, a long event-loop stall, or a single dropped notification. Channel liveness is a transport concern and gets its own mechanism. Replaces the per-Session boolean with a channel-wide snapshot of named holds, derived on every report from the owners of the work (the registry's unfinalized set, the notification queue) rather than from a ledger kept alongside them. Full snapshots make a dropped report self-correcting in both directions, and a Session's absence from one is positive evidence the child released it. Agent holds now use hasUnfinalizedTasks()'s predicate, closing the cancel to finalizeCancelled() window where a cancelled agent looked idle and its terminal notification could be stranded. Leaves prompts out of the child's report: the daemon accepts, queues, dispatches, and settles them, so its own count is authoritative and covers the FIFO wait the child cannot see. A snapshot is flushed ahead of the prompt response so a hold the prompt left behind is on the wire before the daemon drops that count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(serve): confirm idle before closing, and grade the health signal Completes the active-work rework with the two facts a restart controller was still missing and the one guarantee automatic cleanup was missing. Automatic cleanup no longer destroys a Session on the strength of a cached snapshot. It asks the child to close only if unheld, and the child answers under its own close gate — with the gate held no prompt is admitted and no automatic turn starts, so a hold cannot appear between the check and the teardown. A refusal hands back the current holds and the daemon adopts them. An unanswered request is neither retried nor assumed: the Session stays, and the next snapshot settles it, because a Session absent from one has provably been released. Every automatic path — detach, attach rollback, prompt settle, notification settle, a child reporting itself idle — now funnels through one decision point instead of four near-copies. Health gains activeWorkReporting and activeWorkStaleMs. Without them activeWork:false cannot be told apart from "no child told me anything", which is the one case where acting on it is unsafe. Freshness is graded by the daemon rather than the controller, since the cadence is negotiated per channel; a stale snapshot or a child omitting a category degrades the grade instead of silently narrowing what the boolean covers. Tests: acp-bridge 489/489, acpAgent 383/383, Session 534/534, serve suites 1188 with one pre-existing cross-file flake in the Live Appshot integration tests (reproduces on the unmodified tree, failing a different test each run). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): contain snapshot-collection failures, and repair two Session mocks CI caught two things the local runs missed. The reporter's snapshot construction was unguarded. Only the send was wrapped, so a throw while collecting a Session's holds escaped through setInterval and queueMicrotask as an uncaught exception — capable of taking down the ACP child — and through flush() into the prompt path, turning a reporting problem into a failed prompt. Collection is now wrapped and a failed snapshot is abandoned whole rather than sent partially: a Session missing from a report reads as released, and one reported with no holds reads as safe to close, so publishing a partial snapshot would actively invite the daemon to destroy live work. Sending nothing lets the daemon's copy age instead, which its freshness grading already treats as untrustworthy and retains. flush() no longer rejects. Session.review-lease and Session.worktree mock the background-task registry without setStatusChangeCallback, so constructing a Session threw. That break arrived with the original commit, which verified only Session.test.ts; the sibling Session.*.test.ts files were never run. Both mocks now carry the methods the constructor and the hold collector need. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(serve): prove the reporter contains collection and transport failures The previous commit added the guard but could not have demonstrated it: the same commit also gave the acpAgent Session mock a collectActiveWorkHolds, removing the very condition that triggered the throw. The unhandled error disappearing was therefore explained by the mock alone, and active-work-reporter.ts had no tests at all. These cover the escape routes that matter — the interval timer, the coalescing microtask, and flush() on the prompt path — plus the choice to abandon a whole snapshot rather than send a partial one, since a session omitted from a report reads as released and one reported with no holds reads as safe to close. Verified by removing the guard: five of the nine fail with the collection error escaping, and pass again once it is restored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): make every automatic teardown ask before destroying Self-review of the previous revision found that this PR had promoted a cached child report from a hint into the authority that permits destroying a Session. Four teardown paths consulted it, their guards disagreed with each other, and each was weaker than what main had. The four are one defect with four exits, so they are fixed as one change. Absence from a snapshot no longer authorizes teardown. Because reports are complete, a Session the child omits holds nothing on the child side — so absence and reported-with-no-holds are the same fact and now take the same path. The separate absence loop is gone; it lacked the subscriber and client guards `maybeCloseIdleSession` applies, so one snapshot could destroy a Session with a live SSE subscriber and a registered client. That contradicted this PR's own claim that an unreported Session is retained, and the old test asserted the destruction. Both are corrected. A conditional close is now marked in flight across the whole confirm-then- teardown span, and attach, prompt, and rewind refuse a Session in that state exactly as they refuse one already closing. `closeSessionImpl` sets `closing` synchronously, but the round trip in front of it is an await of up to ten seconds; on main the guard sequence ran straight into teardown, so splitting it is what opened the window. A snapshot older than the freshness window stops counting as evidence. Staleness was already computed, but only to grade health, never to gate destruction — so a child that went quiet after one empty report left a cache that permitted reaping indefinitely. Never-reported and gone-quiet now land in the same retained bucket. Reclaiming a channel that has truly stopped answering belongs to transport liveness, not here. The idle reaper asks the child too. Its TTL says the client stopped caring, which is not the same as the child having nothing left to run. Health coverage is exposed as counts and graded once daemon-wide, because grades do not compose: a runtime with zero Sessions is vacuously `full`, and folding that in let an empty workspace vouch for another workspace's unreported Sessions. `activeWorkStaleMs` now measures only covered Sessions, so it can no longer report positive staleness beside a grade saying nothing is covered. Also: bound snapshot `sessions[]` and `holds[]` so a buggy child cannot make the daemon walk an unbounded structure per report, and retract the background-task status callback by identity rather than blanking a single-slot setter the TUI also uses. Tests: the absence test now asserts retention under a registered client and under a live subscriber; new regressions cover the recovered lost close response, the stale-snapshot gate, admission refusal during a conditional close, the reaper's confirmation, the oversized-snapshot discard, and the mixed empty/uncovered health aggregate. * fix(serve): make unknown a reason to ask, not a reason to skip Triage review found that the design doc, the PR description, and the comment on `entryHasActiveWork` all promised the daemon *asks* the child about a Session it has not heard about, while no code path ever did: `entryHasActiveWork` returns true when the child's side is unknown, and the cleanup path returned early on exactly that. The finding predates the guard rework and survived it unchanged. Skipping on unknown looks like the safe direction and is in fact the worse failure. Nothing resolves it — a Session on a channel that went quiet is retained forever, and the idle reaper skips it too, so there is no path out at all. Asking resolves it definitively: the child answers under its own close gate whether or not its snapshots are arriving, the round trip is bounded, and every non-answer still retains. So the predicate is split by what it actually knows. `childReportsHeldWork` is positive knowledge only; `childWorkIsUnknown` is the absence of a gradeable report. The health surface ORs both, because a controller must never read "nobody told me" as "nothing is running". Automatic cleanup blocks only on known work and lets unknown through to `confirmChildUnheld`. Also moves `parseActiveWorkSnapshot` out from between two import blocks (pure relocation, no logic change) and aligns the doc wording, including the shared-guard table, with what the code now does. * fix(serve): close three teardown races the confirm window opened Review found three ways the conditional close can still destroy a live Session. All three share a cause: the round trip turned a synchronous guard-then-teardown into an awaited span, and three things that were previously impossible to observe mid-teardown now are. **A restore in flight looks exactly like an abandoned Session.** `session/load` registers the entry before awaiting `artifacts.restore()` and `seedSessionUpdates()`, and registers its first client only after — so for that whole window there are no clients, no subscribers, nothing held, and the child answers the conditional close truthfully. The snapshot trigger this PR added fires inside it. Excluded in `entryIsAutoCloseCandidate` rather than at the snapshot trigger, so the reaper's TTL elapsing inside a slow restore is covered too. `pendingRestoreIds` already existed but was read only by `hasNoChannelWork`, never by the close funnel. **Teardown re-resolved the target by id without re-checking identity.** `closeSessionImpl` does a fresh `byId.get`, and the id can be re-registered to a different entry during the round trip: an explicit kill removes this one (kill ignores the in-flight flag by design, keeping its force semantics) and a `session/load` for the same persisted id registers a fresh one. The stale continuation then tore down the newly restored Session under its just-attached client. One identity re-check after the await. **The restore path was not upgraded to the new admission predicate.** `sendPrompt`, `rewindSession`, and single-scope attach check `isClosingOrAuthorizingClose`; `restoreSession` still checked bare `closing` at both its guards, so a client could attach inside the window and lose the session under it. That directly contradicted the `closeIfChildUnheld` comment claiming every admission path checks the flag. Its `racedEntry` branch had no closing guard at all — a narrower pre-existing hole, same defect, same predicate. Regression test covers the restore-path admission refusal. The other two need a mid-restore snapshot and a kill-then-reload interleave that the mocked-channel harness cannot stage honestly; both are pinned by reading the code paths, which is weaker and worth saying. --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fd76d4ddde
|
fix(cli): let ESC cancel ongoing work before popping queued messages (#8353)
* fix(cli): let ESC cancel ongoing work before popping queued messages When the agent is actively responding (streamingState === Responding), InputPrompt's ESC handler consumed the key before AppContainer's global cancel-work handler could fire. Users had to press ESC 3 times (pop queue, clear input, cancel work) to stop the agent. Skip the pop-queue-into-input and double-ESC-clear logic when the agent is responding, returning false so the key propagates to the global handler which cancels the ongoing request. The up-arrow key still pops queued messages into the input at any time. Fixes #8201 * fix: narrow ESC fall-through to empty buffer + add regression tests Address wenshao's review on #8353: - Gate the return false on buffer.text === '' to prevent BaseTextInput's default ESC from silently wiping typed input without double-press confirmation - Add resetEscapeState() before return false to clear any pending escPressCount/escape-prompt timer - Add two regression tests with streamingState: StreamingState.Responding: 1. queue non-empty + ESC -> popAllQueuedMessages NOT called 2. buffer has text + single ESC -> buffer NOT cleared * fix: correct ESC comment to reflect KeypressContext broadcast model Address bot suggestion: the comment claimed returning true 'consumed the key before the global cancel-work handler could fire', but KeypressContext broadcasts to all handlers regardless of return value. The real mechanism is that popQueueIntoInput() fills the shared buffer, steering AppContainer's handler into its 'input has content -> double-press to clear' branch instead of the cancel-work branch. * fix: correct comment to accurately describe return false -> BaseTextInput fall-through Bot suggestion: the comment said returning false 'avoids' BaseTextInput's wipe, but return false actually *enables* it (BaseTextInput only short-circuits on truthy returns). The buffer is safe because the buffer.text === '' gate makes the wipe a no-op, not because return false prevents it. Reworded to make this explicit and warn against relaxing the gate. * test(ui): add positive AppContainer ESC cancel regression test The PR's Responding guards were pinned only by InputPrompt-side tests (queue not popped; non-empty buffer preserved). Add the positive case the review asked for: while Responding with an empty buffer and queued follow-ups, a single Esc reaches the global handler's cancel-work branch (cancelOngoingRequest called once) and the queue is not consumed. #8201 * test(ui): clarify ESC cancel test scope vs end-to-end drain The positive ESC cancel test asserts popAllMessages is not called, but the comment framed it as 'must not consume the queue' end-to-end. In production that exact cancel path DOES drain the queue back into the buffer via the cancel handler (cancelOngoingRequest -> onCancelSubmit -> popAllMessages), under the 'never silently drop queued work' invariant. The assertion only holds because cancelOngoingRequest is replaced by a spy here, severing that hop. Reword the comment to describe the real contract: the global keypress handler itself doesn't pop the queue (InputPrompt owns that and skips it while Responding; #8201), while the end-to-end drain is a separate hop severed by the spy. Addresses the review finding on AppContainer.test.tsx:2405. * test(ui): pin ESC return-false branch and dedupe getGlobalKeypress Address two review suggestions on the ESC cancel tests: - The `return false` branch in InputPrompt.tsx (Responding + empty buffer + empty queue -> defer to AppContainer's cancel-work branch) had no test coverage: reverting it left all 334 tests green. Add an InputPrompt test that pins it (no queue pop, no buffer mutation). - The AppContainer cancel test inlined a byte-identical copy of the getGlobalKeypress() helper that already existed ~2600 lines down in the Ctrl+O describe block. Hoist the helper to the outer describe so both blocks share one definition of the fragile toString() discovery idiom. * test(ui): escape raw ESC byte in cancel test fixture Per review (R4-1): the sequence literal embedded a raw 0x1B control byte that renders as an empty string in diffs and truncates grep output, so the fixture was unreadable. Use the escaped form matching the sibling vim-INSERT fixture on the line above. * test(ui): assert buffer stays empty on ESC cancel + fix test comment Per review (R5-1/R5-2): the flagship #8201 test asserted only the mechanism (popAllQueuedMessages not called), not the effect (buffer stays empty so AppContainer takes its cancel branch). Add the buffer assertion. Also correct the return-false test comment: deleting that branch leaves the test green (KeypressContext.broadcast ignores return values, BaseTextInput's clear is a no-op on empty buffer), so the test pins the no-side-effect contract, not the branch itself. * test(ui): cover queue+text ESC and double-ESC-clear while responding Per review (R6-1/R6-2): the pop-skip guard was pinned only for the empty-buffer case, and the double-press clear contract had no test under Responding. Add: - non-empty queue AND typed text: ESC does not pop the queue and preserves the buffer (pins the guard regardless of buffer content). - double-ESC while Responding: first ESC preserves typed text, second clears it (pins the double-press contract this diff preserves). * test(ui): dedupe escKey fixture and tighten double-ESC timing Per review (R7-1/R7-2): the double-ESC test spaced presses with the default 150ms wait (~30% of the 500ms window); use 50ms to match the sibling double-ESC test. Hoist the escKey fixture to the Cancel Handler describe scope so both tests share one definition (matching the getGlobalKeypress hoist this PR already did). * test(ui): add missing removeGoalTurns to cancel-handler queue mock Per review: the cancel-handler test's useMessageQueue mock omitted removeGoalTurns, a required member that every other queue-mock override in this file includes. The real cancel handler calls removeGoalTurns() before popAllMessages(); the test passed only because cancelOngoingRequest was a spy severing that hop. * test(ui): reuse getGlobalKeypress in vim-INSERT cancel test Per review (R9-1): the vim-INSERT test still inlined a handler-discovery loop matching on 'handleExit', duplicating the hoisted getGlobalKeypress helper (matching TOGGLE_THINKING_EXPANDED). Both tokens occur in the same handleGlobalKeypress closure, so the two idioms can only drift. Reuse the shared helper. * test+docs(ui): escape ESC byte in shared fixture and document Responding Esc Per review (R10-1/R10-2): the shared escKey fixture embedded a raw 0x1B control byte (invisible in diffs, truncates grep). Use the escaped form. Also update keyboard-shortcuts.md: Esc now cancels the ongoing request while the agent is responding instead of moving queued messages back into the input. * docs(ui): correct ESC/Up-Arrow queue-pop description to match code Per review (R11-1): the previous wording said queue pop happens only when idle, but Up Arrow pops in any state (no streamingState guard) and Esc pops whenever not actively responding (including WaitingForConfirmation). Reword to match the code, and note that the responding-cancel only fires when the input is empty. * refactor(ui): drop dead Responding ESC guard per maintainer review wenshao's mutation test showed guard #2 (Responding + empty buffer -> return false) is dead code: with it gone, control falls to the escPressCount===0 branch which returns true on an empty buffer, and KeypressContext.broadcast ignores handler return values anyway - AppContainer's own cancel branch acts on the empty buffer either way. Remove it, fold the subscription-ordering invariants it relied on into guard #1's comment, and document that only Responding is gated (do not broaden to !== Idle or ESC becomes a no-op during a tool confirmation). Also tighten the docs wording: cancelled queued messages are moved back into the input, not preserved. #8201 * docs(ui): correct four review comments in ESC cancel path Round-13 review (no blockers) flagged comment inaccuracies that could misdirect future debugging: - R13-1: the invariant comment pointed at an integration test that does not exist - note the harnesses mock each other's side instead. - R13-2: the regression-test comment still said the branch returns false and that AppContainer acts on return values; it returns true and broadcast ignores return values. - R13-4: the docs row claimed Up Arrow/Esc pop in any state, but during WaitingForConfirmation Composer unmounts InputPrompt (isInputActive admits only Idle/Responding), so neither key pops. - R13-5: the guard comment warned against broadening to !== Idle as if WFC ran this branch; it never does because the component is unmounted. No behavior change. #8201 * docs(ui): correct subscription-order and double-ESC-cancel comments Round-14 review: the invariant comment overstated subscription order as load-bearing - the Responding pop guard skips the pop in either order, and InputPrompt re-subscribes after AppContainer on any remount (e.g. a tool-confirmation round trip), so only the buffer.text-liveness invariant matters. And the double-ESC clear comment now notes it composes with AppContainer's cancel on the same keypress in the initial order but lands on the next press after a remount. No behavior change. #8201 --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
88a325bce9
|
feat(workflows): add cooperative pause and resume (#8320)
* feat(workflows): add cooperative pause and resume * fix(workflows): restrict pause to background runs * fix(cli): clarify foreground workflow pause errors * fix(core): preserve dispatch errors across cancellation * test(core): cover late workflow state callbacks * fix(workflows): address review suggestions (#8320) - Rename misleading `terminal` local to `presentation` in BackgroundTasksDialog - Fix vacuous `toContain('p')` assertion to `toContain('Background tasks + p')` - Fix vacuous gate assertion with macrotask yield in scheduler test - Add over-count cap test for `onAgentCompleted` past dispatched count - Add pausing-state approval parking test - Remove dead `concurrencyLimiter` module (no production consumers) * test(workflows): pin review-flagged mutation-surviving branches (#8320) * test(cli): use valid agent status in detail-view reset test (#8320) * test(workflows): harden pause-gate settle probes with a full flush (#8320) * fix(workflows): address round-5 review findings (#8320) * test(ci): sync review timeout assertions with repository variables (#8320) * fix(workflows): address round-6 review findings (#8320) * fix(workflows): address round-7 review findings (#8320) * fix(workflows): address round-8 review findings (#8320) * fix(workflows): address round-9 review findings (#8320) * fix(workflows): address round-10 review findings (#8320) * fix(workflows): address round-11 review findings (#8320) * fix(workflows): address round-12 review findings (#8320) * fix(workflows): address round-13 review findings (#8320) * fix(workflows): address round-14 review findings (#8320) * fix(workflows): address round-15 review findings (#8320) --------- Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> Co-authored-by: qwen-code-bot <qwen-code-bot@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> |
||
|
|
d3164572f3
|
feat(channels): add Feishu ask-user question cards (#8578)
* feat(channels): add Feishu ask-user question cards * fix(channels): harden Feishu question-card handoff and feedback paths * fix(channels): anchor Feishu card-creation timeout at creation start (#8578) * fix(channels): skip settled question-card delivery, retry input-request finalization - present() returns early when the request settled before listener registration (stop/cancel landing in the pre-presentation await): the card is no longer delivered just to be patched terminal, which left a spurious actionable-looking card when the patch failed. - endOutputCardBeforeInputRequest now retries the final patch with stripTables before deleting the card and re-sending plain text, mirroring onResponseComplete and the throttled update path. - truncateCardText budgets the 4-char fence prepend so rebalanced content stays within MAX_CARD_CHARS, matching onResponseComplete. * test(channels): cover Feishu question-card review findings - stop real network leaks: mock addReaction/removeReaction in the two handoff tests that drove onPromptStart with the real implementation (each run hit Feishu's tenant-token endpoint). - assert the OnIt reaction add/remove lifecycle positively. - pin settled-before-registration presentation to zero card delivery. - regression tests: input-request table-stripped retry, finalizing race (throttled timer cleared before the final patch), late card deletion when the input request lands mid-creation, abandoned creation-timer guard. - routing coverage for toast-only handled callbacks (no execute). - parse rejections: empty/malformed multi-select values; cancel-side name<->operation_id mismatch; visible submit/cancel button labels; cancelRun terminal label; submitted answers in patch-failure fallback; truncation tail-keeping and fence-rebalance cap. - harden subset toMatchObject assertions to strict toEqual. * fix(channels): type Feishu card-delivery errors; cover controller wiring seam sendInteractiveCard now throws FeishuCardDeliveryError (with the HTTP status when available) so createStreamingCard classifies delivery failures by error type instead of string-matching three message literals that could drift under rewording. Add an adapter test that keeps the real FeishuQuestionCardController with fetch mocked, asserting the POSTed body carries the question-card JSON (form name, question text, request id) — the constructor wiring seam every routing test previously mocked away. * fix(channels): cap Feishu input-request card retry; harden question-card tests * fix(channels): drain in-flight Feishu card PATCHes before final patch (#8578) Also carry a concurrently-written terminal status into the released card entry, report card-delivery failures from a typed error detail instead of slicing the message, and document the silent empty-completed-turn tradeoff. Adds round-5 review test pins: wiring seams (sendFallback, patchCard, timeoutMs), spy hygiene, claim-time card content, fallback suppression, expiry-timer survival, and non-vacuous release-time timestamp anchoring. * fix(channels): coordinate Feishu card stop races and throttle bursts (#8578) - endOutputCardBeforeInputRequest now defers to Stop (entry guard plus post-await re-checks mirroring onResponseComplete) and releaseOutputCard carries settled stop state, so a stopped run is never labelled 已完成 nor followed by a contradictory terminal message - coalesce throttled card updates queued behind a stalled PATCH into one trailing run instead of a burst - log every FeishuCardDeliveryError from createStreamingCard - consolidate onResponseComplete truncation through truncateCardText - question controller: a not-accepted in-flight response no longer flips a projected 已取消 card to 已过期, and a cancel claim no longer re-patches the terminal card already delivered in the callback response - test hygiene: hermetic fetch mocks for token-failure tests, restored fetch spies, required lifecycle fixture fields, bracket index access * test(web-shell): wait for history search focus before Escape in smoke (#8578) * fix(channels): resolve Feishu card finalization races from review (#8578) * fix(channels): recover Feishu cancel callbacks missing button value (#8578) --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
52f4fbe0b3
|
feat(web-shell): install Extensions from archives (#8621)
* feat(web-shell): install extensions from archives * fix(web-shell): harden extension archive uploads * test(cli): align archive failure bridge assertion * fix(extensions): address archive upload review feedback * fix(webui): stagger extension archive upload timeout * fix(extensions): address archive upload round-4 review feedback * fix(extensions): address archive upload round-5 review feedback --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
b34a08d16f
|
fix(core): separate hook context from transcript display (#7948)
* fix(core): separate hook context from transcript display * test(ci): gate desktop transcript projection * revert: keep desktop CI scope unchanged * test: cover transcript display fallbacks * fix(transcript): address review feedback * fix(transcript): reconcile post-merge provenance paths * fix(webui): preserve legacy transcript concatenation * test(transcript): cover projection consumers * fix(transcript): consolidate hook context projection * fix(transcript): support single-field display provenance * fix(transcript): strip hook context with invalid metadata * test(acp): cover empty replay display text --------- Co-authored-by: destire-mio <248462155+destire-mio@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
681e30d54f
|
docs: clarify SDK interrupt behavior (#8711) | ||
|
|
92ff0a1363
|
perf(review): move remote matching into CLI (#8658)
* perf(review): move remote matching into CLI Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): use exit 7 for match-remote multi-match, drop dead field (#8658) * fix(cli): harden match-remote host resolution per review feedback (#8658) - Strip an explicit port from the input host before remote comparison; a port-bearing GHE verdict host could never match its own remote and a same-repo review was demoted to lightweight mode - Exit 1 only when git itself fails: a bare repository now resolves remotes like any other checkout, matching the documented contract - Inherit an operator-exported GH_HOST when --host is absent, the same resolution submit uses, so bare PR numbers on GHE clones match - Write the machine-read stdout line with loud writeStdoutLine so a failed write exits non-zero instead of exiting 0 with empty output - chdir out of the temp dir before rmSync in match-remote.test.ts (Windows locks a directory that is the process cwd) - Pin both SKILL.md match-remote hunks in SKILL.test.ts so reverting either path to model-prose matching fails a test - Correct the design doc's prompt-size accounting and host-resolution paragraph; label the e2e pointer as an untracked run archive * fix(cli): match partial-clone remotes and unify host resolution (#8658) * fix(cli): thread resolved GHE host through bare-number remote matching (#8658) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
0701b76b87
|
fix(core): refresh MCP session metadata without reconnecting (#8522)
* fix(core): refresh MCP session metadata in place * fix(core): isolate MCP metadata refresh * fix(core): harden MCP session metadata key and refresh (#8522) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
c2026882b7
|
fix(acp): emit context usage updates (#8513) (#8528)
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
4ec0371e61
|
feat(telemetry): attribute daemon-spawned sessions via channel (daemon/desktop) (#8670)
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
* feat(telemetry): add runtime and client attribution to usage statistics Daemon-spawned sessions (TS/Python/Java SDKs, Web Shell, Tauri desktop shell) all report properties.channel=ACP because the daemon spawns plain `qwen --acp` children with no channel argument. Add two stable dimensions to the default usage-statistics payload: - properties.runtime (cli|acp|daemon): the daemon marks every child it spawns with QWEN_CODE_SERVE=1 (ACP session children and channel workers), which takes precedence over the channel-based acp heuristic. - properties.client (vscode|desktop|desktop-shell, omitted when unknown): derived from the --channel value (VSCode/desktop) and the QWEN_CODE_DESKTOP env marker set by the Tauri desktop shell. properties.channel and app.channel are unchanged; the new keys are purely additive. See docs/design/telemetry-runtime-client-attribution-design.md. Issue: #8660 * chore(telemetry): use Qwen Team copyright header on new attribution files New files use "Copyright 2026 Qwen Team" per repository convention (see e.g. packages/core/src/utils/file-identity.ts), not the legacy Google LLC header inherited from the gemini-cli fork. * refactor(telemetry): report daemon attribution via channel, no new payload keys Per maintainer feedback: extend properties.channel instead of adding properties.runtime/client. getChannel() has no behavioral consumers (telemetry is its only reader), so channel is a pure reporting dimension. - resolve the ACP channel fallback from the daemon env markers: QWEN_CODE_DESKTOP -> desktop-shell, QWEN_CODE_SERVE -> daemon, else ACP - drop the runtime/client payload keys and the runtime-attribution module - keep the QWEN_CODE_SERVE spawn marker at both daemon spawn sites * refactor(cli): report Tauri desktop-shell sessions as channel=desktop Maintainer decision: the Tauri desktop shell shares the desktop client identity with the Electron app instead of getting a separate desktop-shell value. Also document the SDK split: SDK query() spawns the CLI directly with --channel=SDK (never daemon, unchanged), while the SDK daemon-client entrypoints ride the daemon bridge and report channel=daemon. * fix(cli): preserve daemon channel attribution * fix(cli): keep attribution markers out of home env bootstrap |
||
|
|
26352fcc6a
|
feat(external-context): Add optional Mem0 memory writes (#8507)
* feat(external-context): Add optional Mem0 memory writes Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(hooks): Preserve confirmation content visibility Render PreToolUse confirmation reasons literally and keep long confirmations accessible through the virtualized TUI. Add unit and interactive regression coverage for Mem0 write confirmations. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(external-context): Address memory write review findings Align Hook and MCP argument handling, distinguish definitive Provider rejections from ambiguous outcomes, improve deployment diagnostics, and document the write-back trust boundary. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(hooks): Refine plain-text confirmations Render URLs consistently, avoid persistent virtual viewport gaps, and document the literal-rendering and managed deployment boundaries. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(external-context): Support Auto Edit write confirmation Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(external-context): Harden write confirmations Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Measure virtual row height directly Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Preserve YOLO Hook confirmation content 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> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
028747aa41
|
feat(feishu): enrich observed contact labels (#8569)
* docs: design feishu observed contact enrichment * docs: add Chinese Feishu enrichment design * feat(feishu): enrich observed contact labels * fix(feishu): preserve enriched contact labels * fix(feishu): harden observed-contact label enrichment lifecycle * fix(feishu): bound label caches, honor observation recency, silence enrichment token failures (#8569) - hydrate runtime label caches from the newest observation per contact so stale group membership labels cannot overwrite more recent ones - cap the user/chat label, in-flight lookup, and write-dedup maps at 500 entries (matching the persisted registry) and evict oldest entries - route best-effort label lookups through a silent token refresh path so enrichment failures no longer write to stderr - add tests for silent token refresh, newest-label hydration, cache cap, and the persisted-observation reject path in hook ordering * fix(feishu): address observed-contact label review feedback (#8569) * Track core (non-silent) waiters on the shared tenant-token refresh so a silent-initiated refresh still logs token errors for joined delivery callers. * Short-circuit label lookups on the resolved names cache so evicted lookup entries do not trigger redundant API requests. * Re-hydrate label caches from the persisted registry after an in-lifetime cache eviction so the next initial write cannot clobber a persisted label with the raw ID. * Add mutation-proof regression tests for the channel-isolation filter, the list-failure swallow, the silent HTTP-error branch, and the 'unknown' label guard. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com> |
||
|
|
b40719a8bb
|
fix(cli): Run ACP agent fan-outs concurrently and past the tool-call cap (#8631)
* fix(cli): Run ACP agent fan-outs concurrently and past the tool-call cap The daemon's ACP session executed tool batches differently from the core scheduler in two ways that broke long agent fan-outs such as /review: runBounded — the runner for concurrent batches — forced the first three calls of any batch larger than the invalid-params threshold to run one at a time, then clamped the rest to concurrency 3, although agent calls are concurrency-safe and core's runConcurrently runs them at QWEN_CODE_MAX_TOOL_CONCURRENCY (default 10). A /review fan-out of 10-15 agents therefore ran almost serially. Agent-only batches now skip the serial prefix and the clamp: an invalid agent call fails in build() before any side effect, so the concurrent loop's near-threshold check still catches invalid-params loops just as fast. The per-turn tool-call cap halted unconditionally at model.maxToolCallsPerTurn (default 100) while core's LoopDetectionService treats the default as adaptive — past the soft cap a productive turn (diverse calls, no repetition) continues until a stuck-repetition signal or the hard backstop (soft cap x 10). A /review orchestrator needs well over 100 calls, so every high-effort review under qwen serve died mid-review at call 101. The daemon now mirrors core's checkTurnToolCallCap semantics, reusing the same thresholds. Measured on two high-effort /review runs (PRs #8522 and #8529): the baseline died at call 101 after ~8.3h each; after this fix both reviews run to completion in 4.4h / 5.3h, first fan-out wave 85m -> 33m, reverse-audit rounds 63-86m -> 25-35m. * fix(cli): regenerate settings schema after maxToolCallsPerTurn doc update (#8631) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Gate the daemon repeat halt on skipLoopDetection like core * fix(cli): Address ACP fan-out review: keep wide-batch results, shared cap predicate (#8631) - runBounded no longer aborts in-flight calls when loop detection fires in the capped race branch: wide batches keep in-flight results and only skip the unstarted tail, matching narrow-batch behaviour (nothing executed is discarded either way). - Extract shouldHaltOnTurnToolCallCap from core's checkTurnToolCallCap and call it from the daemon guard so the two runtimes share one halt predicate and cannot drift. - Hoist canonicalToolName into tools/tool-names.ts beside ToolNamesMigration; scheduler, loop detection, plan redaction and memory refresh now share the single alias resolver. - Correct the wrong-direction cap wording (the daemon undershoots an explicit cap / hard backstop — the batch check runs before execution; the adaptive soft cap is exceeded by design up to the backstop) in the daemon comment, settingsSchema.ts (schema regenerated) and settings.md, and scope the always-on-guard sentence to core-client sessions. - Tests: adaptive hard backstop, wide-batch loop tail skip, wide-batch keep-results, provider-duplicate counter exclusion, `task`-alias fan-out, getToolCallRepeatKey alias/key-order coverage; raise the fan-out concurrency deadline off the 2s wall clock. * fix(cli): Address review: complete loop-guard docs, pin test envs, drop dead export (#8631) * fix(cli): Address review: correct parity comment, pin halt semantics, test cross-response repeats (#8631) --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
edb420393e
|
fix(channels): manage DingTalk interactive card config (#8517)
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
* fix(channels): manage DingTalk interactive card config * test(cli): cover nested channel object validation * fix(channels): harden nested management metadata * fix(channels): isolate invalid management descriptors * fix(channels): isolate invalid management metadata from channel runtime * fix(channels): reject reserved unknown keys in management config upserts * fix(channels): harden channel management validation and editor checks Reject management descriptors that lack a fields array at registration so broken plugins are stripped to unmanageable instead of being advertised as manageable and failing every upsert with an unmapped TypeError. Reserve the top-level "type" field key and require enum fields to declare at least one option, both of which the settings store could never accept. Treat whitespace-only number drafts as empty in the channel editor, consistent with the module's other emptiness checks. Also give the SDK descriptor mirror test a runtime wire-shape walk over the built-in catalog, add the parser's timeout rejection boundary, and restore the exact built-in catalog membership assertion. * fix(channels): validate management field shapes and editor bounds (#8517) * fix(channels): align management validation layers and pin gate behavior (#8517) Read envResolvable by truthiness in the settings store so it matches the registration gate and the editor, instead of rejecting the advertised environment references of untyped plugins. Fail closed at registration on non-finite exclusiveMinimum values, empty object property lists, and async validateConfig functions, all of which would otherwise advertise a field or save path that can never succeed. Strip invalid management metadata over a prototype-preserving copy so class-instance plugins keep their createChannel implementation. Move the unchanged-value preservation exemption ahead of the object shape rejection so a stored non-record value (for example a hand-written null) no longer locks every unrelated management edit of that channel. Clamp DingTalk question-card timeouts at the maximum setTimeout delay, since Node treats larger delays as one millisecond and would expire cards instantly. Pin the previously untested load-bearing behaviors: per-key previous threading in the recursive validation, the preservation exemption's precedence over nested required enforcement, nested "type" properties, depth-2 nesting rules, and the nested-only constraints of the daemon descriptor wire contract. * fix(channels): close reserved-key preservation gaps and pin gate behavior (#8517) * test(cli): tolerate IPv6-less hosts in serve ::1 bind tests (#8517) The self-hosted CI containers can have no IPv6 loopback, where the two runQwenServe tests that bind ::1 fail with EADDRNOTAVAIL. Probe the interfaces once and skip only the IPv6-dependent binds there; every assertion still runs on IPv6-capable hosts. * fix(channels): align descriptor type contracts with runtime validation (#8517) The registry already rejects object fields without a non-empty properties array and enums without unique options, but the descriptor types still admitted both, so TS-authored plugins only learned about it when registration stripped their management surface. Make `properties` required, give enums a dedicated descriptor member with required `options`, and drop the never-honored `envResolvable` flag from number descriptors, in both channel-base and the SDK mirror, and export the descriptor sub-types through the webui barrels. Also map a throwing `validateConfig` to the usual invalid-config error and pin the store contracts that had no distinguishing tests: omitting a parent object drops the stored object without checking its nested required, writes replace nested values wholesale, unchanged stored scalars are still re-validated, and valid plugins register by original reference. * fix(channels): defuse validateConfig rejection leak and close descriptor gate gaps (#8517) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
3edecac116
|
feat(channels): support group pairing (#8440)
* feat(channels): support group pairing * fix(channels): address group pairing review * fix(web-shell): show group pairing management * fix(channels): recheck group pairing before history backfill * test(channels): verify group approval isolation * fix(channels): address group pairing review findings and pin behaviors (#8440) - Grandfather the group allowlist file in PairingStore legacy migration - Offer the pairing groupPolicy option in github/gitlab descriptors - Re-export DaemonChannelPairingSubject from the webui barrels - Refresh the GroupGate doc comment and channel docs rows - Pin the unpinned group pairing behaviors called out in review: subject dedup, trigger matrix, notification content/cap/failure/ thread routing, DM negative space under groupPolicy pairing, stored DM loop authz, pairing-enabled guard negative space, approval/revocation HTTP bodies, descriptor-driven gate branch, and the web-shell group approval mirrors - Add a compile-time assertion for the revocation request union * fix(channels): address group pairing review findings (#8440) - Accept 'pairing' in the GitLab connect warning, descriptor help text, and gitlab.md: todos dispatch after one-time group approval. - Model group approvals in the web-shell e2e mock daemon (approve by subject type, GET returns senderIds+groupIds, DELETE accepts groupId) and exercise the group pairing flow in the channels spec. - Add 'pairing' to the groupPolicy enumerations in the plugins and per-channel docs (telegram, feishu, dingtalk, qqbot, wecom). - Update the channel pairing CLI help to cover group requests. - Cap pending pairing requests at one per sender so a single member cannot occupy every shared pending slot. * fix(channels): address group pairing review findings round 7 (#8440) * fix(channels): address group pairing review findings round 8 (#8440) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(channels): address group pairing review findings round 9 (#8440) --------- Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
adec1ea50f
|
feat(core): share compression cache with Gemini and Vertex AI (#8425)
* feat(core): share compression cache with Google GenAI * fix(core): preserve restored compression accounting * fix(core): preserve estimated compression accounting * fix(core): preserve estimated token provenance on resume * fix(core): harden compression provenance flow * fix(core): keep compression counts conservative * fix: preserve compression token provenance * fix(web-shell): preserve estimated context usage * fix(core): require provider-reported anchor for compression cache sharing An estimate-derived token count misses the ~15-20K system/tools overhead the shared compression request carries, so a magnitude-only anchor gate could approve a shared request that overflows the context window. Gate cache sharing on a provider-reported count, keeping estimate-only sessions on the cold path until provider usage arrives. Pin the zero-baseline end-to-end composition (derived baseline reaches the service, missing anchor routes to the cold side query), repair the garbled R3.4 test rationale comment, and log estimate-clamp padding. |
||
|
|
bf3abdee81
|
fix(serve): Allow approved same-host text reads outside workspace (#8620)
Some checks failed
npm cache producer / Save npm cache (push) Has been cancelled
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
* fix(serve): allow same-host daemon text reads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): address review on same-host text reads Record what the read capability does not fix: #8618 still reproduces for the write and edit family, whose delegated writes are refused after the user has already approved the diff. Give the daemon's pre-approval SSE fan-out its own bullet in the user-facing security section, restore the sentence stating that environment isolation is not an OS security boundary, and make the design doc the single owner of the tradeoff list so tuning a limit cannot leave stale copies behind. Test fixtures no longer land in the developer's real home directory, the assertion pinned to localized rejection copy is dropped, and the combined capability case is split so deleting the write half cannot silently remove read coverage. * fix(test): declare REPO_ROOT and bind the external-read session to the daemon's workspace The external-read regression test referenced REPO_ROOT twice without declaring it, which made it unrunnable everywhere: - On a developer box the ReferenceError was swallowed by the bare catch in findExternalReadBase(), every candidate was discarded, and the test reported a green skip -- exactly the silently-disabled security test the CI loud-fail added last round was meant to prevent. The guard was defeated three lines above itself. - On CI that loud-fail branch threw at module scope, so the file failed to collect and took the four pre-existing tests down with it. Declare REPO_ROOT the way every other daemon integration test does. The session also asked for `workspaceCwd: REPO_ROOT` while beforeAll binds the daemon with `--workspace workspaceDir`, so the create returned 400 Workspace mismatch even once the constant existed. The read under test is external because externalReadDir sits outside the bound workspace, not because the session claims a wider one. Finally, collect each candidate's rejection reason instead of dropping it, and fold it into both branches: the CI throw names why every candidate failed and the developer-box skip warns with the same text. A bare catch cannot tell "no /var/tmp on this image" from a bug in the function, and the second reads as a green skip. Reported by @wenshao, who reproduced all three consequences against a real qwen serve daemon on Linux and supplied the repair. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6897ef7440
|
feat(core): share compression caches with OpenAI providers (#8418)
* feat(core): share compression cache with OpenAI providers * chore: refresh generated settings schema * fix(core): scope compression cache marker to OpenAI * test(core): cover OpenAI cache guards * fix(core): tighten OpenAI cache-sharing contracts * test(core): pin OpenAI prompt cache guards * fix(core): centralize prompt cache sharing gate * test(core): assert absent cache sharing marker * fix(core): partition OpenAI cache keys for subagents * fix(core): cover default OpenAI cache endpoint * fix(core): preserve prompt cache identity for forks * fix: avoid OpenAI cache fields on DashScope default |
||
|
|
2eb5cd6df5
|
feat(serve): observe daemon and child memory against real denominators (#8423)
* feat(serve): observe daemon memory pressure against a real denominator The daemon samples its own RSS and heap but has nothing to divide them by, so nothing in `/daemon/status` says whether a figure is fine or nearly fatal. #8245 landed the denominator (`limits.memory`); this turns it into a reading. `runtime.memory.pressure` reports `level`, `ratio`, `source`, and the six raw figures behind them. The level is the worse of two independent ratios, because the two failure modes are independent: a container dies by RSS against its cgroup limit, while a process on a large host can exhaust V8's heap long before RSS is a meaningful fraction of the machine. Reporting only one hides whichever failure the deployment is actually heading for. `source` names which ratio produced the level, and `unknown` says the daemon could not measure itself — which a consumer must not read as healthy. The denominator is `availableMemoryMb`, not `effectiveBudgetMb`: pressure asks how close this process is to being killed, and what kills it is the cgroup limit or host memory. An operator's budget is a policy number, so classifying against it would report `critical` for a daemon in no danger. `--memory-pressure-mode` is `off | observe`, default `observe`. Both modes report every figure; only `observe` also raises the `daemon_memory_pressure` warning, so `off` leaves the top-level `status` rollup untouched — the thresholds are inherited from an interactive-CLI monitor and are not yet calibrated for a long-running daemon, and a deployment that alerts on `status` needs the reading without the verdict. There is deliberately no `enforce`: nothing here remediates, and a value a caller can pass but never use is a dead switch. It arrives with the enforcement. Scope is the daemon root process only. `childRssCoverage` still reads `primary_only` and says so on the wire; aggregate child RSS and channel workers are separate measurements and land separately. Severity is `warning` at every level including `critical`, because `error` would make `rollupStatus` return `error` for the whole daemon — too strong a claim to stake on uncalibrated thresholds. Refs #8051. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(serve): report aggregate ACP child RSS, not just the primary's (#8462) * test(serve): close the under-determined assertions review probed The automated review mutation-probed this diff and found several assertions that were live but under-determined — each mutant it names kept the whole suite green. All confirmed locally, and all now fail: - Deleting `level !== 'normal'` from the issue gate raised daemon_memory_pressure on a healthy daemon and flipped top-level status to warning on every response — the exact false positive `--memory-pressure-mode off` exists to opt out of. Now covered on both sides: nothing raised at a realistic denominator, exactly one warning at a denominator sized to land this process in `soft`. - Summing children over `list()` instead of `listManaged()` dropped a draining-but-process-holding workspace while `activeAcpChildren` still counted it. The draining bridge now reports RSS, so the byte count can only come from that child. - The message's denominator ternary had no coverage; inverting it sent an operator hunting RSS growth during a heap-driven incident. - A truthiness guard on `ageMs` turned a measured-fresh reading (age exactly 0, when a status read lands in the sampler's millisecond) into `null`, which the field's own docs say never means fresh. - The multi-contributor age test listed ages ascending, so a plain-overwrite accumulator produced the same answer as Math.max. Reordered descending, which kills last-wins and first-wins both. Two declaration-only hunks — the issue-code union member and the `pressure` field — were guarded by tsc alone, which vitest does not run. Both are now pinned at runtime by asserting the code string and the full key set. Also fixes a real display defect: `toFixed(0)` renders a ratio of 0.795 as "hard at 80%", and 80% is critical's documented threshold. One decimal, so the number and the level cannot contradict each other. And corrects a JSDoc claim of mine that was simply wrong: `pressure` is absent not only for direct-embed but on the bootstrap /daemon/status route, which omits runtime.memory wholesale even though the budget is resolved — and that window is not just startup, since a daemon whose runtime fails to start serves the bootstrap app for its lifetime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(serve): model a per-child heap partition of the daemon budget (#8508) * feat(serve): add the child-heap admission primitives, unwired Groundwork for #8182 step 2. Nothing calls any of this yet, so no child is sized differently and no spawn is refused. `ProcessRegistry.committedProcessCount` counts attached children plus reservations that have not attached. That is the figure admission has to key on: `reserve()` inserts its token synchronously before `spawn()`, so two racing spawns each see the other, while neither appears in `activeProcessCount` until its child attaches. A child leaves the count on exit rather than when `terminate()` starts, so a channel swap counts twice while the old process winds down — deliberate, since its memory is still resident. `getAcpMemoryArgs(explicitMb?)` takes an optional share that bypasses both the module cache and the raise-only guard. Both bypasses are load-bearing. The cache, because the share depends on how many children are live now rather than on the host. The guard, because a budget-derived share is normally *below* the daemon's own heap limit, so routing it through `targetMB > currentLimitMB` would drop the flag, silently restore the overcommit, and leave every test green — the trap against a multi-GB runner, and mutation-checking it by reinstating the guard fails two tests. `createChildHeapPolicy` holds the mode, the budget, and the would-be refusal counter, and answers `decide(concurrentChildren)`. The refusal is derived from the unclamped quotient, not from `recommendedChildShareMb`, because that function clamps *up* to the 512 MB floor: past the point where the pool stops covering the count its answer saturates and can no longer distinguish "barely does not fit" from "wildly does not fit". `ChildHeapPoolExhaustedError` with both transport mappings — REST 503 with Retry-After, ACP `child_heap_pool_exhausted` — added together, since the two mappings are hand-written and drift silently otherwise. Refusing at spawn rather than at registration is the correction #8182 demands: registration allocates nothing, so this surfaces as "no new session in this workspace right now", which is true and retryable. Refs #8182. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(serve): size each ACP child by concurrently live children Wires the primitives from the previous commit into the spawn path, behind `--child-heap-mode off | observe | enforce`, default `observe`. Under `enforce` a child's `--max-old-space-size` is a share of the child pool divided by the children concurrently committed at the moment it spawns — read from the shared ProcessRegistry after `reserve()`, so two racing spawns each see the other. When the pool cannot cover another child at the 512 MB floor the spawn is refused with ChildHeapPoolExhaustedError, which is what turns a per-child ceiling into an aggregate bound: concurrent children can never exceed pool/512. Keyed on concurrency, never on registrations. A dormant workspace has no child, so it costs nothing — the specific correction #8182 records against the withdrawn proposal, which would have shrunk a lone live child to 614 MB because of 24 idle registrations. Default `observe` computes the share and the admission decision and applies neither, counting the refusals that would have happened. The divisor has never been checked against a real multi-workspace deployment, and a non-zero count is how an operator learns enforcement would have broken them without being broken. It also catches the case worth worrying about: a channel swap counts the dying child alongside its replacement, so on a saturated pool enforcement could refuse a restart and leave that workspace with no child at all. Excluding terminating children would authorise real overcommit to dodge a hypothetical refusal, so the count reports it instead. Ceilings already granted are not revisited — V8 cannot lower them — so granted ceilings transiently exceed the pool. Acceptable: the flag is a ceiling, not a reservation, and a workspace with no live sessions has no child and picks up the current share on its next spawn. `limits.memory.enforced` stops being a required literal `false`. #8245 made it one so a client could never mistake that namespace for enforcement that had not shipped; it has now, so the field is a boolean derived from the mode — and stays `false` under `observe`, which applies nothing. Refs #8182. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(serve): correct the claims child-heap enforcement makes false Two sentences in the protocol doc described the memory section as unconditionally observational: "a required `enforced: false`", and "no child spawn argument derives from these values, and no request is refused on their basis". Both are false under `--child-heap-mode enforce`, so both are rewritten rather than left to rot — `enforced` is now documented as the boolean that answers exactly this, and the refusal is documented with its wire shape on both transports. Also documents `childHeap.refusals` as the calibration signal, since a would-be-refusal count is useless if operators do not know to read it before switching to `enforce`; the flag row in the three operator docs; and the design doc's Part 1, which listed applying a share as a compatibility risk without recording how that was resolved. The end-to-end test asserts the policy reaches a real booted daemon's status with `enforced: false` under the default mode — the wire type in that test is a hand-written mirror, so its `enforced: false` literal had to widen too, which is the check that caught the type not being widened everywhere. Refs #8182. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(serve): cover both branches of the enforced tripwire `enforced` was only ever asserted false — the unit tests build no policy and the end-to-end daemon runs the default `observe` mode, so the branch that makes the field worth having was untested. Hardcoding it back to `false` passed everything. Also pins `childHeap: null` as distinct from a policy in `off` mode: the first says no policy exists (direct-embed, or the bootstrap window before the runtime is built), the second says one exists and computes nothing. Refs #8182. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): partition the child pool so granted ceilings stay inside it Review was right that the previous design did not deliver the aggregate bound it claimed. Sizing each child by the count live at *its* spawn bounds the child count but not the memory: V8 cannot lower a running child's ceiling, so grants accumulate as P + P/2 + P/3 + ... = P x H(n). Reproduced exactly — 9557 MB authorised against a 3687 MB pool at seven children on an 8 GB host, and 61355 MB against 15360 MB at the limit on 32 GB. That is 2.6x and 4x the pool, which is what the policy exists to prevent. Grant accounting alone does not fix it: the first child would take the whole pool and the second would be refused immediately. Keeping the invariant requires early children not to receive the whole pool, so the ceiling is now a fixed partition — childPoolMb / maxConcurrentChildren, constant for every child, with maxConcurrentChildren itself derived from the pool and capped at MAX_DAEMON_WORKSPACES. The sum is then n x ceiling <= pool by construction, with no ledger of outstanding grants and no dependence on arrival order. Tested as an invariant across four host sizes: fill the daemon to its admission limit and the authorised total still fits. The cost is deliberate and now documented rather than hidden: a lone workspace on a 32 GB host gets 614 MB rather than the pool, because any child may still be running when the house fills. An 8 GB host admits seven concurrent children at 526 MB each. Also from review: - The policy is no longer built for an injected `deps.bridge`. That bridge carries its own channel and never reaches the factory the policy rides on, so status could report `enforced: true` while nothing was being sized. - Both transport mappings now have direct tests. They are hand-written beside each other and drift silently; the spawn-policy tests cannot catch a wire regression. - Swept the "does not size any child" claim, which enforce makes false, out of the CLI help text, ServeOptions docs, the two operator tables, and the e2e header comment. The 17-configuration table realigns wholesale because that cell was its widest — whitespace only. Refs #8182. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(serve): model the child heap partition, defer applying it Review established that the refusal counter cannot tell an operator whether enforcement is safe, and that is the ground the enforcing mode stood on. While observing, children run on the host-derived ceiling (16384 MB on a 32 GB host), so a workload needing 2 GB of old space is healthy with zero refusals and OOMs the moment a 614 MB partition is applied. The counter measures admission pressure, not ceiling adequacy. Rather than ship a switch with no safe way to decide when to turn it on, `enforce` is removed. `--child-heap-mode` is `off | observe`, and the mode that would apply the partition arrives with the measurement that justifies it: peak old-space per child, compared against the modeled ceiling. That is a real measurement chain — the child reports rss and cpu today, and `--max-old-space-size` bounds old space specifically, so neither rss nor heapUsed answers the question. With nothing applying the partition, the machinery that existed only to apply it goes too rather than shipping unreachable: `getAcpMemoryArgs(explicitMb?)`, `ChildHeapPoolExhaustedError` and both transport mappings, and `limits.memory.enforced` reverts to the required literal `false` it was before. The spawn path is untouched again; the factory asks the policy what it would decide purely so the count is real. Also fixes the zero-pool defect review found, which the removed clamp caused: forcing at least one admissible child on a 512 MB host — where the root reserve consumes the whole 256 MB budget — produced a ceiling of 0, and `--max-old-space-size=0` is V8's *default* heap, not a zero ceiling. A pool that cannot cover one child at the floor now reports `maxConcurrentChildren: 0` and `perChildCeilingMb: null`, and the test that enshrined the old behaviour is inverted. Status now publishes `maxConcurrentChildren` and `perChildCeilingMb`, so an operator can judge the partition against their own workload — the substitute for a counter that cannot judge it for them. Every claim that a zero refusal count means the partition is safe to apply is removed from the flag help, the operator docs, the protocol doc, and the design doc. Refs #8182. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * fix(serve): repair the child-heap assertion and the reservation leak Three findings review raised against #8508 after the partition became observation-only, all still live on this branch now that it has merged. The status assertion in `run-qwen-serve.test.ts` failed on head: it used `toEqual` against `{ mode, refusals }` while the wire also carries `maxConcurrentChildren` and `perChildCeilingMb`, so the suite was red at 217 passed / 1 failed. The local type restating the wire shape was short the same two fields. Both are filled in, and the assertion stays `toEqual` so an unannounced field still fails it — the two derived figures get matchers because this suite boots a real daemon and the pool follows the machine. What they have to satisfy is now pinned separately: a fixed ceiling times the number admitted must fit inside the pool it partitions, which is the whole reason the partition bounds anything. `decide()` and `getAcpMemoryArgs()` ran between `reserve()` and the `try` that cancels the reservation. `childHeapPolicy` is a public `createSpawnChannelFactory` option, so `decide()` is caller code and may throw; the spawn then rejected with the token held for the process lifetime, inflating `committedProcessCount` for every later spawn. Both calls move inside the `try`. The regression test is mutation-verified — reverting the move gives `expected 1 to be +0`. `ServeOptions.memoryBudgetMb` still promised a `childHeapMode: 'enforce'` that sizes children and refuses spawns. No such mode exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): report no child-heap partition under `off` `snapshot()` returned `maxConcurrentChildren` and `perChildCeilingMb` unconditionally, so a daemon run with `--child-heap-mode off` still published a partition — 7 children at 526 MB on an 8 GB host — under a mode whose documentation says "do not model it". Review raised it, and it mattered more than it looked: with `enforce` gone, `off` and `observe` differed only in whether `refusals` incremented, so nothing on the wire distinguished a model that was switched off from one in force. Both figures are now `null` under `off`, which required widening `maxConcurrentChildren` to `number | null` in the daemon type and the SDK mirror. `null` rather than `0`: zero is already the computed answer for a pool too small to host one child at the 512 MB floor, and collapsing the two would tell an operator who disabled the model that their host cannot run anything. That leaves three distinguishable states — no policy at all (`childHeap: null`), a policy modeling nothing (`mode: 'off'` with null figures), and a live model — and each now has a test. The `off` unit test previously asserted only `refusals`, so its name ("models nothing at all when off") promised more than it checked. It now covers the figures, with a sibling test pinning 7 / 526 under `observe` on the same budget so nulling them unconditionally cannot satisfy both. Mutation-verified in both directions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): never model a child heap ceiling below the documented minimum `perChildCeilingMb` is `min(floor(pool / maxConcurrentChildren), legacyChildCeilingMb)`. The first term is at least `MIN_CHILD_HEAP_MB` by construction; the second is `floor(available / 2)` and is not, so the `Math.min` could publish a ceiling *below* the `minChildHeapMb` sitting beside it in the same snapshot: avail=768 --memory-budget-mb 1024 pool=512 legacyCeil=384 perChild=384 avail=1023 --memory-budget-mb 1024 pool=767 legacyCeil=511 perChild=511 Unreachable from a derived budget — the pool reaches 0 first — but an explicit budget has a floor of 1024 while available memory does not, and `docs/users/qwen-serve.md` tells operators on exactly these hosts to pass that flag. The documented remedy is what reaches the band. Refuse the model rather than shrink under the floor, with `maxConcurrentChildren` zeroed in lockstep: a ceiling no child may run at is not a partition, and "one child fits" beside a null ceiling is the same contradiction from the other side. Nothing is applied today so the impact was a wrong published figure, but this is the number the partition asks to be judged by and the one an `enforce` mode would hand to `--max-old-space-size`. The existing matrix resolves derived budgets only, which is why the mutation sweep came back clean; add the `budgetMb` axis, asserting in each case the shape that makes it reachable, and pin the inclusive boundary (1024/1024 -> one child at 512) so nulling unconditionally cannot pass instead. Also, in the same review pass: - Split usable-gauge handling into numerator and denominator. Coercing an unusable numerator to 0 published `rssBytes: 0, rssRatio: 0, level: 'normal', source: 'rss'` — a daemon that measured nothing, indistinguishable from an idle one, which is the confusion `source: 'unknown'` and `sampled: 0` exist to prevent everywhere else here. An unusable numerator now retires its own side. Zero stays a reading for a numerator and not for a denominator. - Document that `rssRatio` divides by host total under `availableMemorySource: 'host'`, so it is a lower bound on real pressure there — a denominator problem no threshold calibration addresses. - Document that `refusals` counts channel swaps at full occupancy (the terminating child is counted until it exits) and equals the total spawn count on a host too small to model a partition. Deliberately not fixed by giving the comparison swap headroom, which would admit a 26th ceiling against a 25-child pool. - Keep the sampler's rejection handler as a documented backstop — the shipped `refreshChildResource` never rejects, but it is an optional `async` interface member, so a foreign implementation throwing early would otherwise surface as an unhandled rejection — and give it the workspace so it is attributable across the fan-out. - Test hygiene: drop a duplicated `enforced` assertion; replace a host- dependent `expect.any(Number)` with a key-set pin plus a branch, since a small host now legitimately reports no partition; use `vi.spyOn(Date, 'now')` over direct assignment; reuse the exported `ChildHeapMode` on the child-heap side, leaving the independent `memoryPressureMode` switch alone. Reported by @wenshao. 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> |
||
|
|
650e085fec
|
fix(core): bound backward transcript pages in long single-turn sessions (#8553)
* fix(core): bound backward transcript pages in long single-turn sessions * fix(core): keep byte budget when backward turn alignment fails Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): keep tool call/result pairs intact on backward pages * refactor(core): share replay turn-start predicate with ACP history selection * fix(core): bound backward pair extension for long tool_result runs * fix(core): bound backward pair extension by the page byte budget * fix(core): cap backward page expansion at a shared byte ceiling Turn-alignment expansion was record-bounded only, so a byte-heavy turn whose start sat within the expansion window could absorb records past the workspace route's response cap and dead-end backward pagination at that anchor on every retry. Gate both turn-alignment and pair extension at a shared hard page ceiling (SESSION_TRANSCRIPT_MAX_EXPANDED_PAGE_BYTES); the route derives its serialized-response cap as twice that value so the caps cannot drift. Also accept the expansion floor only when it is a real turn boundary, so pages inside a long turn stay `limit` records instead of `2 * limit`, and exclude interleaved realtime conversation records from the pair-boundary predicate so the pair walk passes through them to the owning call. Extension skips are logged with a reason, and backward chaining under maxBytes is covered by tests. * fix(core): bound backward pagination expansion and mid-turn turn starts * fix(core): pair tool calls with results across backward pagination and bulk replay Exempt the force-joined owner from the pair-extension byte budget so an oversized tool-call record no longer splits its pair by construction, and mirror the same pair-extension guard onto the ACP bulk-replay selector. Deduplicate the bounded boundary walk into a shared findBoundaryAtOrBefore, harden the ceiling-clamp byte-budget tests, pin mid-turn notification classification, and refresh the stale bulk-load contract sentence. --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen Autofix <autofix@qwen-code.dev> |
||
|
|
7edc16ba11
|
feat(review): say so when the bundle is older than the review it runs (#8390)
* feat(review): say so when the bundle is older than the review it runs Every `qwen review …` step runs the BUILT bundle, not the working tree. So editing a review command, or switching to a branch that contains one, changes nothing about the run until someone rebuilds -- and the failure is silent and total: the run behaves like the last build, and every conclusion drawn from it is a conclusion about that build. Measured on 2026-08-02, dogfooding /review against #8368 from a checkout whose bundle was fourteen hours old. Three things were invalidated at once and none announced itself: `drive` and `mock-provider` had merged that morning and were absent from the binary, so "the agent never reached for them" measured nothing; and #8345's guard against scoring a mutant `survived` when its own collocated test was red had merged too, so the run reproduced the bug it fixed and filed three findings the current code holds as `inconclusive`. The round was discarded and re-run after a rebuild. `parse-args` is the first command of every review, which makes it the only place a notice reaches a reader before they act on a result. It names the file that is ahead, by how much, what actually runs from the bundle, and the command to rebuild -- "rebuild" without evidence is advice nobody can check. mtime, not git: the question is whether this bundle was built from this source, and a git comparison answers a different one. A margin absorbs a checkout, which writes everything at once in no guaranteed order. An installed package has no sources beside it, finds nothing to compare, and stays silent -- a check that cannot see the files must not accuse the build. Also documents `findings --test-delta` for users: it can lower a severity, and therefore change what the verdict is computed from, so it belongs beside `--outcomes` rather than only in the skill. * fix(review): watch the file every subcommand is registered in `packages/cli/src/commands/review.ts` is where all 30-odd subcommands are imported and registered, and it sits beside the directory rather than in it -- so a new command, or a changed dispatch, was exactly the change this check could not see. A root may now be a single file, which is what that one is. Confirmed end to end: with `review.ts` three hours ahead of a fresh bundle, the warning names it. Also two comments that did not match the code: symlinks of every kind are skipped, not only directories (`isFile()` is false for a symlinked file too), and the module now says what `QWEN_CODE_CLI` already covers -- talking to a different program -- so it is clear this guards the other half, the right program built before the change. * fix(review): compare content, because a timestamp check cried wolf The first version compared the bundle's mtime against the newest review source, and it was wrong in the direction that matters most. `git checkout` rewrites every file that differs between two commits, so returning to the branch a bundle was built from re-stamps exactly those files and the check calls a byte-for-byte correct bundle stale. Measured: with the sources untouched and the bundle two minutes older, it warned. A line that fires when nothing is wrong teaches its reader to skip the line, which would have made this worse than absent. The build now stamps a digest of the review sources it bundled into `dist/review-sources.sha256`, and the check re-derives that digest from the tree and compares. No margin to tune, no clock to trust, and no answer but the true one. Verified end to end across all five cases: a clean tree is silent, a source touched but unchanged is silent, and a real change under any of the three roots -- the command directory, the `review.ts` that registers them, the bundled skill -- warns. The digest is now one rule stated twice, since the build script cannot import the package it runs before building. `scripts/tests/review-source-digest.test.ts` holds the two equal, on this repo and on a synthetic tree that exercises the file-shaped root; a package test may not reach into `scripts/`, so it lives on the side of the boundary that may. Paths are folded relative to the repo root with separators normalised, and the file list is sorted -- `readdir` order is a property of the filesystem, so without it a bundle built in CI and a tree cloned locally would hash the same source differently and every run would warn. * fix(review): a diagnostic must not kill the run, and tests are not the bundle Two Criticals and five suggestions from review, all verified before changing anything. `writeStderrLine` throws on EPIPE, so stderr piped to `head` would have killed the review before it parsed a single argument -- a warning that destroys the run it was warning about, and the opposite of this change's own invariant. `writeStderrLineSafe` is the convention for diagnostics in this subsystem and is what it calls now. `reviewSourceRoots` builds paths with the platform `join`, and the test asserted forward-slash literals, so all three elements would have failed on the merge queue's Windows leg -- which the pull_request event never runs, so the green CI here proved nothing about it. Test files left the digest. esbuild follows imports from the CLI entry and no test is reachable that way, so folding them in fired the warning for an edit that cannot change a byte of the bundle -- the false positive this module already rejected once. 112 files became 61, and a test-only edit is now silent while a production one still warns. The handler wiring is tested at last, against a real temp tree rather than a mock of the reads under test: the derivation from `process.argv[1]`, the stamp read, and the warning. All three mutations the review named -- dropping the call, reading the stamp from the wrong directory, collapsing repoRoot to distDir -- now redden it. Also: the stamp's filename is pinned across the boundary it crosses (the build wrote a literal while the check read `DIGEST_FILE`, so a one-sided rename would have silenced the feature with every test green); the digest is computed only when there is a stamp to compare it against, instead of hashing a hundred files for a value the first guard discards; the `rebuildCommand` parameter no caller ever set is gone; and the build script's comment no longer claims a code-sharing relationship that does not exist. * fix(review): fixtures are not in the bundle either The same false positive, a third time and one directory over. Excluding tests from the digest was right and incomplete: `review/__fixtures__` holds four files — three responder modules and a captured comment — that a test loads at runtime, from no import the bundler follows. Measured against `dist`: none of the four appears in it, so editing one changed the digest while the bundle stayed byte-identical and the warning claimed a review command had changed. Both walks skip the directory now, and the parity test's synthetic tree grows a fixture and a `.spec.tsx` so the two implementations are held equal on the whole exclusion, not just the part the first case exercised. Reverting one side reddens the local case AND both parity cases, which is what that guard is for. Verified the other direction too, since an exclusion can overshoot: every review source that reaches `dist` is still covered. `DESIGN.md` and `SKILL.md` both ship and both remain in the digest — checked, not assumed, after two rounds of this exact mistake. Six cases end to end after a rebuild: a clean tree, a test edit and a fixture edit are silent; a production edit, a `review.ts` edit and a `DESIGN.md` edit each warn. * fix(review): allowlist the stamp, and stop guessing what the bundle holds The Critical first: `create-standalone-package.js` fails on any top-level dist entry outside its allowlist, and `review-sources.sha256` was on neither list. The next release would have aborted the standalone archive on all five targets, and no PR-time job runs the packager, which is why this suite is green. Allowlisted -- shipping it is harmless, since a standalone install has no `packages/` to compare against and the check stays silent there. `lib/test-utils.ts` was in the digest: test support with a production-looking name, imported by two test files and nothing else. That is the fourth patch to one rule -- `.test.ts`, then `__fixtures__/`, then this, plus `.DS_Store` -- and each was found by a reviewer after it shipped. So the rule stops being a list somebody remembers to extend: a new test asserts the property the list approximates, that every file the digest folds in is reachable from production code and nothing reachable is left out. Dropping `test-utils.ts` from the exclusion reddens it, which is the fifth instance failing in CI instead of in a review. Three branches that no test reached, each with a mutant the review measured surviving the whole suite: the walk's symlink skip (a directory cycle would send the first command of every review into unbounded recursion), the read-failure path (hashing the survivors of a concurrent checkout would accuse a tree that is merely mid-change), and the build's stamp call site (removing it left the scripts suite green while `npm run bundle` silently stopped writing the stamp). All three now redden. And `unmeasured` had no reader, so the one edge this check cannot measure but can see -- sources present, stamp absent -- passed in silence. That is the state of every existing checkout the moment this ships, and it is exactly the silent failure the change was written to end. It now says so, while an installed package, which has no sources either, still says nothing. * fix(review): the guard was shallower than the property it claimed The guard added last round asserts that every file in the digest is reachable from production code. It did not: a file imported by nothing passed, because the filter also required some test to import it; only `.ts` was inspected, so a test-only `.tsx` or `.mts` helper walked through; and it read static imports only, while this directory has nine `await import('./…')` edges. It asserts the property now — every extension, orphans included, dynamic edges seen — and the tree has no violators, so the strictness cost nothing today and is there for the next file. `__snapshots__` joins the exclusions. `vitest --update` regenerating a snapshot would have moved the digest with the bundle byte-identical; none exists under the review roots today only by chance, and 120 `toMatchSnapshot()` calls live elsewhere in this package. Three couplings that no test held: - the allowlist entry that fixed the release-breaking R2-1 -- reverting those five lines left the whole scripts suite green, and the next failure would have been a release aborting on all five targets. `isAllowedDistEntry` is exported and the stamp's own name is asserted against it, so a one-sided rename fails here instead; - the `.DS_Store` member of `NOT_BUNDLED_FILE`, absent from the repo and so from the parity tree -- one-sided removal stayed green while a macOS checkout would digest differently on the two sides forever; - each `unmeasured` reason. Swapping the two arguments at the single call site kept all 76 tests green while telling a pre-stamp checkout its sources were missing. And two comments that said the opposite of the code beneath them: the digest is computed unconditionally on purpose (the pre-stamp notice needs it), and `NOT_BUNDLED_FILE` helpers are deliberately not importers, since nothing reaches the bundle through a file the bundle does not contain. The two stderr diagnostics are documented for users, beside the sibling paragraph this PR already added. * fix(review): measure only the layout that can carry a stamp `npm start` launches `node <root>/packages/cli`, and node sets `argv[1]` to that directory -- so the derivation found sources under `<root>` with no stamp beside them and printed "could not check" on every review, forever, with advice that could never make it stop. That is the fires-when-nothing-is-wrong failure this change argues against, on the path `start.js` sets `QWEN_CODE_CLI` to precisely so reviews reach that build. Only a `<root>/dist/cli.js` layout is measured now; anything else has no stamp to find and no way to grow one. The build-side digest could kill `npm run bundle` where the check side degrades gracefully: a file vanishing mid-walk threw out of the hash loop, and the stamp is the copier's last step, so the build would fail with every asset already in place. Caught and skipped -- a missing stamp is `unmeasured`, which the runtime already treats as an acceptable answer. The skill now says what to do with the warning, which is the half that makes it reach a human: `parse-args` runs inside an agent's shell tool, the user reads the agent's summary rather than raw stderr, and a line nobody repeats is a line nobody sees -- which is how the 2026-08-02 round went wrong in the first place. It also records that the instruction cannot help the run that needs it, since the skill comes from the same bundle. And the scope is stated where silence could be over-read: the digest covers the review commands, the file that registers them, and the bundled skill -- not the shared helpers those import. A quiet run means the review code matches the bundle, not that the tree does. * fix(review): refuse to certify a bundle the copier may not describe The stamp described the tree as the COPIER saw it, and the copier runs after esbuild -- so a source edited in between, or `copy_bundle_assets.js` run on its own (it self-executes), wrote a digest certifying a `cli.js` built from something else. Silence then means "verified fresh" when it is not, and that is the only direction here where a quiet run is affirmatively wrong rather than merely uninformative: every other gap degrades to `unmeasured`. Timestamps are the wrong tool for judging staleness and the right one for judging whether this stamp can be honest at all, so the build refuses when any source is newer than the bundle it would attest to, and says why. Driven for real: touching a review source and running the copier alone now prints "skipped the source digest rather than certify a bundle it may not describe". `it('counts the same files')` compared nothing -- it asserted `> 50` on the build side while the check side exposes no count, so the title claimed a parity the body never checked, and the margin over the real 56 made it a future false alarm in `scripts/` for an unrelated change. Removed; the digest parity already holds the file set. "Root is a file" was inferred from `readdirSync` raising ENOTDIR, an assumption about every platform's libuv on the one root that is a file -- `review.ts`, where "a new subcommand was registered" lives. `statSync(root).isFile()` says it instead. And the check itself moves out of the handler into `bundleStalenessNotices`, which is where the rest of it already lived. `parse-args` is about parsing arguments again, the wording is testable without the yargs harness, and a second caller -- an agent resuming a review never runs step 1 -- is one line. * fix(review): align the twin walk, and stop a test from passing on nothing The build side still inferred "this root is a file" from `readdirSync` raising ENOTDIR, one commit after the check side stopped doing exactly that and said why. A platform that maps the case differently would drop `commands/review.ts` from one digest and not the other, and a byte-for-byte correct bundle would warn on every review forever, on that platform alone, with rebuilding reproducing the same one-sided walk. Both sides ask `statSync(...).isFile()` now. Fixing one half of a pair and not the other is the mistake this file keeps making. The filename parity test had been passing on nothing since the previous commit: it matched `writeFileSync(join(distDir, '…'))` against the script's source, the literal moved into a `stampPath` variable, and the regex returned `undefined` so the assertion compared against nothing. It runs the build against a fixture now and reads the name off `dist/`, so it measures what the build does instead of what its source looks like. Renaming the stamp on one side reddens it. Also from review: the duplicated comment block in `parse-args`; an unreadable source now says the check could not run rather than passing in the same silence as an installed package, which is what the docstring already promised; the "could not check" line no longer asserts that the checkout predates the feature, since the build has three refusal paths and one of them means the opposite; every refusal removes an existing stamp, because leaving an older attestation beside a newer bundle is a weaker form of the certifying it refuses; and `drive` calls the check, which the module comment argued for and the diff had not done -- a resumed review never runs step 1, and that is where the long work starts. * fix(review): pin the regex group the parity tree missed, and say source, not command * fix(review): allowlist what the bundle holds, and cover the drive notice (#8390) * fix(review): treat unreadable review sources as unmeasured (#8390) * test(review): pin the stamp guard mutations that survived the suite (#8390) * fix(review): close staleness-check gaps and pin the round-4 survivors (#8390) * fix(review): close round-5 staleness gaps for parity, refusals, and partial checkouts (#8390) * fix(review): close round-6 gaps in the clause classifier, symlink layout, and pin honesty (#8390) * fix(review): close round-7 gaps in the closure oracle, parity pin, and refusal pins (#8390) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): close round-8 gaps from the maintainer review (#8390) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): close round-9 nits from the maintainer review (#8390) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): pin the lease root in the synthetic digest parity case (#8390) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen Autofix <autofix@qwen-code.dev> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
6405714c9d
|
docs: document serve sub-session concurrency settings (#8404)
* docs: document serve concurrency settings * docs: clarify serve concurrency settings |
||
|
|
03eb5043cc
|
fix(dingtalk): keep status cards continuous and attributable (#8565)
* fix(dingtalk): keep status cards continuous during runs * fix(dingtalk): render attributable markdown replies * fix(dingtalk): harden status card attribution and fallback paths (#8565) * fix(dingtalk): deliver boundary content reliably and harden card fallbacks (#8565) * fix(dingtalk): halt refreshes on dead cards, keep delivered content (#8565) The per-second status chain kept pushing metadata updates after the content stream latched failed, and once the 3-failure breaker tripped an idle card could never revive. Stop the chain on a latched stream failure and keep a low-frequency probe so a recovered metadata API revives it. Also re-send boundary content declared delivered via the card when a failed/cancelled terminal overwrites the continuity card, give a re-latched status context a fresh segment id after input_requested so a later failure still reaches the card failure UX, dedup inline sender echoes, and require the 'I' of IMAGE in the partial-marker regex so a bare trailing '[' survives in final fallback text. Consolidate the duplicated content-cap constants and pin the reviewed delivery, drain, breaker, and attribution behaviors with tests. --------- 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> |
||
|
|
037b4d9c03
|
feat(daemon): Add SSE stream and client observability (#8572)
* feat(daemon): add SSE stream observability Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): Address SSE observability review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
e76dff1c6b
|
feat(review): add declarative repository-context manifest (#8401)
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
* feat(review): add OpenJDK repository context Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(review): extract repository context foundation Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): repair CI type guard and add manifest repository context Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): harden repository context per maintainer review Address both maintainer reviews on the repository-context PR: - repo-context: a PR plan whose merge base never resolved (mergeBaseSha: null) now degrades to a null artifact without consulting the worktree, instead of throwing a misleading "invalid plan" error or falling back to the PR head. - Identity reads return the same shape in PR and local modes (CRLF->LF, trimmed) and fail closed: absence yields null, a present-but-unreadable file throws. - Context-required roles can no longer override the roster's effort, topology, and mode gates. - The relatedPaths scan bound rises from 1024 visited entries to 16384 and is documented, so honestly scoped manifests no longer abort reviews. - A present-but-invalid repositoryContext now fails closed in every consumer; the gate no longer silently drops the disclosure. - The duplicated validators and bounds are shared between the wire format and the manifest provider; the context role allow-list is derived from a single const; manifest arrays no longer require hand-sorting (uniqueness only). - Nits: dead mkdir removed, output message names the provider, escape-message fix, unsafe changed paths skip instead of aborting, segment-glob regexes memoised, list helper hoisted. - Docs: user-facing manifest section, trust-boundary residuals and foundation status in the design doc, fail-closed exit guidance in the skill. * fix(review): skip unsafe related paths in manifest context (#8401) * fix(ci): align review timeout helper test with externalized variables (#8401) * fix(review): harden repository context bounds and base identity reads (#8401) * fix(review): bound manifest matching work and pin round-2 review gaps (#8401) * fix(test): isolate serve streaming suite from stray workspace settings (#8401) * fix(review): cap identity reads, bill match work by length, pin round-3 gaps (#8401) --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
95655daf30
|
feat(cli): render inline terminal images (#8305)
* feat(cli): render inline terminal images * fix(cli): preserve text around hidden citations * fix(cli): clear pending image state on reset * fix(cli): bound inline image rendering * fix(cli): clear compacted image overflow markers * docs(cli): clarify inline image resume scope * fix(cli): address inline image review feedback --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
79027bb2db
|
fix(web-shell): scope artifact actions to owning workspace (#8510)
* fix(web-shell): scope artifact actions to owning workspace * fix(web-shell): preserve artifact authority in strict mode * fix(web-shell): address artifact workspace review * test(web-shell): cover missing file owner * fix(web-shell): keep open artifact tabs alive across pane and list gaps |
||
|
|
02f1692d40
|
fix(web-shell): allow session refresh with daemon auth (#8445)
* fix(web-shell): allow session refresh with daemon auth * test(web-shell): cover SPA fallback shell branch for non-session navigations * test(web-shell): retarget sec-fetch SPA fallback test to non-session navigation Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(web-shell): serve pre-auth navigations during the deferred runtime window The deferred-runtime gate applied bearerAuth to every non-bootstrap request while the runtime was cold, so a browser refresh of /session/<id> (and / and /assets/*) 401'd on the default `qwen serve --token ...` start until something else warmed the runtime. Exempt the same surface mountWebShellAssets registers before auth, via a shared isPreAuthWebShellRequest predicate, so cold document navigations start the runtime and load the shell while JSON fetches, API subpaths, and --no-web daemons stay gated. The predicate is dynamically imported to respect the serve fast-path import-boundary guards. * fix(cli): align deferred pre-auth web shell gate with warm routing (#8445) * fix(cli): report daemon startup failure to pre-auth web shell navigations (#8445) A pre-auth-exempted Web Shell navigation that hit a failed deferred runtime startup fell through to the bootstrap app's bearer gate and received a misleading 401 instead of the 503 daemon_runtime_failed envelope authenticated requests get for the same failure. Track the exemption in the deferred dispatch and answer the diagnostic envelope directly. Also cover the deferred gate's HEAD exemption, which was previously untested. * fix(cli): exempt bare /assets from the deferred pre-auth gate (#8445) * refactor(cli): dedupe runtime failure envelopes and deferred-window test setup (#8445) * refactor(cli): rename startup envelope helper and pin query-string deep links (#8445) * fix(cli): serve the // root alias pre-auth and fail-close the deferred predicate (#8445) * docs(cli): record the pre-auth %2F session deep-link invariant (#8445) * fix(test): isolate serve streaming suite from stray workspace settings (#8445) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: 克竟 <dingbingzhi.dbz@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
732f4d8a29
|
feat(voice): support trusted private ASR base URLs (#8350)
* feat(voice): support trusted private ASR base URLs * fix(voice): address private endpoint review findings * test(voice): cover private endpoint edge cases * test(voice): pin remaining endpoint edge cases * fix(voice): address private endpoint review feedback * fix(voice): clarify allowlist URL and normalize IPv6 * fix(voice): harden NAT64 address validation * fix(voice): address managed endpoint review findings * refactor(voice): extract shared IPv6 transition unwrap ladder (#8350) Deduplicate the IPv6-transition unwrapping sequence (mapped, compatible, NAT64, dotted-quad) that was repeated verbatim between isPrivateNetworkIp and isAlwaysBlockedVoiceAddress on both CLI and Desktop surfaces. A single unwrapIpv6TransitionStep helper now yields the next canonical address (or 'blocked' for unrecognized ::ffff: forms), and each predicate recurses through it, preserving the exact re-check semantics at every unwrap level. * test(voice): cover allowInsecureBaseUrl wiring through desktop default transports (#8350) * fix(voice): add allowlist hint to private-network rejection error (#8350) * fix(voice): reject always-blocked base URLs before offering the allowlist hint (#8350) * fix(voice): resolve exact desktop voice provider before OAuth (#8350) * fix(voice): address review feedback for trusted private base URLs (#8350) * fix(voice): align desktop voice resolution with CLI semantics (#8350) * fix(voice): scope desktop fail-closed resolution to policy-bearing entries (#8350) * fix(voice): address round-8 review findings for trusted private base URLs (#8350) Run the invasive process-global `mock.module('ws')` suite as voice-ws-handler.isolated.ts so the desktop package's single-process `bun test` run no longer leaks the fake socket into unrelated ws consumers; the existing isolated loop runs it in its own process. Shape-guard the desktop provider scan: non-object modelProviders elements are skipped (falling through to OAuth instead of throwing a raw TypeError), and non-string baseUrl/envKey/settings.env values on a voice-model entry now surface the PROVIDER_ENTRY_REMEDY remediation error instead of crashing. Compute the DashScope-compatible /v1 rewrite before any allowlist match in fromExactModelProvider so the stage-1 check, the remediation messages, and the top-level recheck all compare the same final URL and a single allowlist entry converges for split-horizon deployments. Extend the CLI allowlist remediation messages to state which settings scopes honor the entry, since serve mode never shows the interactive workspace-strip warning. Thread providerProtocol through the CLI voice model seams (createVoiceModelSource and the daemon buildModelsConfig) so protocol-mapped custom provider groups resolve like the rest of the CLI model surface, and document the remaining protocol-agnostic desktop scan in the design doc. Correct the getHomeEnvFallback comment: it adopts the narrower getHomeEnvFallbackVars candidate set on purpose. Add multi-record DNS answer tests on both CLI and desktop net guards so the records.some classification is pinned against the array shape defaultLookupHost always produces in production. * fix(voice): address round-9 review findings for trusted private base URLs (#8350) * fix(voice): address round-10 review findings for trusted private base URLs (#8350) * fix(voice): classify desktop voice duplicates before ambiguity check (#8350) * fix(scripts): compare voice guard mirrors as parse trees (#8350) --------- Co-authored-by: rockybot2026 <265985139+rockybot2026@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
f4cf4268f5
|
fix(core): cap a streaming response's total lifetime, slim the review fan-out launch (#8602)
* fix(core): cap a streaming response's total lifetime The stream inactivity watchdog resets on every chunk, so a drip-fed stream — a gateway trickling keep-alive chunks, or a model crawling through an oversized single message — kept it alive forever while the message never completed: in issue #8597's CI review runs that meant 2.5-4.5 hours of silence ending in the outer timeout's kill. Add a per-request lifetime cap (default 15 min) that does not reset on chunk arrival. Tripping it throws a retryable ETIMEDOUT, so the existing transport-continuation recovery resumes a healthy generation the cap happened to cut. Config field streamMaxLifetimeMs; env knob QWEN_STREAM_MAX_LIFETIME_MS; 0 disables. * fix(review): carry findings lists as digest-named files, not inline agent-prompt folded the cumulative findings list into every printed verify/reverse-audit block. On a 12-14-auditor round that made the launch one 65-82 KB assistant message — the oversized single message whose stream generation never completed in #8597 — and cost a 5-10 minute paged relay through the orchestrator's context before every round. The list now goes where the brief already goes: on disk, named by the same findings digest that keys the record; the block carries a read_file pointer. The delivery guarantee is unchanged — a launch that drops the read matches no record — and the retirement scheduler's echo guard reads the list back from the file the prompt names, failing toward auditing when it is gone. * fix: address review feedback on stream guards and findings files * fix: address review feedback on findings delivery and stream guards The delivery floor counted only the brief's read receipt, so an instruction-skipping verifier could open its brief, skip the one instructed read of the findings file its block points at, and clear the gate having never seen the list it ruled on. The floor now extracts the findings pointer from the recorded prompt and requires a successful read of it — a new findings-unread delivery shape, with gap texts for the verify, reverse-audit, and combined steps. A failed findings-file write also speaks on stderr instead of silently pointing a whole round at a missing file. Stream guards: consult the lifetime deadline at the top of the loop, not only when the timer wins the race — a pre-buffered chunk resolves next() as a microtask and beats setTimeout(…, 0) every time. Merge the two ETIMEDOUT bypass blocks into one rule. New tests pin the idle-off + lifetime-on branch, the both-guards-0 disable (config and env), and the buffered-source shape. Also reword the prose sites that still described the inlined list. * fix(review): name the findings file in the verify/reverse-audit briefs The two briefs still told the agent the findings were "listed in the message that launched you", but since the list moved behind a read_file pointer they find only a pointer there. The agent reads its brief first (the block mandates it), so the stale text sent it looking for finding blocks in the launch message — the exact findings-unread shape the new delivery floor gates on. Point both briefs at the .findings.md file and the required read instead. * fix(review): address round-3 review — empty-round brief, guard hardening The b796708 brief fix stated unconditionally that the launch message points at a findings file, but an early reverse-audit round on a clean review names no file (the empty branch prints '## Nothing is confirmed yet' with no pointer) — the auditors were being sent to read a path their prompt does not contain. The reverse-audit brief now says the file is named when there is a list, and that an early round has none. Also from the round-3 review: - writeFindingsFile's failure diagnostic now uses writeStderrLineSafe: the catch exists to keep the build alive, so it must not throw out of it on EPIPE (qwen … | head). - The retirement echo-guard resolves the findings pointer from the CLI's own record (never the orchestrator's pasted copy), confined to the plan's record dir, memoized per round — an out-of-bounds or unreadable file degrades to the prompt, failing toward auditing. - Document the delivery floor's deliberate weakening: it proves the findings file was opened, not paged to completion (coverage.ts), and drop the last stale 'record folds the findings in' comment. - withStreamInactivityTimeout returns the source untouched when both guards are disabled, so the invariant survives a caller refactor (setTimeout(Infinity) would otherwise clamp to ~1ms). - settings.md + the config comment now say the stream guards are OpenAI-compatible-only, and that the 15-minute cap bounds a stream whose idle timeout was raised above it (raise or disable the cap to keep a longer window). * fix(review): address round-4 review — memo hygiene, read-visibility, naming The retirement echo-guard memoized a failed read's per-record fallback under the round's shared findings pointer, so one chunk's launch prompt would serve as a sibling chunk's findings list. Memoize only successful reads; a miss falls back to THIS record's own prompt, uncached. The findings block now prints the list's line count, so an agent whose read_file truncates can see it saw a fraction rather than the whole confirmed list — a visibility aid the delivery floor (which proves the file was opened, not paged) does not provide. Also from the round-4 review: rename withStreamInactivityTimeout to withStreamGuards (it now enforces two guards), correct the constants comment (the cap is measured from the stream's first iteration, not its first byte), and give the two stream-guard knobs a dedicated settings.md entry instead of burying them in the timeout paragraph. * fix(review): address round-5 review — deterministic record walk, accurate line count The cross-contamination regression test only discriminated under one readdir order: readRecordedPrompts walked the record dir in filesystem (filename-hash) order, so which chunk's fallback poisoned the shared memo depended on the walk. Sort the directory listing — a deterministic walk removes that whole class of order-sensitivity from a module that reasons per-record, not just from this test (a temporary revert of the memo fix confirms the test now fails against the buggy shape). findingsSection printed the line count of the TRIMMED findings body while writeFindingsFile writes the untrimmed content, so a list with leading/trailing blank lines got a label smaller than the file the agent actually reads — precisely the under-reading the count exists to make visible. Count the untrimmed content. * fix(core,review): charge the lifetime cap on upstream-wait, not delivery time Round-2's top-of-loop deadline check made the lifetime cap measure end-to-end delivery time: a healthy upstream that finished and buffered its chunks was cut for the CONSUMER's slowness (a paused IDE client, a big render), and a stream whose terminal done resolved at the boundary was converted into a retry. The cap is now charged on accumulated upstream-wait — the time the loop is blocked in await it.next() — so a buffered, already-complete stream always completes and only real upstream latency counts; the drip-fed never-completing stream spends exactly that time waiting, so #8597's shape is still caught. Also from the round-6 review: - Hoist the stream-guard error branch above the thinking-tag check: a drip-fed gateway cutting mid-<think> surfaced the guard's ETIMEDOUT as a PROTOCOL_TAG_LEAK and burned the tag-leak retry budget. - The findings line count drops the trailing newline's empty segment, so a 12-line list is not advertised as 13. - settings.md: the stream guards are env/config-only (no settings.json key); document that streamIdleTimeoutMs: 0 embedders now also need streamMaxLifetimeMs: 0 to fully opt out. - constants: a functionCall already streamed (the tool-heavy common case) recovers as a visible classified error, not a continuation. - coverage.ts: drop the stale 'four shapes' counts after Delivery grew a fifth; pipeline: dedupe the instanceof in the guard debug log; and correct the both-guards-off test's comment to pin the outcome, not the caller mechanism. * fix(core,review): monotonic guard clock, findings-write fallback, read-only floor Round-6 follow-ups the previous commit left open: - The stream guards accounted on Date.now(): an NTP step forward (or a long sleep) killed a healthy generation on the next iteration, and a backward step silently disabled the lifetime cap — the hang #8597 exists to bound. All guard accounting is now performance.now(); the setTimeout it races is monotonic too, so the two agree. Vitest fakes performance alongside the timers, and a new test A/B-verified against the wall-clock shape (it fails on Date.now() accounting). - A failed findings-file write returned the path anyway, pointing a whole 12-14-agent round at a file that does not exist — every agent burned its round, then the delivery floor failed it. writeFindingsFile now returns null on failure and findingsSection falls back to inlining the list (the pre-#8597 shape): the recorded prompt carries the list, the delivery check compares it verbatim, and the build stays alive. - The findings delivery floor matched the path in ANY successful tool call's serialized args, so a search_file_content or list_directory that merely named the file cleared it without reading a line. The transcript parser now records read_file calls apart, and the floor counts only those — a mention is not an open. * fix(core,review): round-7 leftovers — upstream-wait wording, key helper, test mock The round-6 error message and class doc still said 'total lifetime cap' and promised the continuation recovery unconditionally, and the wrap-site comment still read 'aborts at maxLifetimeMs from stream start' — all three now describe the upstream-wait semantics the guard actually implements, and the message names the wall clock separately so the two numbers reconcile. The round suffix baked into findings-role record keys and the findings file name was derived independently in three sites (findingsFileFor, runAllChunks, the single build) — a change to how a round is spelled would update two of three and silently fork the artifacts; roundPartOf spells it once. The writeFindingsFile test's module mock replaced all of stdioHelpers (three stubs, missing the other exports) and asserted mock.calls[0][0] with no mock reset — it now spreads importOriginal, clears mocks in beforeEach, and matches the stderr lines with stringContaining. * fix(review,core): round-3 follow-ups — conditional findings briefs, JSDoc placement, log labels The verify/reverse-audit briefs told the agent its findings live in the .findings.md file unconditionally, on the exact path (writeFindingsFile returning null -> findingsSection inlining the list) this PR added to work WITHOUT one; both now say 'when the message points at a findings file, read it; on the rare write-failure fallback the list is inlined in the message, read it there.' roundPartOf had been inserted between findingsFileFor's JSDoc and the function, orphaning the comment that records the one-file-per-round and null-means-inline contracts; the helper now sits above it. The guard debug log labelled the wall clock 'streamLifetimeMs' beside the cap; it is now 'wallClockMs' so the two numbers reconcile the same way the error message does. * fix(review): anchor the findings-pointer extraction to its emitted shape findingsPointerOf matched the FIRST read_file(file_path="….findings.md") anywhere in the recorded prompt. On the write-failure inline fallback the findings list occupies exactly the position the pointer would sit in, and a finding entry there can itself quote a read_file pointer of its own (a finding about this pipeline, which the harness produces when it reviews this repo). The loose match then extracted the quotation as the pointer and the readers diverged: coverage demanded a read of a path no agent was told to read (a spurious findings-unread on a run that is already degraded), and retirement, worse, confined-and-read an earlier round's file and flipped a just-filed finding to an echo, retiring a chunk that had just reported — the one direction the module's header commits to never failing. A quoted pointer inside a findings entry is indented or embedded in prose, so anchoring to a standalone read_file line removes it; the happy-path pointer is alone on its own line inside its fence. A test drives an inlined list containing a pointer-shaped line (fails under the old loose regex). --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
44747cc27b
|
docs: add legacy code audit (/audit) design doc (#8397)
* docs: add legacy code audit (/audit) design doc Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: revise legacy audit design with round-2 replication evidence Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: note cross-file tracer cost and budget rule in legacy audit design Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: address review feedback on legacy audit design (#8397) * docs: wire invariant triple, personas, and event detection into audit design (#8397) * docs: address round-3 review feedback on legacy audit design (#8397) * docs: address round-4 review feedback on legacy audit design (#8397) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: address round-5 review feedback on legacy audit design (#8397) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: address round-6 review feedback on legacy audit design (#8397) * docs: address round-7 review feedback on legacy audit design (#8397) * docs: address round-8 review feedback on legacy audit design (#8397) * docs: address round-9 review feedback on legacy audit design (#8397) * docs: address round-10 review feedback on legacy audit design (#8397) * docs: address round-11 review feedback on legacy audit design (#8397) * docs: address round-12 review feedback on legacy audit design (#8397) * docs: address round-13 review feedback on legacy audit design (#8397) * docs: address round-14 review feedback on legacy audit design (#8397) * docs: address round-15 review feedback on legacy audit design (#8397) * docs: address round-16 review feedback on legacy audit design (#8397) * docs: address round-17 review feedback on legacy audit design (#8397) --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
2ad72fd5e9
|
fix(cli): Bound ACP textual tool-result payloads (#8450)
* fix(cli): Bound ACP textual tool-result payloads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Harden ACP text projection budgets Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * perf(cli): Cap multi-block ACP projection scans Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Cover ACP projection boundary guards Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
477859bb3f
|
feat(channels): support local gh authentication (#8461)
* feat(channels): support local gh authentication Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(channels): align registry catalog test and visuals with optional GitHub token (#8461) * fix(channels): address review feedback for GitHub local gh auth (#8461) Treat a blank replacement of an optional secret as a clear so an existing GitHub channel can no longer ship an empty or whitespace-only PAT to the daemon. Reuse the shared missing-field predicate in the editor's GitHub credential validation, wrap malformed baseUrl failures in an actionable channel error, and surface sanitized gh stderr in local authentication failures. * fix(channels): address second-round review feedback for GitHub local gh auth (#8461) Pin the whitespace-only token gate, the bounded gh stderr sanitization, and the required-secret blank-replacement guard with mutation-resistant tests. Log the authenticated account identity on channel connect so an out-of-band gh auth switch is visible to operators. Align test secret-source fixtures with the SDK union and complete the design doc's change footprint. * fix(channels): address third-round review feedback for GitHub local gh auth (#8461) * fix(channels): address fourth-round review feedback for GitHub local gh auth (#8461) * fix(channels): address fifth-round review feedback for GitHub local gh auth (#8461) * fix(channels): address sixth-round review feedback for GitHub local gh auth (#8461) * fix(channels): address seventh-round review feedback for GitHub local gh auth (#8461) * fix(channels): address eighth-round review feedback for GitHub local gh auth (#8461) --------- 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-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
a123d0030a
|
ci(review): prepare evidence-image tooling for GitHub-triggered reviews (#8454)
* ci(review): prepare evidence-image tooling for GitHub-triggered reviews Reviews triggered on GitHub cannot attach images today for three reasons; this wires the two that live in the workflow: - Install tmux and freeze (pinned, checksum-verified) before the review runs. Both are optional by contract — the evidence ladder degrades honestly without them (png -> ans-only -> refused, recorded in the capture manifest) — so the step never fails the review; it only decides which rung the runner can reach. tmux mirrors the tolerant install qwen-autofix.yml already uses; freeze falls back to ~/.local/bin when passwordless sudo is absent. - Pass QWEN_REVIEW_ASSETS_REPO from a repository variable to the review step. Publishing stays OPT-IN by design: with the variable unset the env is empty and publish-assets refuses (parseAssetsRepo trims and rejects empty), so nothing changes until a maintainer sets the variable. When set, evidence images land on commit-pinned pr-assets/<pr>-review branches — already covered by the visuals cleanup workflow — pushed with the same CI_BOT_PAT the step uses. The third reason is release lag: the capture producer (capture-tui, #8388) has to merge and ship in a release before rendering claims can generate images on CI at all. This change is inert until then. * fix(ci): capture-tools step review fixes — enforced tolerance, version pin, cached fallback R1-1: the never-fails contract is now enforced twice — continue-on-error at the YAML level (the belt) and set +e with a trailing exit 0 inside (the suspenders); under the runner's default bash -e several statements (mktemp, install, sudo install with an empty path) could previously abort the step and fail the review the comment promised never to fail. R1-6: probe the VERSION, not just the binary — on a persistent self-hosted runner an installed freeze made any FREEZE_VERSION/SHA bump a silent no-op; the pin now forces a refresh when the cached binary does not match. Cached-fallback fix: put ~/.local/bin on PATH (and GITHUB_PATH) before the probe — a sudo-less runner otherwise re-downloads the tarball on every review run forever. R1-3: the step comment says capture-tui is UPCOMING (#8388, not in the released CLI) and names qwen review drive as today's tmux consumer, so the step cannot be mistaken for stale dead weight and deleted from under the follow-up. R1-4: the retention comment scopes the cleanup-workflow claim to the same-repository designation; a fork or scratch destination manages its own retention (docs updated to match, plus a note documenting the repository VARIABLE a maintainer sets to enable publishing). R1-5: the step's real bash now runs in the workflow behavioural harness under bash -e with stubbed sudo/apt/curl/sha256sum/tar/uname: worst-runner and checksum-reject scenarios exit 0 installing nothing, the no-sudo happy path pins the ~/.local/bin + GITHUB_PATH pairing, and the version-pin probe is pinned from both sides (wrong version re-downloads, matching version skips). Real freeze/sudo on a developer machine are shadowed so the tests are deterministic and can never install to /usr/local/bin. Nit: both sudo guards now check sudo -n true. * fix(ci): capture-tools step review fixes — step-owned tool dir, anchored probe, honest failures Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): capture-tools test harness — shadow tmux, don't blank its PATH dir The harness dropped every host PATH directory that ships a tmux so the step's apt branch would depend on the scenario, not on the machine hosting the suite. On GitHub-hosted ubuntu runners tmux lives in /usr/bin, so the filter blanked /usr/bin wholesale — bash, grep, mkdir, and tar included — and execFileSync('bash') died of ENOENT: all seven capture-tools tests failed in the Test (ubuntu-latest Node 22.x) job while passing on tmux-less dev machines. Replace the directory-level drop with an entry-level shadow: each tmux-bearing directory is mirrored (symlinks) into a scratch dir minus the tmux entry, in place, preserving PATH order and the empty-entry stripping the old filter did. Hosts without tmux take the map through unchanged, and Windows (no tmux in its PATH, no symlink branch) keeps its current behavior exactly. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): capture-tools test review fixes — faithful stubs, env shape pins, missing-branch scenarios Round-3 review findings: the harness executed several of the step's branches but asserted nothing about them, so probe-verified mutants (dropped tmux guard, deleted warning/degradation messages, malformed or missing FREEZE_VERSION/FREEZE_SHA256, wrong hash variable, dropped URL `v` prefix or curl `-L`, severed tarball paths, broken regex boundary, leaked mktemp dir) all shipped green. - Make the curl/sha256sum/tar stubs model their real contracts: exact pinned URL, pinned checksum over a file curl actually wrote, existing -xzf operand - Pin FREEZE_VERSION/FREEZE_SHA256 shape in captureToolsSource - Pin the full curl flag set and the three-site tarball path agreement - Assert the stale-renderer warning (fires on degraded re-download, silent on the happy path) and the tmux-unavailable message - Pin TMPDIR and assert the mktemp cleanup leaves it empty - Add the two missing scenarios: tmux-present skips apt, cached version extending the pin with a leading digit re-downloads Verified by 13 mutation probes: every named mutant now turns the suite red (13/13 killed), baseline 34/34 green. * fix(ci): capture-tools step review fixes — hash-verified cache, per-run PATH promotion * fix(ci): capture-tools step review fixes — verified-bytes-only installs, step timeout Review findings on the capture-tools step: - Drop the PATH-trust branch: a freeze already on PATH was accepted on its own --version and executed to probe it — exactly the self-report the FREEZE_BIN_SHA256 comment declares attacker-controllable, from dirs writable between jobs on both runner classes. The checksummed download always runs now; the cache makes it free after the first run. - Guard $tools_bin in the download branch: with mktemp failing, the unguarded install resolved to /freeze — harmless unprivileged, but a root-in-container self-hosted runner writes it and reports success with nothing on PATH. - Copy-then-verify the cache: install into the fresh per-run dir FIRST, verify THOSE bytes, delete both copies on mismatch — the verified bytes are the bytes later steps execute, closing the check-then-copy race for free. This makes the separate pre-verify block redundant; it is deleted. - Add timeout-minutes: 5 — continue-on-error bounds failure, not duration, and a stalled `sudo apt-get update` mirror had no other bound under the 300-minute job cap. - Report block: say the resolved freeze is likely broken when its --version produces nothing, instead of echoing a blank line and calling it stale; the mismatch wording is direction-neutral now. Tests: replace the PATH-trust scenario with a planted-PATH one (marker outside the scenario dir proves the plant never executes), add the mktemp-failure scenario (the install stub succeeds like root would, so the unguarded mutant is caught) and the promoted-dir 0700 assertion; re-anchor the two digit-boundary tests on the report's warning. 41/41 green; both fix mutants verified killed. * fix(ci): capture-tools review fixes — stale-dir cleanup, pinned guards Address round-5 review: - R5-1 (Critical): the per-run qwen-review-tools.* dir under RUNNER_TEMP was never removed; RUNNER_TEMP survives across jobs on the shared pool, so every review run accumulated one dir + one Go binary, unbounded. 'Clean stale agent state' now removes stale dirs before the install step creates the current run's dir, matching the qwen-triage.yml convention. The harness comment claiming the dirs were runner-cleaned is corrected. - R5-6: the cache re-verification rejection branch now logs why it deletes the cached binary instead of degrading silently. - R5-7: bump-checklist note beside the freeze pins — the harness stubs key on the same env values, so a transposed hash pair must be caught against the real release artifacts at bump time. - R5-2/R5-3/R5-4/R5-5: four unpinned step properties now pinned (the if: guard, the sudo -n probe flag, install-after-context ordering, and the cache branch's tools_bin guard via a new mktemp-fails scenario); six mutation probes confirm each pin kills its mutant. * fix(ci): capture-tools review fixes — curl budget, swept scratch dir, wiring pins * fix(ci): capture-tools review fixes — harness mutation pins, pin-pair self-check Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): capture-tools review fixes — shadow-farm cleanup, backoff budget term * fix(ci): capture-tools review fixes — report probes only installed freeze, age-gated sweep --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
c73b5ed887
|
ci: run Windows merge queue tests on ECS (#8386)
* ci: run Windows merge queue tests on ECS
* test(channels): skip POSIX mode assertion on Windows
* ci: expose Git Bash on Windows ECS runner
* ci: scope Windows ECS tuning to self-hosted and restore full test:ci
Review feedback on the Windows ECS routing: dropping test:scripts removed the only Windows execution of 9 Windows-only install-script tests, and the job-wide PowerShell default plus narrowed test command changed the kill-switch fallback away from the known-good hosted configuration.
Restore the full npm run test:ci on both paths (bash is available: pre-installed on hosted runners, exposed via the Git Bash PATH entry on ECS) and gate every ECS-specific adjustment on runner.environment: the PowerShell setup step (now also skip_ci-guarded), TEMP/TMP/LC_ALL env writes, and the Linux-style Node setup split that fails with an actionable error naming MAINTAINER_ECS_RUNNER_DISABLED. The windows-2022 fallback is byte-for-byte the pre-ECS job again.
* test: make Windows CI suites platform-aware
* ci: add stale-checkout guard to Windows ECS test job
* test(core): compare canonical directory identity
* ci: add fork guard and review follow-ups to Windows ECS job
* test(core): exercise real directory identity change
* test(core): wait for killed lease process exit
* test(scripts): avoid cmd echo trailing spaces
* test(scripts): use unambiguous cmd echo syntax
* test(cli): avoid sidecar I/O in truncation test
* test: fix Windows script-suite gaps and unify platform gating
- Fix missed trailing-space cmd stub in package-scripts.test.js so the
'runs prepare steps in order' assertion passes on Windows.
- Add qwen-pr-review-workflow.test.js and pr-self-report-label.test.js to
the win32 exclude list (both test Linux-only workflows and are not
portable to Windows).
- Replace local itPosix/describeOnNonWindows consts with vitest's built-in
it.skipIf/it.runIf/describe.skipIf, matching the codebase idiom.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(scripts): restore Windows workflow coverage
* test(scripts): re-exclude Windows-incompatible workflow tests on win32
Re-add pr-self-report-label.test.js and qwen-pr-review-workflow.test.js to
the win32 exclude list. Both fail on a Windows runner for reasons the code
still carries: qwen-pr-review-workflow.test.js calls execFileSync('mkdir'),
which has no executable to resolve there, and pr-self-report-label.test.js
joins PATH with ':', corrupting the ';'-separated Windows PATH so its gh
stub never resolves. Excluding them restores a green Windows gate; Linux CI
remains their authoritative coverage. Document the criterion inline.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* ci: extract checkout-head guard into composite action, pin Windows gate (#8386)
Address review round 2: move the stale-checkout guard shared by the four CI gates into .github/actions/verify-checkout-head so the copies cannot drift, pin the Windows gate kill-switch routing and guard wiring in the script tests, re-enable lint.test.js on Windows via separator normalization and a lazy linter setup in scripts/lint.js, unify the platform skips on it.skipIf(process.platform === 'win32'), and document the queued-run behavior of the ECS kill switch.
* ci: fail fast in Windows gate environment setup (#8386)
* ci: dedupe self-hosted runner steps into actions, pin gate mutations (#8386)
* fix(ci): checkout before repository-local actions in Windows gates (#8386)
* fix(ci): configure Windows runner before bash guard
* test(ci): pin remaining shared-action wiring in script tests (#8386)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): skip zip-dependent packaging tests when zip is missing (#8386)
* fix(ci): validate full Windows smoke path
* fix(ci): match Windows smoke shell to gate and drop dead runs-on guard (#8386)
* fix(ci): make SIGTERM escalation test Windows-aware and tighten pins (#8386)
The CDP acceptance test asserted a POSIX-only SIGKILL escalation, which
fails deterministically on Windows where kill('SIGTERM') terminates the
child directly — blocking the Windows merge-queue gate. Assert the
platform-appropriate signal instead.
Also address review suggestions: probe `unzip` alongside `zip`, pin the
integration_cli guard's missing step-level `if:`, stop getWorkflowStep
at unnamed steps, pin install-script.test.js out of the win32 excludes,
add the stale-checkout guard to windows-runner-smoke.yml, pin the
Node preflight warning branch and the guard reject path contiguously,
and extend the smoke shell-parity loop to the npm cache step.
* docs(ci): clarify Windows runner trust boundary
---------
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
|
||
|
|
a5c637b749
|
feat(web-shell): add native Live Voice (#7859)
* feat(web-shell): add native Live Voice * fix(web-shell): address review feedback for Live Voice PR (#7859) - Quote all strings in electron-builder.yml to fix yamllint CI failure - Gate discovery publish on liveVoiceEnabledAtBoot to avoid writing bearer token to disk when Live Voice is disabled (M1) - Add child identity guard to CommandMonitor stdout/stderr handlers to prevent stale helper output from corrupting the new buffer (M4) - Add exponential backoff to sent-completion delivery retry (M3) - Skip broadcastState when setCallState/setTranscript value is unchanged to reduce per-audio-delta overhead (H1) - Document sent-mode completion notification in module docstring (H2) - Remove dead protocol/nonce aliases from readDiscoveryFile - Fix single instance lock fall-through with process.exit(0) * fix(cli): register realtime_voice in docs contract and env guard (#7859) * fix(web-shell): address review feedback for Live Voice PR (#7859) * fix(cli): discard orphaned isolated dir when parent restore fails (#7859) * fix(web-shell): address review feedback for Live Voice PR (#7859) * fix(serve): harden live turn recovery * fix(desktop): restore Live Host native build * fix(live): align native host and session isolation * fix(acp): preserve live worker continuation lineage * fix(live): classify provider close reasons * fix(serve): discard unused recovered conversation dirs * fix(live): isolate authorized realtime responses * fix(live): preserve realtime response authority * feat(web-shell): complete Live Voice onboarding * fix(live): persist realtime-owned dialogue * fix(live): preserve final speech while stopping * Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)" This reverts commit 7110bec6b034c702bca6e28e35b93c7f70e729cd. * Revert "fix(cli): discard orphaned isolated dir when parent restore fails (#7859)" This reverts commit 85165f1b2ddfaa311b8be91acdd76a6f388f6204. * Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)" This reverts commit 9199fa633e102bb8f24e4b216d322be4323eb3fc. * Revert "fix(cli): register realtime_voice in docs contract and env guard (#7859)" This reverts commit 6b6b1718352ef01a98a73976b5c7c4433fd14c35. * Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)" This reverts commit e083779105199d26de3afd8ad00719a08efe3099. * revert(live): remove remaining takeover behavior * revert(live): restore pre-rollback implementation * test(cli): align Live diagnostics env guard * test(release): cover Live Host publication * fix(ci): re-sign Live Host package before verification * fix(serve): scope sent completion notifications to Live * fix(web-shell): preserve live setup errors * fix(live): align realtime backend speech lifecycle * ci(live): publish Live Host independently * test(cli): mock Live speech bridge handler * test(release): align Live Host workflow contract * fix(live): address release and lifecycle review findings * fix(live): release completed call tracking --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com> Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
67d128715e
|
chore(cua-driver): sync upstream v0.17.0 (#8564) | ||
|
|
3084326243
|
fix(webui): recover complete turns after live journal truncation (#8414)
* fix(webui): recover live journal turns Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(webui): address PR review feedback (#8414) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(webui): refresh repair pagination anchor (#8414) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(webui): restore evicted repair side effects Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
1a2bb10298
|
fix(review): admit evidence images by content, not by name — magic-byte sniffing (#8459)
* fix(review): admit evidence images by content, not by name — magic-byte sniffing The publish-assets allowlist was extension-based, and an extension is a claim anyone can make: combined with a prompt-injected review run, whatever could name a file evidence.png could host up to the size cap of arbitrary bytes at a github.com URL through the evidence push (raised in #8454's review as an enablement consideration). sniffImageFormat reads the four admitted signatures (PNG, JPEG, GIF87a/89a, RIFF+WEBP — RIFF alone is not enough, AVI and WAV share the container prefix) and validateAssetContent rules the content against the format the extension claims, fail-closed: an unrecognized signature refuses even when the extension is allowed. publish-assets applies the ruling to every file's first bytes before anything is uploaded — all-or-nothing, same refusal contract as the other gates. Pinned: the four signatures, truncated/empty headers, the RIFF/AVI near-miss, extension-format mismatch, fail-closed unknown extensions, and end-to-end: a shell script named evidence.png refuses with exit 3 and nothing pushed. * fix(ci): re-pin review timeout tests to vars externalization (#8459) * fix(review): single-source the asset allowlist and pin sniff depth (#8459) Address round-1 review suggestions: - ASSET_EXTENSIONS now derives from EXTENSION_FORMAT, so admitting a format is a one-place change the batch gate and the content gate cannot drift on; the duplicated extension extraction moves into one claimedExtension helper shared by both gates. - Pin what the mutation probes showed unpinned: the GIF87a branch (sniff + admission), uppercase extensions at the content gate, the refusal message direction, the full depth of every signature (near-miss negatives), and the publish-time 16-byte slice end to end (a WEBP publishes through runPublishAssets; verified the test fails when the slice shrinks to 8). * fix(review): pin every sniff check and guard the allowlist lookup (#8459) * fix(review): pin every sniff byte and the two-gate format coupling (#8459) * fix(review): align the two-gates comment with the pin that enforces it (#8459) * fix(review): sharpen evidence-gate diagnostics and pins (#8459) - Content refusals now name the JSON-quoted full path, so two same-named files from different directories are tellable apart (the sibling read-error refusal already spent the path). - Export ASSET_HEADER_BYTES from the lib that owns the sniff depth; the publish call site uses it and the two-gates pin slices canonical headers to it, so a future longer signature fails the pin, not real publishes. - One shared refusal builder keeps the allowlist message identical in validateAssetFile and validateAssetContent. - Narrow the sniffImageFormat threat model to what magic bytes buy: binds the claimed type to the leading bytes, does not stop prefixed payloads. - One-byte-off matrix becomes a labeled it.each table (failures name the exact corrupted byte); imports re-alphabetized. * test(review): pin the shared extension refusal across both asset gates (#8459) * test(review): pin lastIndexOf extension parsing for multi-dot asset names (#8459) * fix(review): admit WEBP by its fourcc and name refused files once (#8459) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
32e2741577
|
perf(core): clear tool results to a low watermark to preserve prompt cache (#8464)
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
* perf(core): clear tool results to a low watermark to preserve prompt cache Size-triggered microcompaction now clears oldest compactable tool results down to half the threshold instead of stopping just below it, so the conversation prefix stays stable between clearings and provider prompt caches keep matching. The recent-result budget now protects committed results only; pending results no longer consume protection slots but stay counted, uncleared, and live for file-read-cache resolution. Adds the watermark to cleanup metadata and the debug log. Fixes #8463 * fix(core): harden size-cleanup protection against zero-char and pending refs Review follow-up for the low-watermark change: keepRecent now selects from committed results that are actually clearable (positive, successful, uncleared output), so trailing errors, prior placeholders, and empty outputs no longer absorb protection slots. Pending refs are dropped from the keep set entirely — a pending read may be a cache-hit placeholder rather than file bytes, so it must not suppress eviction reporting; over-disarming only costs a redundant re-read. Adds regression tests for both plus the protected-saturation consecutive-trigger corner. * qwen: address PR review feedback (#8464) Pin the (soft-exceeded) log marker with the one-line assertion suggested by the sandboxed verification report (finding S-1): the all-protected overage test now asserts 'target 250000 (soft-exceeded)', killing the surviving mutant M4. * qwen: address PR review feedback (#8464) * qwen: address PR review feedback (#8464) * qwen: address PR review feedback (#8464) Two P1 context-integrity fixes from review: (1) media-only tool results (image/PDF reads with empty text output and bytes on functionResponse.parts) stay in the idle-path keepRecent candidates instead of being dropped by the zero-char filter; (2) only write_file results vouch for file residency in kept-path accounting — edit calls carry just old/new snippets while still setting the cache's sticky full-read flags, so a kept edit can no longer suppress eviction reporting after the full read is blanked. Regression tests for both. * qwen: address PR review feedback (#8464) Pin the absence of the (soft-exceeded) marker at the exact watermark boundary: clearing that lands the virtual total exactly on the watermark must not be flagged. Kills the >= and always-true mutants of the marker condition that previously survived the suite. * qwen: address PR review feedback (#8464) --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |