mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-10 01:29:17 +00:00
956 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9ee8546a60
|
fix(shell): avoid Unix pager default on Windows (#6390)
* fix(shell): avoid Unix pager default on Windows * fix(shell): clear inherited pager env on Windows * docs(shell): clarify platform-specific pager default * fix(shell): normalize pager env handling * fix(shell): preserve git pager fallback behavior * test(shell): stabilize pager env coverage |
||
|
|
067cfbba62
|
docs: consolidate design docs and plans under docs/ (#6417)
Design docs and implementation plans were scattered across .qwen/design, .qwen/plans, and docs/superpowers. The .qwen/ locations are git-ignored, so docs written there never got tracked, while docs/design already held the richer, version-controlled set. Consolidate everything under docs/design and docs/plans, relocate two stray root docs into docs/design, and repoint the references left dangling by the move (moved-doc cross-links and a few source comments). Also update AGENTS.md and the feat-dev skill so the documented workflow writes new design docs and plans to the tracked docs/ locations. Co-authored-by: DragonnZhang <dragonzhang1024@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
06cd7ce13f
|
feat(cli): add --project and --global flags to /model for per-project model persistence (#6060)
* feat(cli): add --project and --global flags to /model for per-project model persistence Add scope control to the /model command so users can persist model selections to either project-level or user-level settings independently. - /model --project: persist to workspace .qwen/settings.json - /model --global: persist to user ~/.qwen/settings.json - /model (no flag): unchanged behavior (backward compatible) - Model dialog title shows scope: 'Select Model (this project)' / 'Select Model (global)' - Completion and argumentHint updated with new flags - Full i18n support for zh/en Closes #6052 Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): add missing zh-TW translations for /model scope flags Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): address PR review — scope flags, subcommand persistScope, titles, tests - parseScopeFlags: use (?:^|\s) instead of \b for --flag matching (\b fails because - is not a word character) - Completion: strip all flags to isolate model prefix, supports any order - Subcommand dialogs (fast/voice/vision) now propagate persistScope - slashCommandProcessor forwards persistScope for all subcommand cases - ModelDialog title combines subcommand mode + scope label e.g. 'Select Fast Model (this project)' - Subcommand confirmations show scope suffix (project/global) - Extract persistScopeSpread() helper to reduce duplication - Add 9 tests covering scope flags, dialog returns, confirmations - Add i18n keys for scope suffix labels in zh/en/zh-TW Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): use Partial<Config> & {[key:string]:unknown} to fix index signature TS error Replace Record<string,unknown> with Partial<Config> & {[key:string]:unknown} to satisfy TS4111 index signature access rule in the CI build. Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): add scope suffix to ModelDialog history items Address review comment: historyManager.addItem for voice/fast/vision/main model selections now shows scope indicator like ' (this project)' or ' (global)', consistent with CLI direct-set confirmations. Affected: handleModelSwitchSuccess (main), handleSelect (voice/fast/vision) Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): wrap scopeSuffix in t() and unify wording with ModelDialog - scopeSuffix in modelCommand.ts now uses t(' (this project)') / t(' (global)') instead of hardcoded English strings, matching ModelDialog.tsx wording - Main model confirmation uses shared scopeSuffix instead of separate i18n keys, eliminating 'Model: {{model}} (project)' duplication - Remove unused i18n keys from en/zh/zh-TW locales - Update tests to expect '(this project)' wording Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): address code review feedback — scope validation, i18n, tests - Reject inline prompt + scope flag combination with clear error (#1) - Add mutual exclusivity check for --project and --global (#5) - Verify setValue scope parameter in tests + add --global test (#2) - Extract scopeSuffix to shared variable, remove duplication (#3) - Remove dead i18n keys 'Select Model (this project)' / '(global)' (#4) - Fix scopeSuffix placement on model line not API key line (#8) - Add fr.js / ja.js translations for scope keys (#10) - Remove unused export ModelDialogPersistScope (#6) - Wrap non-interactive help text in t() with new flags (#7) - Fix argumentHint grouping to show mode vs scope flags (#11) Signed-off-by: Alex <alex.tech.lab@outlook.com> * fix(cli): reject --project when workspace is untrusted Reject --project scope flag before direct persistence or opening ModelDialog when settings.isTrusted is false. Workspace settings are ignored on merge in that state, so the save would silently not take effect. Also mirrors the guard in ModelDialog.tsx resolvePersistScope() to fall back to user scope when the dialog is opened with --project on an untrusted folder. Default mock settings now includes isTrusted: true. Signed-off-by: Alex <alex.tech.lab@outlook.com> --------- Signed-off-by: Alex <alex.tech.lab@outlook.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
80340fb73f
|
fix(review): remove qwen-code-specific core-infra gate from bundled /review (#6412)
The bundled /review skill is a general command that runs against arbitrary
repositories (and cross-repo PRs), but a previous change baked qwen-code's own
"core infrastructure is maintainer-only" governance into the shipped prompt:
hardcoded packages/core and packages/*/src/{auth,providers,models,config,tools,services}
paths, a 500+ line hard block, and an authorAssociation-based maintainer check.
Those path names are generic — src/auth, src/config, src/tools, src/services are
common across monorepos — so an external contributor's large PR to an unrelated
repo would be hard-blocked as "must be maintainer-initiated" under a policy that
repo never adopted.
Remove the gate and its escalate-flag plumbing (Steps 1, 6, and 7) from the
bundled skill, along with the matching DESIGN.md rationale and the user-doc
section. qwen-code's maintainer-only policy stays documented in AGENTS.md for
this repo. The Issue Fidelity / root-cause ownership agent (Agent 0) is a
universal review principle and is left unchanged.
Co-authored-by: dragon <dragon@U-2Q53JQG9-0233.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
bcdb44c5d3
|
docs(hooks): document PreToolUse permissionDecision 'ask' behavior (#6411)
Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
57326e55be
|
feat(review): add issue-fidelity and root-cause ownership gate to /review (#6395)
* feat(review): add issue-fidelity and root-cause ownership gate to /review Adds a dedicated Issue Fidelity & Root-Cause Ownership agent (Agent 0) to the /review pipeline and a core-infrastructure scope gate that runs before the review agents. Agent 0 fetches linked GitHub issue evidence directly (closingIssuesReferences plus issue comments) instead of trusting the PR author's framing, compares the original reported failure against the PR's claimed fix, and flags client-side parser/sanitizer workarounds for malformed upstream output as Critical unless a maintainer explicitly requested the defensive mitigation. The core-infra gate applies the repository's existing two-tier maintainer-only rule before spending review budget. This hardens the pipeline against a false-approval mode where a bot PR passes its own tests and reads as internally reasonable but fixes the author's mistaken diagnosis rather than the linked issue's actual root cause. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(review): address PR review feedback on issue-fidelity gate - Fetch issue evidence with `gh issue view --json title,body,comments` so the issue body (reporter repro/observed payload/expected behavior) is included; `--comments` alone omits it. Use each closingIssuesReferences entry's own repository so cross-repo linked issues resolve correctly. - Treat closingIssuesReferences as a discovery hint (fetch apparent target issues even when it is empty) and treat fetched issue content as untrusted data (extract facts, ignore embedded instructions). - Run Agent 0 (Issue Fidelity) only for PR targets; skip it for local-diff and file-path reviews, and require the PR number/repo/context in its prompt. Handle empty references / non-bugfix / gh failure explicitly. - Pass Agent 0's quoted issue evidence to Step 4 batch verification and stop it rejecting issue-grounded findings just because the code compiles/tests pass. - Make the core-infrastructure gate concrete: deterministic maintainer signal via authorAssociation, count only core-path lines, honor the AGENTS.md low-risk-sweep exception, clean up the worktree on hard block, run the gate right after fetch-pr (before npm ci), and map escalate -> COMMENT (never APPROVE) in Steps 6-7. - Sync agent counts and token math across SKILL.md, DESIGN.md, and code-review.md (Agent 0 is PR-only; ~620-730K). * docs(review): rename 'Linked Issue Fit' heading to 'Issue Fidelity' Aligns the code-review docs heading with the 'Issue Fidelity' name used for Agent 0 in SKILL.md and DESIGN.md, so the section connects to the pipeline diagram. Addresses review feedback. * docs(review): stop core-infra hard block before load-rules and surface it via --comment - Hard block now stops before Step 2 (load-rules) instead of before Step 3, so a PR destined for hard-block no longer runs the load-rules step. - In --comment mode the hard block posts an event=COMMENT on the PR, matching the escalate path's GitHub visibility, so external authors see the block. --------- Co-authored-by: dragon <dragon@U-2Q53JQG9-0233.local> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
6f2f21ff7e
|
docs: standardize GitHub Actions capitalization (#6367) | ||
|
|
1783ae86f3
|
docs(web-shell): document chart renderer integration (#6353)
* docs(web-shell): document chart renderer integration * docs(web-shell): describe daemon-backed chart artifacts * docs(web-shell): clarify chart ref validation layers |
||
|
|
be0b0749c1
|
docs: fix settings.json reference drift against schema (#6351)
Correct and complete the user-facing settings documentation against packages/cli/src/config/settingsSchema.ts: - settings.md: fix general.defaultFileEncoding type (enum, not string); document the general.voice.* dictation settings, top-level modelFallbacks and modelPricing, tools.computerUse.idleTimeoutMs, mcp.toolIdleTimeoutMs, and the skills.disabled denylist. - model-providers.md: correct the resolution-layers table — only --openai-api-key/--openai-base-url exist; there are no provider-specific credential CLI flags. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
b23f888d73
|
[codex] add proactive channel loop tools (#6287)
* feat(channel): add proactive loop tools * fix(channels): stabilize proactive loop routing * fix(channels): gate loop tools in shared sessions * fix(channels): tighten channel loop tool routing * fix(channels): close loop tool review blockers * fix(dingtalk): preserve markdown tables * fix(dingtalk): use app token for reactions * fix(channels): scope loop tools to active caller * fix(channels): preserve group session metadata * fix(channels): normalize loop targets * test(cli): cover settings cron disable path * fix(channels): address dingtalk review suggestions * fix(dingtalk): restore table normalization * fix(channels): mark loop tool failures * fix(channels): tighten loop mcp protocol handling * test(channels): cover loop tool guard paths * fix(channels): await loop mcp registration * test(channels): preserve base proactive target default * refactor(channels): clarify loop target promotion * fix(channels): harden loop recurring input * fix(channels): ack loop mcp notifications * fix(channels): preserve legacy loop targets * test(channels): cover channel loop wiring paths * fix(channels): retry skipped loop mcp registration * fix(channels): keep promoted loop targets visible * fix(channels): harden loop mcp input logging |
||
|
|
3bf0fa0af0
|
Feat: LSP Server support hot reload (#5953)
* feat(core): Add LSP server config hot-reload support - Implement reconcileServerConfigs to diff desired vs current LSP configs and apply minimal add/remove/restart operations with a serialized reconcile queue - Add configHash utility to detect config changes via stable hashing - Add lspConfigWatcher in CLI to watch .lsp.json and trigger reconciliation on file changes - Extend LspServerManager with per-server config hash tracking and detailed debug logging - Add design docs for LSP runtime reinitialization and hot-reload overview - Include comprehensive unit tests for all new modules * refactor(cli): Extract registerLspHotReload from main function Move the LSP config file watcher setup and reconciliation logic into a dedicated module-private function registerLspHotReload, reducing the size and nesting depth of the main startup flow. Added a JSDoc summarizing responsibilities, early-return conditions, and the AppEvent.LspStatusChanged side effect. * fix(lsp): release server resources during reload * fix(lsp): address hot reload review feedback * fix(lsp): harden hot reload reconciliation * docs(lsp): update hot reload design notes * fix(lsp): harden hot reload retry semantics * fix(lsp): harden hot reload lifecycle * fix(lsp): harden hot reload lifecycle * fix(lsp): isolate hot reload recovery paths * fix(lsp): align command probes and replay tracking * fix(lsp): prevent crash restarts during shutdown * fix(lsp): preserve reload state across failures * fix(lsp): cancel reloads during shutdown * fix(lsp): handle socket startup races * fix(lsp): harden command probe env and socket startup * fix(lsp): report skipped reload and restart states * fix(lsp): harden hot reload lifecycle cleanup * chore: add one comment for `Object.create(null)` --------- Co-authored-by: heyang.why <heyang.why@alibaba-inc.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
5d0733f79c
|
feat(core): stabilize tool schema declaration order (#6339)
Make tool declaration ordering deterministic so prompt-cache prefixes do not depend on asynchronous registration history. - Sort function declarations by canonical tool name after existing visibility filtering - Preserve deferred, revealed, and alwaysLoad filtering semantics - Add tests covering deferred tools, revealed tools, and MCP registration order - Document the prompt-cache motivation and next-step cache break detection plan Co-authored-by: heyang.why <heyang.why@alibaba-inc.com> |
||
|
|
fa81e0a604
|
docs: fix skill invocation syntax and include Feishu in channel lists (#6320)
* docs: fix skill invocation syntax and include Feishu in channel lists * docs: add Feishu column to channel media-handling table Address review feedback on PR #6320: after adding Feishu to the prose channel lists, the 'Platform differences' media-handling table still omitted it. Add a Feishu column (images/files via the authenticated Open API resources endpoint, 50MB limit; rich-text 'post' captions), verified against packages/channels/feishu/src/media.ts and FeishuAdapter.ts. Add a note that QQ Bot ignores incoming media (per QQChannel.ts) so it has no row. * docs: clarify Feishu post messages drop embedded images The Platform differences table claimed Feishu rich-text (post) messages carry 'mixed text + images', but FeishuAdapter's post parser only extracts text/a/at nodes and silently drops img nodes (both the live handler and the history-backfill path). Correct the Captions cell to say text is extracted and embedded images are ignored. * docs: mark clientId/clientSecret as required for Feishu too Feishu declares requiredConfigFields: ['clientId', 'clientSecret'] (packages/channels/feishu/src/index.ts), same as DingTalk, but the channel options table listed both fields as DingTalk-only. Update the Required column and descriptions to cover Feishu (App ID / App Secret). * docs: note token is not needed for Feishu in channel config table * docs: note 50MB limit on Feishu image downloads Feishu images and files both flow through the same downloadMedia() in packages/channels/feishu/src/media.ts, which enforces a single MAX_DOWNLOAD_BYTES = 50MB cap. Add the (50MB limit) note to the Images cell for consistency with the Files cell. * docs: clarify /skills panel-vs-run behavior per review feedback - skills.md: add a migration Note that /skills <name> now opens the Skills panel and ignores trailing args; use /<skill-name> to run. - commands.md: list the /skills Usage cell as /skills, /<skill-name> for consistency with the rest of the table. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: 易良 <1204183885@qq.com> |
||
|
|
b13032d3ae
|
docs(design): daemon side-channel coordination (A1/A2/A4/A5) (#4511)
Co-authored-by: jinye <djy1989418@126.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: 易良 <1204183885@qq.com> |
||
|
|
fe816f625f
|
feat(cli): Surface daemon prompt queue status (#6325)
* feat(cli): surface daemon prompt queue status Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6325) * codex: address PR review feedback (#6325) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6325) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
7a528d078a
|
feat(daemon): Add session organization (#6305)
* feat(daemon): add session organization Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): address session organization review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(daemon): cover session organization review cases Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6305) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6305) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6305) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6305) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6305) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6305) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(web-shell): Address session organization review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Harden session organization review edge cases Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Address session organization review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
6a726f6c63
|
refactor(core): centralize extension runtime refresh (#6152)
* refactor(core): centralize extension runtime refresh * test(core): add regression tests for allSettled and try/catch resilience in refreshExtensionRuntime * test(core): add restartMcpServers reject test and restore design rationale comments Address @wenshao's review feedback: - Add test for restartMcpServers rejection (the only fatal error path) - Restore key 'why' comments about allSettled and try/catch design decisions - Logger tag change to EXTENSION_RUNTIME_REFRESH is intentional (standalone module) * docs(core): add error-handling tier contract to refreshExtensionRuntime Add block comment documenting the three-tier error-handling contract (fatal/swallow/swallow) so future maintainers know which tier applies when adding new refresh steps. Addresses review feedback on #6152. --------- Co-authored-by: 俊良 <zzj542558@alibaba-inc.com> Co-authored-by: 易良 <1204183885@qq.com> |
||
|
|
bfce93a304
|
feat(acp-bridge): Add EventBus subscriber byte cap (#6314)
* feat(acp-bridge): add EventBus subscriber byte cap Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6314) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
e1f5d21008
|
fix(core): treat request timeout of 0 as disabled instead of aborting immediately (#6288)
* fix(core): treat request timeout of 0 as disabled instead of aborting immediately A provider `generationConfig.timeout` of `0` (and `QWEN_CODE_API_TIMEOUT_MS=0`) now disables the request timeout, matching the existing `QWEN_STREAM_IDLE_TIMEOUT_MS=0` convention, instead of being coerced to the 120s default (Anthropic `||`) or passed to the OpenAI SDK as `timeout: 0` (which the SDK treats as an immediate abort). - add `resolveRequestTimeout()` + `DISABLED_REQUEST_TIMEOUT_MS`, mapping a disabled timeout to the JS timer ceiling (2^31-1 ms), reusing the same ceiling already used for `MAX_STREAM_IDLE_TIMEOUT_MS` - use it in the OpenAI default/dashscope providers and the Anthropic provider - accept `0` in the `QWEN_CODE_API_TIMEOUT_MS` env override without weakening the shared `parsePositiveIntegerEnv` (relied on by ~15 other callers to reject 0) - document the timeout unit and 0-disables semantics in settings.md Fixes #6049 * Update packages/core/src/core/openaiContentGenerator/provider/dashscope.test.ts Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com> * test(core): fix broken constants mock merge in dashscope.test Commit 0f5aa0c interleaved two vi.mock('../constants.js') blocks, leaving orphaned fragments that produced TS syntax errors and stopped the dashscope suite from loading. Replace with a single importOriginal-based mock that overrides only DASHSCOPE_PROXY_BASE_URL and delegates every other constant (timeouts, DISABLED_REQUEST_TIMEOUT_MS, resolveRequestTimeout) to the real module, so the mock cannot drift from the implementation. --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com> |
||
|
|
e9a7917d5e
|
feat(web-shell): support compact echarts full data blocks (#6232)
* feat(web-shell): support custom code block rendering * fix(web-shell): harden custom code block rendering * docs: add skill capability gating design * fix(web-shell): make chart skill host supplied * docs(web-shell): write chart skill in English * docs(web-shell): document full-data chart payload * docs(web-shell): use dataset-backed chart payload * feat(web-shell): add echarts full-data renderer * chore(web-shell): keep chart skill host supplied * fix(web-shell): show loading for streaming chart blocks * style(web-shell): polish echarts full-data renderer * fix(web-shell): harden custom code block language parsing * fix(web-shell): harden echarts full-data renderer * fix(web-shell): polish echarts renderer followups * fix(web-shell): reuse enhanced table for chart data * fix(web-shell): recover chart renderer after errors * fix(web-shell): harden chart option handling * fix(web-shell): harden chart data rendering * fix(web-shell): tighten chart renderer guardrails * fix(web-shell): update chart fallback title * fix(web-shell): polish chart renderer review fixes * feat(web-shell): support compact echarts full data blocks * docs(web-shell): add chart skill template * fix(web-shell): address chart review suggestions * fix(web-shell): preserve punctuation language aliases * fix(web-shell): address chart follow-up review * fix(web-shell): harden chart ref resolution * fix(web-shell): cover chart sanitizer follow-ups * fix(web-shell): address chart review follow-ups * fix(web-shell): address chart review leftovers * fix(web-shell): close chart review gaps * fix(web-shell): handle latest chart review |
||
|
|
cdf83d8bd0
|
fix(core): give Stop-hook continuations a fresh per-turn tool-call budget; make the cap configurable (#6238)
* fix(core): give Stop-hook continuations a fresh per-turn tool-call budget; make the cap configurable A blocking Stop-hook continuation (e.g. a /goal iteration) feeds a fresh user-role prompt to the model — a new logical turn — but the loop detector never reset, so an entire goal chain billed one per-turn tool-call budget and healthy long-running goals halted with turn_tool_call_cap. The ACP daemon path already used per-continuation budgets; core now matches. - Reset loop detection at each blocking Stop-hook continuation - Add model.maxToolCallsPerTurn setting (default 100; <= 0 disables), resolved once in Config (<= 0 maps to Infinity) - Honor the in-session 'Disable loop detection for this session' choice in the per-turn cap, as the dialog always claimed - Point the headless halt message at the setting; update dialog/docs * test(cli): add DEFAULT_MAX_TOOL_CALLS_PER_TURN to core module mocks --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
5dc2e1501f
|
feat(serve): Add runtime.activity fields to daemon status API (#6270)
* feat(serve): add runtime.activity fields to daemon status API Add activePrompts, lastActivityAt, and idleSinceMs to the GET /daemon/status runtime section. These fields already exist on the bridge (and are exposed via GET /health?deep=1) but were missing from the richer status endpoint that operators use for troubleshooting. The idleSinceMs value is computed from a cached lastActivityAt read (same pattern as the health handler) to ensure consistency within a single response. * feat(serve): add MCP server health summary to workspace status Extract serversConnected, serversErrored, and serversDisabled counts from the MCP servers array into the workspace.mcp.summary object. Operators can see MCP fleet health at a glance without expanding the full JSON. * fix(serve): guard activity fields against undefined bridge getters Add ?? null / ?? 0 fallbacks for lastActivityAt and activePromptCount to prevent RangeError when a test fake bridge omits these properties. |
||
|
|
3911b1dc34
|
fix(serve): optimize daemon NDJSON stream handling (#6263)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
fe3dd93e8f
|
Add sessionless workspace memory forget and dream (#6227)
* feat(serve): add sessionless memory forget and dream * fix(serve): thread abort through memory forget * fix(serve): address workspace memory review feedback * fix(serve): address memory review follow-up * fix(memory): harden forget review paths * fix(serve): classify memory availability failures * fix(serve): document memory task capacity tiers * fix(memory): address review edge cases * chore: remove mobile-mcp formatting noise |
||
|
|
2a21963026
|
feat(web-shell): display nested sub-agents as a tree in the tasks panel (#6239)
Carry nested-agent lineage (parentAgentId, parentName, depth) through the daemon tasks snapshot as optional fields and render the web-shell tasks panel as a tree: children group under their parent with a ↳ marker and clamped indentation, agents whose parent left the roster are promoted to root with a "from <parent>" annotation, and the detail view gains a nesting line. The [blocking] tag and the two-step stop confirmation now apply only to provably user-blocking chains, mirroring the TUI's agent-forest semantics from #6191. |
||
|
|
9658dccfbb
|
feat(daemon): add session artifact APIs (#5895)
* docs: add session artifacts daemon API design * docs: tighten session artifacts design scope * docs: frame artifacts API as complete v1 capability * docs: address artifacts review follow-ups * docs: clarify artifacts reset boundary * docs: clarify batch hook artifact flow * docs: address latest artifact design audit * docs: tighten artifact event and store semantics * docs: simplify artifact v1 merge policy * docs: resolve artifact v1 review blockers * docs: tighten artifact trust and retention semantics * docs: close artifact v1 boundary gaps * feat(daemon): add session artifact APIs * fix(daemon): harden session artifact semantics * fix(sdk): update daemon browser bundle budget * fix(daemon): tighten artifact ingestion boundaries * fix(daemon): cache artifact workspace realpath * fix(daemon): sanitize artifact add dispatch input * docs(daemon): align artifact change wire shape * fix(daemon): harden artifact status validation * test(daemon): cover artifact acp dispatch * test(daemon): update artifact capability baseline * fix(daemon): clear workspace locator on published artifacts * fix(core): forward post-tool batch artifacts * fix(daemon): harden artifact status refresh * fix(daemon): guard artifact event ingestion * test(daemon): cover non-strict artifact drops * fix(core): align artifact display validation * fix(daemon): serialize artifact store operations * chore(daemon): clarify artifact publisher tool name * fix(daemon): coordinate artifact route mutations * fix(daemon): harden artifact refresh comparison * fix(daemon): harden artifact ingress edge cases * fix(daemon): guard artifact rpc mutations during archive * fix(daemon): gate session metadata mutation auth * fix(daemon): harden artifact route boundaries * fix(channels): compact drained group history * fix(daemon): address artifact review findings * fix(daemon): address artifact review follow-ups * fix(daemon): preserve hook artifact success output * fix(daemon): handle artifact review edge cases * fix(daemon): address artifact review hardening * test(daemon): cover artifact review edge cases * fix(daemon): validate hook artifact aggregation * fix(daemon): improve artifact ingestion diagnostics * fix(daemon): address artifact review feedback * fix(daemon): address artifact review feedback * fix(daemon): harden session artifact ingress * fix(daemon): harden artifact edge cases * fix(daemon): tighten artifact path validation * fix(daemon): address artifact review races * fix(daemon): surface artifact path inspection errors * fix(daemon): forward batch hook artifacts in ACP * fix(daemon): clean artifact bridge metadata * test(daemon): cover artifact store edge cases * fix(daemon): resolve artifact file url symlinks * fix(daemon): harden artifact ingestion paths * fix(daemon): harden artifact review paths * test(daemon): cover artifact tool name sync * fix(daemon): harden artifact republish validation * chore(daemon): remove unrelated artifact PR churn * fix(daemon): address artifact review gaps * test(daemon): cover artifact url rejection * chore(daemon): drop unrelated formatting churn * chore(daemon): update settings schema * fix(daemon): harden artifact validation * fix(daemon): tighten artifact event validation * docs(core): clarify artifact env flag comment * test(cli): align soft failure artifact expectation * fix(daemon): address artifact review edge cases * fix(daemon): enable artifact metadata recording * fix(daemon): harden artifact store review paths --------- Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
0633b8a985
|
feat(scheduler): make recurring cron/loop job expiration configurable (#6173)
* feat(scheduler): make recurring cron/loop job expiration configurable Recurring cron/loop jobs previously auto-expired after a hardcoded 7 days with no way to extend or disable the limit, forcing long-running daemon deployments to recreate jobs weekly. Add an experimental.cronRecurringMaxAgeDays setting (default 7) and a QWEN_CODE_CRON_MAX_AGE_DAYS environment-variable override (takes precedence, for cloud/container deployments). A value of 0 disables expiry so jobs run until deleted; negative or unparseable values fall back to the 7-day default. The configured limit also applies to durable tasks restored from disk, and the CronCreate tool description now reflects the effective limit instead of a hardcoded '7 days'. Closes #6167 * refactor(scheduler): drop unused DEFAULT_RECURRING_MAX_AGE_MS export Review feedback on #6173 — the ms constant is only used inside the module as the constructor default; keep only the days constant public. * fix(scheduler): align zero max-age semantics and warn on cron expiry misconfiguration Review feedback on #6173: - CronScheduler constructor now maps 0 to Infinity (never expire), matching the config layer instead of silently substituting the 7-day default for direct callers. - Invalid QWEN_CODE_CRON_MAX_AGE_DAYS / cronRecurringMaxAgeDays values now log a warning before falling back to the default, leaving a breadcrumb for misconfigured deployments. - Durable tasks found past the recurring max age at load now log a warning before their final fire + delete, since a lowered max age retroactively expires long-lived tasks and deletion is unrecoverable. - New durable-restore tests: a custom max age applies to tasks reloaded from disk, and a disabled max age (Infinity) restores a 30-day-old task as live instead of aging it out. * fix(scheduler): surface cron expiry warnings on the console Review feedback on #6173: - The invalid-config and retroactive-expiry warnings moved from debugLogger (file-only, off unless QWEN_DEBUG_LOG_FILE is set) to console.warn so they reach container/daemon logs where this knob matters; the config warning is latched to fire once per Config instance. - CronCreateTool's constructor now resolves the max age once instead of three times, so an invalid env var can't emit duplicate warnings during tool registration. * fix(scheduler): reject non-finite timestamps in durable task validation Review feedback on #6173: JSON like -1e999 parses to -Infinity, which passes the typeof-number check and then poisons date math — with a finite lastFiredAt the entry reads as an overdue aged task and the retroactive-expiry warning's toISOString() throws RangeError mid-load, so one malformed persisted entry blocks durable cron startup/takeover. Validation now requires finite createdAt (and lastFiredAt when non-null), routing such entries through the existing fix-or-delete contract for corrupt files. Verified the new end-to-end test reproduces the RangeError without the fix. * refactor(scheduler): single-source the max-age contract and freeze it at Config construction Review follow-ups on #6173: - Extract normalizeRecurringMaxAge as the single owner of the 0/Infinity no-expiry contract, used by both the Config layer and the CronScheduler constructor, so the constructor's 0 handling can no longer be removed as apparent dead code. - Resolve QWEN_CODE_CRON_MAX_AGE_DAYS once at Config construction into a readonly field, honoring the setting's requiresRestart contract; mid-session env changes can no longer make the tool description, tool output, and scheduler report different expiries. The warn once-latch is now unnecessary (construction warns at most once). |
||
|
|
8de93b876b
|
feat(core): allow sub-agents to spawn nested sub-agents up to a configurable depth (#6189)
* feat(core): allow bounded nested sub-agent spawning via maxSubagentDepth Sub-agents may now spawn sub-agents up to a configurable maximum nesting depth (default 5; 1 reproduces the previous no-nesting behavior). Enforced in two layers sharing one predicate: prepareTools() hides the agent tool from leaf-depth sub-agents, and AgentTool.execute() rejects over-depth spawns as an authoritative backstop. Teammates, forks, and the workflow tool remain excluded from nesting. Launch depth is persisted in the agent meta sidecar and restored on resume (including deferred-approval continuations and in-process AgentInteractive frames) so a resumed nested agent cannot regain spawn capacity. See knowledge/qwen-code/design/nested-subagents.md. * fix(core): address review findings on nested sub-agent spawning - Deny the agent tool to workflow-spawned subagents: depth gating would otherwise re-admit it, letting a workflow leaf spawn outside the orchestrator's concurrency cap, agent accounting, and token budget. - Reject non-finite maxSubagentDepth values (JSON 1e309 parses to Infinity and would unbound the recursion cap; NaN would silently block all nesting) and cap the knob at 100 to catch typos. - Add a --max-subagent-depth CLI flag mirroring sibling budget flags, with loud validation for flag typos, and document the setting. - Log guard rejections (depth, fork containment) and silent fork-to-subagent downgrades through the agent debug logger. - Refresh stale comments (depthOverride resume pinning, depth-gated AgentTool exclusion) and drop references to a design doc that lives outside the repository. - Fill review-noted test gaps: nesting predicate primitives, fork-context prepareTools, persisted-depth restoration on background resume, nested AgentInteractive depth pinning, nested fork fallback, and the blocked-spawn returnDisplay shape. * fix(cli): add maxSubagentDepth to the CliArgs test literal The exhaustive CliArgs mock in gemini.test.tsx missed the new field, failing CI's clean tsc build (the local incremental build skipped the test file). * fix(core): add a teammate backstop to the agent spawn guards execute() backstopped depth and fork containment but not the teammate exclusion, so its guards covered less than prepareTools() gates. A teammate spawn call that slipped past schema-hiding would have nested. Block it symmetrically with the fork guard, log the rejection, and pin the behavior in a test. * fix(core): normalize persisted maxSubagentDepth on resume The resume path trusted the raw sidecar value, bypassing the Config clamp — a tampered or malformed sidecar (1e309 parses to Infinity; JSON.stringify turns Infinity into null) would remove the nesting cap for resumed agents. Extract the clamp into a shared normalizeMaxSubagentDepth used by both the Config constructor and the flag-restore path, and refresh the stale settings schema description (clamp range, non-finite fallback, workflow-agent wording). * test(core): pin null-to-default normalization of resumed maxSubagentDepth JSON.stringify(Infinity) === 'null', so a sidecar can legitimately carry null; widen the persisted flag type to admit it and parameterize the resume test over both the clamp (5000 -> 100) and the null fallback (null -> 5). * fix(core): harden nesting depth edges from final review pass - Normalize persisted meta.depth on resume: the sidecar is untrusted JSON, and a tampered negative depth (or -1e309 → -Infinity) would pin the resumed frame below zero and pass canSpawnNestedAgent for every cap. Invalid values fail closed to the depth ceiling — the agent keeps running but cannot spawn. - Register monitor notification routing for in-process interactive agents: framing runLoop() made their monitors agent-owned, and owned dispatch has no session fallback, so notifications were silently dropped. InProcessBackend now routes them into the agent's message queue and tears the routing down on release. - Downgrade background spawn requests from nested launchers to awaited foreground runs: a nested launcher cannot honor the background completion contract (send_message/task_stop excluded, notifications session-scoped), which orphaned the child's results. - Extract spawnBlockReason() as the single spawn-exclusion policy shared by prepareTools() and execute(), replacing two hand-kept copies of the depth/teammate/fork rules. - Share DEFAULT_MAX_SUBAGENT_DEPTH / MAX_SUBAGENT_DEPTH_LIMIT across the core normalizer, the CLI flag validator, and the settings schema. - Log dropped teammate names from nested spawns; revert the impossible |null persisted-flag widening to an honest tampered-sidecar framing; document the constructor-time depth capture invariant. * test(cli): add DEFAULT_MAX_SUBAGENT_DEPTH to core package mocks settingsSchema.ts now imports the shared constant, so CLI tests that mock @qwen-code/qwen-code-core with an explicit export list need the new export. * feat(cli): display nested sub-agents as a tree in the TUI (#6191) * feat(core): allow bounded nested sub-agent spawning via maxSubagentDepth Sub-agents may now spawn sub-agents up to a configurable maximum nesting depth (default 5; 1 reproduces the previous no-nesting behavior). Enforced in two layers sharing one predicate: prepareTools() hides the agent tool from leaf-depth sub-agents, and AgentTool.execute() rejects over-depth spawns as an authoritative backstop. Teammates, forks, and the workflow tool remain excluded from nesting. Launch depth is persisted in the agent meta sidecar and restored on resume (including deferred-approval continuations and in-process AgentInteractive frames) so a resumed nested agent cannot regain spawn capacity. See knowledge/qwen-code/design/nested-subagents.md. * feat(cli): display nested sub-agents as a tree in the TUI Render nested agents depth-first with indent + dim '↳' in the live agent panel and background tasks view; promote orphaned children to root with a '· from <parent>' annotation. Detail view gains a level badge, Parent breadcrumb, and Sub-agents section. The [blocking] tag and two-step cancel confirm now apply only to provably user-blocking foreground chains. Parent completion summaries carry a '· N sub-agents' tail (guard-rejected spawns now record as failed tool calls so the count stays honest). Also fixes the live-panel Enter-for-detail order mismatch by sharing one display order between the panel render and the composer keyboard mapping. * fix(core): address round-1 review on nested sub-agent spawning - Derive launch metadata (hooks, spans, task rows, meta sidecar) from the resolved subagent config instead of the raw requested type, so a fork request that falls back to the awaitable path no longer reports "fork". - Pin the blocked-spawn failure contract in tests: error is set and returnDisplay.status is 'failed' for both the depth and fork guards; also document the failure-path routing at buildSpawnBlockedResult. - Drop source-comment references to private knowledge/ design docs that do not exist in this repository. * test: address round-2 review on sub-agent counting and fork fallback - Exercise the legacy 'task' alias in the scrollback sub-agent count so the migration-aware name set is covered, not just the canonical name. - Pin the nested-fork downgrade: a sub-agent requesting a fork falls back to the awaitable general-purpose subagent even in interactive mode. - Drop a duplicated 'nesting depth guard' describe block left behind by the automated base-branch merge (kept the copy with the failure-shape assertions). * fix(core): keep actionable guidance in blocked-spawn error messages The scheduler's failure path sends only error.message to the model and the scrollback, discarding llmContent. With the terse terminateReason as the message, a blocked spawn lost its "do the task yourself instead" instruction, inviting retry loops. Carry the full guidance text in error.message and keep terminateReason for the display card. * test(cli): pin the tree indent clamp at depth beyond TREE_INDENT_MAX_LEVELS Maintainer mutation-testing on the PR found that removing the clamp in treeRowPrefix survived the suite. Assert a depth-4 row indents 3 levels (12 spaces), plus the base marker/indent behavior. --------- 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-bot@users.noreply.github.com> |
||
|
|
dc8e155927
|
docs: correct stale CLI flags/keybinding and document model.reasoningEffort (#6219)
- Remove nonexistent --all-files/-a and --show-memory-usage flags from the CLI arguments and headless option tables (no longer defined in the yargs parser in packages/cli/src/config/config.ts). - Add the commonly-needed --model/-m flag to the headless options table and fix the --approval-mode example to use the valid choice auto-edit (the parser rejects the underscore form auto_edit). - Drop the stale Meta+Enter alias from the external-editor shortcut; that chord is bound to NEWLINE, while OPEN_EXTERNAL_EDITOR binds only Ctrl+X. - Document the model.reasoningEffort setting (set via /effort), which is exposed in the settings dialog but was missing from the settings reference. Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
ca61d7827e
|
feat(channels): add identity and task lifecycle metadata (#6105)
* chore: ignore local worktrees * docs(channels): design identity and task lifecycle p0 * docs(channels): plan identity and task lifecycle p0 * feat(channels): add identity and task lifecycle metadata * fix(channels): suppress cancelled tool call lifecycle * fix(channels): cover loop lifecycle metadata * fix(channels): harden lifecycle event edges * fix(channels): finalize cancelled lifecycle before cleanup * fix(channels): address lifecycle review suggestions * fix(channels): close lifecycle cancellation races * fix(channels): separate pending cancel state * fix(channels): order clear cancellation lifecycle * fix(channels): suppress loop chunks during pending cancel * test(channels): use active session in cancel regression * fix(channels): sanitize lifecycle tool fields * fix(channels): route shared tool call lifecycle * fix(channels): preserve pending cancel intent * fix(channels): preserve responses after failed cancel * fix(channels): tighten lifecycle cancellation reasons * fix(channels): close lifecycle cancel races and validate identity config Address the outstanding review findings on #6105: - carry a typed reason on ChannelLoopSkippedError and report disabled loops as 'dropped' instead of 'timeout' - treat a turn as committed once delivery starts: /cancel re-checks deliveryStarted after the cancel RPC settles, and neither prompt path lets a late-settling cancel rewrite a delivered turn into cancelled (or follow a /clear cancellation with completed) - tag failed lifecycle events with phase: agent vs delivery - validate identity/memoryScope shape at config parse time instead of throwing an opaque TypeError on the first prompt of every session - guard onPromptStart hooks, pass job.id to loop onPromptStart/End, append the boundary block after operator instructions, gate /who + /status identity lines on configured identity/memoryScope, cache the boundary prompt, and route error logs through lifecycleError() Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(channels): keep failed terminal event when cancel settles post-delivery Self-review follow-up: once delivery started, the catch paths no longer reconcile a pending cancel — a late-resolving cancel RPC used to flip cancelled=true there, suppressing the failed emit while the /cancel handler (seeing deliveryStarted) also declined to emit, leaving a started task with no terminal lifecycle event at all. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(channels): hold streamed chunks while a cancel is pending Chunks arriving while a /cancel RPC is in flight were pushed straight into the BlockStreamer, which can send a block on a size/paragraph threshold before the cancel resolves — leaking output a successful cancel can never recall. Hold the pending-window chunks instead: replay them (block streaming + text_chunk transcript) when the cancel fails, discard them when it succeeds. onResponseChunk stays live through the window so adapter-accumulated display state has no permanent hole on a failed cancel; adapters gate visible updates on their own stop flags. Also stop passing the loop job id to onPromptStart/onPromptEnd (and the /clear eviction path): the hook contract is inbound platform message ids, and adapters act on them — cards and reactions keyed to a fake id. Lifecycle events still carry job.id for correlation. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(channels): defer adapter chunks while cancel is pending --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
55e425c1d2
|
docs: document the /config slash command (#6145)
Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
2c95d9383f
|
feat(core,cli): unified reasoning effort with /effort command (#6072)
* feat(core,cli): unified reasoning effort with /effort command
Expose a provider-agnostic reasoning-effort ladder (low/medium/high/xhigh/max) through a new /effort slash command and a global model.reasoningEffort setting. Bare /effort opens an interactive tier picker (and lists tiers in non-interactive/ACP mode); /effort <tier> sets one directly. A single canonical reasoning.effort config is translated and clamped per provider so one control works everywhere:
- OpenAI: reasoning_effort (max clamps to xhigh)
- DeepSeek: flat reasoning_effort (low/medium→high, xhigh→max)
- GLM / z.ai: new adapter flattens nested reasoning.effort to a verbatim reasoning_effort
- Anthropic: output_config.effort, gated per model — Opus 4.7/4.8 and 5.x families pass xhigh/max through; Opus 4.6/Sonnet 4.6 take max only; older clamp to high
- Gemini: thinking_level (adds medium; xhigh/max cap at HIGH)
- Qwen / DashScope: a set effort turns on enable_thinking (no per-tier field yet — the single column to extend when qwen ships one)
Config.setReasoningEffort updates the live config so the change takes effect on the next turn without an auth refresh, and is re-applied across model switches and auth refreshes (including the initial one at startup) so the effort survives /model switches and CLI restarts — even for provider-backed models, whose preset sync would otherwise overwrite reasoning and drop the persisted effort. The model-with-reasoning status line now tracks the effort and updates immediately when /effort changes it.
A new core/reasoning-effort.ts holds the canonical tier list, rank-based clamp, and input normalizer shared across providers and the command.
* fix(core): reject date suffix as Anthropic minor version for effort gating
The version regex parsed the 8-digit date suffix of dated model ids
(e.g. claude-opus-4-20250514 = Opus 4.0) as the minor version, so
atLeast(4, 6)/atLeast(4, 7) returned true and Opus 4.0 was wrongly
granted the xhigh/max effort tiers it does not support, producing an
HTTP 400 on /effort max with no clamp warning.
Cap the minor-version group at one or two digits with a trailing (?!\d)
so a date suffix is not mis-parsed (a bare {1,2} cap is insufficient: it
would still greedily capture '20' from '20250514'). Dated ids that do
carry a minor, like claude-opus-4-7-20251101, still resolve to minor 7.
Adds a regression test asserting effort 'max' clamps to 'high' on
claude-opus-4-20250514.
* fix(i18n): translate /effort command description for all locales
The mustTranslateKeys strict-parity test failed because the new /effort
command description fell back to English in zh and zh-TW. Add the
description string to every locale file so the command is translated like
its siblings and strict-parity locales no longer fall back.
* fix(core): correct DeepSeek effort mapping and adaptive-thinking model parse
Address two review findings on the reasoning-effort path in
anthropicContentGenerator:
- DeepSeek anthropic-compatible output_config.effort accepts only high/max,
but resolveEffectiveEffort passed low/medium through verbatim (only
remapping xhigh -> max), which the endpoint would 400 on. Mirror the
DeepSeek OpenAI adapter (deepseek.ts): low/medium lift to high, xhigh/max
group to max.
- modelSupportsAdaptiveThinking used /claude-(?:opus|sonnet|haiku)-(\d+)-(\d+)/,
so a dated id like claude-opus-4-20250514 (Opus 4.0) parsed the 8-digit date
suffix as the minor version (>= 6), wrongly selecting adaptive thinking and
tripping a server 400. Apply the same date-suffix-guarded regex the sibling
anthropicSupportedEffortTiers already uses, also covering the fable/mythos
families and a bare major (claude-opus-5).
Add regression tests for both.
* fix(core,cli): address /effort review feedback
- anthropic: family-guard the `max` tier on the 4.x branch so haiku 4.x no
longer gains `max` (a server 400); 5.x families still get it via major>=5.
- effort command: when `setReasoningEffort` no-ops because thinking is disabled
(reasoning: false), report that the tier won't take effect yet instead of a
misleading success message (the tier is still persisted for future sessions).
- dashscope: drop the pipeline-injected nested `reasoning` object when
`enable_thinking` is set, so qwen never receives two competing thinking knobs
(mirrors deepseek.ts / zai.ts).
Adds regression tests for each.
* fix(core): collapse empty reasoning to undefined and drop redundant effort re-apply
setReasoningEffort(undefined) now resets reasoning to undefined instead of
leaving a truthy empty object, which the pipeline would emit as wire noise
and which would make 'if (cfg.reasoning)' a false positive.
handleModelChange no longer re-applies the reasoning effort after the
full-refresh path: refreshAuth already captures and restores it, so the
second call was redundant. The qwen-oauth hot-update path keeps its own
re-apply because it never routes through refreshAuth.
* fix(core): log once when Gemini clamps xhigh/max effort to HIGH
Gemini has no tier above HIGH, so /effort xhigh|max silently ran at HIGH
with no trace. Mirror the Anthropic generator's one-time clamp warning via
a per-generator latch so the downgrade is discoverable in debug logs.
* fix(core): warn when GLM model on non-Z.ai host skips reasoning_effort flatten
isZaiProvider routes any glm-* model through this provider, but the
reasoning_effort reshape stays hostname-gated. A self-hosted GLM on a
non-Z.ai hostname therefore kept its nested reasoning.effort unflattened
silently. Keep the reshape hostname-gated (so we never push GLM's flat
field at an arbitrary OpenAI-compatible backend) but log the skip once so
the gap is discoverable.
* docs(core,cli): correct false OpenAI 'max'->'xhigh' clamp claim
The reasoning-effort docs claimed OpenAI adapters clamp 'max' to 'xhigh',
but the default OpenAI-compatible pipeline forwards the tier verbatim and
no such clamp exists. Reword the doc comment and the reasoningEffort
setting description (and the regenerated VS Code schema) to match.
* fix(cli): clarify effort confirmation and improve the effort dialog UX
- The /effort confirmation now states the tier is the requested one and the
effective tier depends on the active provider/model, instead of implying
the shown tier is what reaches the wire (providers clamp per model).
- EffortDialog no longer pre-selects 'high' when no effort is configured;
the cursor starts at the top and a footer notes the provider default is
in effect, so the highlight is not mistaken for the current setting.
- The dialog path now mirrors the slash-command read-back: when thinking is
disabled (setReasoningEffort is a no-op), it surfaces an info message that
the tier is persisted but won't take effect until thinking is re-enabled.
* refactor(core): extract shared parseClaudeModelVersion for Claude effort gating
anthropicSupportedEffortTiers and modelSupportsAdaptiveThinking each parsed
Claude model ids with their own near-identical regex (family list +
date-suffix guard), kept in sync only by a comment. Extract a single
parseClaudeModelVersion helper both consume so adding a new family is a
one-line change with no drift risk between effort tiers and the thinking
shape.
* fix(anthropic): drop manual budget_tokens on models that reject it
Opus 4.7+ and every 5.x family (Sonnet 5, Fable 5, Mythos 5) dropped
manual extended thinking: a request with
thinking:{type:'enabled', budget_tokens:N} returns a 400 on those models,
which require adaptive thinking with output_config.effort instead
(https://platform.claude.com/docs/en/build-with-claude/effort).
buildThinkingConfig honored an explicit reasoning.budget_tokens on every
model, so a preset carrying budget_tokens (with or without /effort) shipped
the manual shape to an adaptive-only model and 400'd. Gate the escape hatch
behind a new modelRejectsManualThinking() predicate so the budget is only
emitted for models that still accept it (Opus 4.5/4.6, Sonnet 4.6, older
4.x, and unknown/unversioned ids); adaptive-only models fall through to
{type:'adaptive'} and let output_config.effort govern thinking depth.
* fix(cli,core): warn on invalid model.reasoningEffort; guard hot-update field set
- modelConfigUtils: surface a startup warning when settings.model.reasoningEffort
holds an unrecognized value (normalizes to undefined and was silently skipped),
so a typo in settings.json doesn't leave /effort with no visible effect.
- config: document that the qwen-oauth hot-update field set deliberately excludes
`reasoning`, which is captured and re-applied via setReasoningEffort() — adding
it to the copied set would mask the effort restore across model switches.
* fix(core,cli): address /effort review feedback
- config: re-apply reasoning effort on the full-refresh model-switch path.
switchModel() wipes modelsConfig.reasoning via applyResolvedModelDefaults
before handleModelChange fires, so refreshAuth's own capture reads undefined;
restore the tier from the live config. Add a regression test.
- gemini: make buildThinkingConfig's effort switch exhaustive (explicit
'undefined' case + never-guarded default) so a new tier is a compile error
rather than a silent map to UNSPECIFIED.
- anthropic: emit a one-time warning when budget_tokens is dropped on models
that reject manual thinking (Opus 4.7+/5.x); reword the version-regex comment
to correctly credit the (?!\\d) lookahead. Add reseller-prefixed and 5.x
effort-gating tests.
- cli/effort: confirm the requested tier in-chat on dialog success (mirror the
slash-command message); add hook tests.
- tests: add dashscope vision-branch effort test and determineProvider z.ai
routing tests.
* fix(core): harden reasoning-effort normalize and correct stale clamp comment
- normalizeReasoningEffort: reject non-string input (e.g. a hand-edited
settings.json with a boolean/number reasoningEffort) instead of calling
.trim() on it and crashing at startup; add regression tests.
- contentGenerator: update the reasoning-effort ladder comment to reflect
the per-model Anthropic tier gating (Opus 4.7+/5.x accept xhigh/max,
Opus/Sonnet 4.6 accept max, older models cap at high) instead of the
outdated blanket 'xhigh/max -> high' description.
Addresses PR review feedback.
* fix: remove stray OpenClaw-Query-Submit gitlink
This branch carries a submodule gitlink (mode 160000, commit be66572)
with no .gitmodules entry. It entered via a merge from an older main
state; current main no longer contains it, so this branch would
re-introduce it on merge, breaking clone --recursive and any submodule
init. Remove it from the index.
* test(cli): add EffortDialog render tests
Adds render/behavior tests for EffortDialog mirroring
ApprovalModeDialog.test.tsx: renders the title and all five tiers,
shows/hides the 'no effort configured' hint based on currentEffort,
and verifies the active Escape handler cancels with undefined while
non-Escape keys do not.
* refactor(anthropic): single-source Claude family list; warn on DeepSeek effort remap
Derive the ClaudeModelFamily union and the model-id regex from one
CLAUDE_MODEL_FAMILIES const so the type and the parser can't drift apart
behind the 'as ClaudeModelFamily' cast.
Emit a one-time debugLogger.warn when the DeepSeek anthropic-compatible
branch remaps a requested effort tier (low/medium -> high, xhigh -> max),
mirroring the real-Anthropic clamp path so a silently upgraded effort is
visible in debug logs.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: DragonnZhang <dragonzhang1024@gmail.com>
|
||
|
|
bb7b4d8d57
|
feat(serve): support HTTPS/TLS via --tls-cert and --tls-key flags (#6032)
* feat(serve): support HTTPS/TLS via --tls-cert and --tls-key Serve `qwen serve` over HTTPS when both `--tls-cert` and `--tls-key` point at PEM files, instead of plain HTTP. The motivation is mobile / cross-device access: a LAN IP (`192.168.x.x`) is not a browser secure context over `http://`, so `getUserMedia` (voice input), WebRTC, and other secure-context-only APIs are blocked on phones/tablets. Bringing your own cert (e.g. via mkcert) unlocks them. Implementation wraps the existing Express app in `https.createServer` when TLS is configured; `https.Server extends http.Server`, so the connection cap, address lookup, ACP attach, and graceful-close paths are unchanged. The plain-HTTP path stays bit-for-bit identical when no certs are given. The startup banner and loopback same-origin set now reflect the active scheme. Scope is TLS termination only — no auto-generation, no ACME. TLS is orthogonal to the bearer-token gate: non-loopback binds still require a token. Boot fails loudly if only one of the two flags is given, or if a cert/key file can't be read, rather than silently downgrading to HTTP. Closes #6001 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(serve): add HTTPS loopback origins to self-origin and ACP checks * fix(serve): accept port-less Origin header on default ports 80/443 Per RFC 7230 §5.4, browsers omit the port in the Origin header when it matches the scheme default (http→80, https→443). The origin checks in self-origin.ts and acp-http/index.ts always included the port suffix, so a browser on port 443 sending 'Origin: https://localhost' (no :443) would fail the loopback origin match. Add port-less origin entries when the server listens on port 80 or 443, mirroring the pattern already used in auth.ts hostAllowlist. * fix(serve): add port-less origins to installSameOriginOriginStrip for RFC 7230 compliance * fix(serve): validate TLS cert expiry and parse errors at boot https.createServer starts cleanly with an expired certificate, then every client handshake is rejected (NET::ERR_CERT_DATE_INVALID) while /health stays green — a silent outage that's hard to diagnose. Parse the cert with X509Certificate at boot and fail loud with an actionable message when it's expired or unparseable. Also wrap createServer so a cert/key mismatch surfaces a framed error instead of a raw OpenSSL string. Add tests for the expired-cert guard and the previously-uncovered --tls-key read error path. Co-Authored-By: Qwen-Coder <noreply@qwen.ai> * fix(serve): close TLS validation gaps from review - Reject not-yet-valid certs (notBefore > now) at boot, symmetric to the existing expiry guard — same silent NET::ERR_CERT_DATE_INVALID outage. - Add port-less host entries (default 80/443) to the WS upgrade Host allowlist, mirroring the REST allowlist (auth.ts) and the Origin checks; without it every WS upgrade is 403'd when TLS runs on port 443. - Cover the previously-untested X509 parse-error and cert/key-mismatch boot branches. - Docs: include localhost/127.0.0.1 in the mkcert TLS example so the URL --open rewrites to (127.0.0.1) isn't rejected with CN mismatch. Co-Authored-By: Qwen-Coder <noreply@qwen.ai> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Qwen-Coder <noreply@qwen.ai> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
427b5ade33
|
docs: document model/auth settings, /model --vision, and --safe-mode (#6028)
* docs: document model/auth settings, /model --vision, and --safe-mode Refresh user docs to match the current codebase: - commands.md: add the /model --vision override (vision-bridge model) - settings.md: add model.baseUrl, model.sessionTokenLimit, visionModel, and voiceModel; document the deprecated security.auth.apiKey and security.auth.baseUrl keys with a pointer to modelProviders - troubleshooting.md: document the --safe-mode flag for isolating customization issues * docs: address review feedback on sessionTokenLimit, safe-mode, deprecation notes - model.sessionTokenLimit: correct default to -1 (runtime fallback in core/config.ts) and clarify breach behavior (current send dropped, not session abort) per client.ts SessionTokenLimitExceeded handling. - --safe-mode: expand the disabled-customizations list to also cover permission rules, approval mode overrides, memory features, and sandbox settings, matching cli/config.ts. - security.auth.apiKey/baseUrl: align deprecation wording with the existing tools.* entries (**Deprecated.**) and drop the unsubstantiated '(slated for removal)' qualifier. * docs: note QWEN_CODE_SAFE_MODE env var as a safe-mode alternative Document the QWEN_CODE_SAFE_MODE=true environment variable as an alternative activation path for safe mode, for cases where the CLI cannot accept flags (verified against isSafeModeEnv in packages/core/src/utils/safe-mode.ts). * docs: clarify model.baseUrl, sessionTokenLimit=0, and safe-mode subagents - model.baseUrl: describe it as a picker-managed disambiguator, not a hand-editable override (stale values can misroute to a same-id provider). - model.sessionTokenLimit: note that 0 is treated as unlimited (same as -1), unlike model.maxToolCalls where 0 disallows all calls. - --safe-mode: include custom subagents in the list of disabled customizations (only built-in subagents load in safe mode). * docs: clarify sessionTokenLimit semantics and add --safe-mode to headless flags - settings.md: reword model.sessionTokenLimit to reflect that the gate compares the last recorded prompt token count before the next send (not a per-send preflight cap), and that the next send is dropped. - headless.md: add a --safe-mode row to the CLI flags table so the diagnostic flag is discoverable there, cross-referencing Troubleshooting. * docs: align safe-mode sandbox wording to 'sandbox settings' Safe mode passes an empty Settings object to loadSandboxConfig (packages/cli/src/config/config.ts:1793), so it strips settings-sourced sandbox config while the --sandbox flag and QWEN_SANDBOX env still apply. Match headless.md to troubleshooting.md's accurate 'sandbox settings'. * docs: correct safe-mode approval-mode wording and align both lists Safe mode only strips settings-sourced approval mode; the --yolo and --approval-mode CLI flags are evaluated before the safeMode guard (packages/cli/src/config/config.ts:1521-1528) and still take effect. Reword to 'settings-sourced approval mode overrides' and note the CLI flags in troubleshooting.md and headless.md, and make the enumerated safe-mode disable list identical (same items and order) across both. --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
487fb45510
|
feat(cli): Harden daemon-managed channel worker (#6098)
* feat(cli): harden daemon-managed channel worker Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): address channel worker review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): harden channel worker log supervision Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): redact channel worker snapshot errors Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * perf(cli): precompute channel worker log redactors Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): escalate permanent channel worker failures Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): bound oversized worker log tail discard Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6098) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
6a5ad453c8
|
[codex] Add explicit channel memory for messaging channels (#6051)
* chore: ignore local worktrees * feat(core): add channel memory store * fix(core): preserve dots in channel memory paths * fix(core): guard dot channel memory paths * feat(channels): add channel memory commands * fix(channels): defer channel memory session invalidation * feat(channels): inject channel memory into sessions * fix(channels): serialize channel memory context injection * fix(channels): harden channel memory context races * fix(channels): let queued turn retry memory context * feat(cli): wire channel memory into channel startup * docs(channels): document channel memory * fix(channels): harden channel memory handling * fix(channels): gate channel memory injection * fix(channels): serialize channel memory clears * fix(channels): tolerate channel memory read failures * fix(channels): harden channel memory review gaps * fix(channels): harden channel memory review gaps * fix(channels): protect shared channel memory * test(channels): allow group memory view test * fix(channels): block group memory writes * fix(channels): include memory in loop prompts * fix(channels): preserve session context order after merge * fix(channels): retry loop memory context after read failure --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
1a46df5d92
|
fix(cli): load browser MCP tools by default (#6006)
* fix(cli): load browser MCP tools by default * fix(cli): cover browser MCP env flags * fix: address browser MCP review follow-ups * fix(cli): add browser MCP diagnostics * fix(cli): tighten browser MCP auto-wiring * fix(cli): address browser MCP diagnostics * revert(cli): drop browser MCP diagnostic churn * revert(cli): drop optional CDP startup diagnostic * refactor(cli): load browser MCP dynamically * fix(cli): lazily attach CDP tunnel * test(cli): use repo deps for CDP tunnel acceptance * fix(cli): scope browser MCP defaults to extension origins * fix(cli): recover from lazy CDP attach failures * fix(chrome-extension): bind CDP replies to source socket * ci: allow slower actionlint runs * fix(cli): harden chrome devtools runtime MCP registration * test(cli): satisfy lint in CDP registration race test * test(cli): cover chrome devtools MCP retry loop * test(cli): cover chrome devtools skip paths * ci: restore actionlint timeout |
||
|
|
b4fe43741a
|
feat(daemon): Add session archive support (#6058)
* feat(daemon): add session archive support Add active versus archived daemon session storage, archive and unarchive APIs, strict live-session close handling, ACP and SDK support, and coverage for archive listing, load rejection, deletion, and route mapping. Closes #6057 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: fix CI failure on PR #6058 Update the no-AK integration capability baseline to include the new session_archive capability added by this PR. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6058) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): preserve live session on strict close failure Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): serialize session delete with archive transitions Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(daemon): share session archive orchestration Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): clean up strict close failures Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(daemon): clarify archive recovery tradeoffs Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): log session archive outcomes Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): parallelize archive session closes Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): serialize archive restore races Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): satisfy archive lint checks Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): keep strict close retryable Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): distinguish archive conflicts Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): warn on unreadable session heads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(core): document active-only session helpers Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): tighten archive review edges Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): allow concurrent session restores during archive gate Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): avoid archive head reads on session restore Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6058) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(sdk): account for session archive bundle size Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address archive gate review (#6058) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address archive review follow-ups (#6058) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address ACP archive review feedback (#6058) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): Address session archive review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(daemon): Cover close ownership restoration Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): Address archive review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(daemon): Cover ACP close prompt fallback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(daemon): Cover archive close channel loss Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): Address archive follow-up review Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): Return archiving conflict for delete gate Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Treat unreadable archived ids as occupied Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(daemon): Share session delete orchestration Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): Address archive review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(deps): Clear critical runtime audit failures Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(deps): Sync runtime dependency ranges with main Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
891c59fbaa
|
feat(channels): add group history backfill (#6074)
* feat(channels): add group history backfill * fix(channels): harden group history backfill * fix(channels): address group history review gaps * fix(channels): apply wildcard group history limit |
||
|
|
17fbaa25cd
|
refactor(review): drop deterministic-analysis and autofix steps (#6092)
* refactor(review): drop deterministic-analysis and autofix steps
Slim the bundled /review skill from 11 to 9 steps by removing Step 3
(deterministic analysis — auto-run of tsc/eslint/ruff/clippy/go vet
plus CI-lint discovery) and Step 8 (autofix — PR-worktree auto-fix,
commit and push).
Renumber the remaining steps (4→3 … 11→9) and update every
cross-reference. Agent 7 (Build & Test) stays in the parallel review
step and now always runs build+test instead of skipping when Step 3
had already compiled. The [linter]/[typecheck] source tags are dropped;
[build]/[test]/[review] remain. DESIGN.md is updated to match (step
numbers, removed Autofix/deterministic rationale, LLM-budget table).
* refactor(review): address review feedback on the /review slimming
Follow-up to the 11->9 step change, addressing PR review comments:
- Restore base-branch CI-config protection in Agent 7. The removed Step 3 carried the instruction to read CI config from the base branch; without it Agent 7 would discover build/test commands from the untrusted PR branch. Re-added to Agent 7's CI-config discovery clause.
- Drop the stale "linters" token from the worktree rule (no standalone linter step runs anymore).
- Narrow the exclusion criteria: substantive lint/type issues (unused vars, unreachable code, type errors) are no longer auto-excluded now that no deterministic tool catches them; only pure formatting stays excluded.
- Update user docs to match: docs/users/features/code-review.md (11->9 steps, remove the Deterministic Analysis and Autofix sections, drop the two comparison-table rows, renumber the Token-efficiency table) and docs/users/features/commands.md (agent count).
- Fix stale step-number references in CLI comments: cleanup.ts (Step 11->9) and presubmit.ts (Step 9->7, comment + yargs describe).
* refactor(review): remove orphaned deterministic subcommand, polish docs
Second round of PR feedback:
- Remove the now-orphaned `qwen review deterministic` subcommand. review.ts describes these subcommands as "internal helpers used by the /review skill"; with Step 3 gone the skill no longer invokes it, so the ~740-line module plus its import / registration / describe / subcommand-list entries were dead code. Deleted deterministic.ts and its wiring in review.ts.
- Decouple the exclusion criterion from pipeline state (SKILL.md): substantive lint/type issues (unused vars, unreachable code, type errors) are now "in scope — LLM agents should report them" rather than "no longer have a deterministic tool catching them", so the rule stays correct if a linter step is ever re-added.
- Drop the stale "linting" justification from the worktree dependency-install note (SKILL.md); only build/test remain.
- DESIGN.md: rename the subcommands section to "presubmit and cleanup", drop "lint" from the review-tools rejected alternative, and fix the "we already have those" cell to "We retain build/test (Agent 7)".
* refactor(review): resolve exclusion-criteria contradiction, polish wording
Third round of PR feedback:
- Merge the exclusion criteria to remove the contradiction between the unconditional "matches codebase conventions" exclude and the "substantive lint/type issues are in scope" include. Now a single bullet: cosmetic style/formatting/naming is excluded, but substantive issues a linter or type checker would flag (unused variables, unreachable code, type errors) are in scope even where the surrounding code tolerates them. Kept decoupled from pipeline state (no "deterministic tool" wording). Applied in both SKILL.md and docs/users/features/code-review.md.
- SKILL.md lightweight-mode skip: "(no local reports or cache)" to match Step 8's title ("Save review report and cache").
- DESIGN.md: rename the CI-config section to "auto-discover build/test commands" to match the body, which was narrowed to build/test only.
* test(review): guard qwen review subcommand surface, fix stale docs linting ref
- Add packages/cli/src/commands/review.test.ts verifying the `qwen review` builder registers exactly [fetch-pr, pr-context, load-rules, presubmit, cleanup], no longer registers the removed `deterministic` subcommand, and that `describe` no longer mentions deterministic analysis. Guards against silently re-adding the subcommand or dropping a helper (review.ts previously had no test).
- docs/users/features/code-review.md: drop the orphaned "linting" from the Worktree Isolation dependency-install note; only build/test remain now that Step 3 is gone.
|
||
|
|
855a2b3d69
|
feat(auto-mode): add classifyAllShell setting to route all shell commands through classifier (#6040)
* feat(auto-mode): add classifyAllShell setting to route all shell commands through classifier When autoMode.classifyAllShell is true, every shell command (including read-only ones that would normally be auto-approved) is routed through the auto-mode LLM classifier for safety review. Default false preserves existing behavior. Closes #6039 * fix(core): add classifyAllShell check to autoApproveCompatiblePendingTools The third forceAutoReviewForAllow computation site in autoApproveCompatiblePendingTools was missing the shouldClassifyAllShellForAutoMode OR-branch, allowing shell commands to bypass the classifier when classifyAllShell is enabled. Addresses qwen-code-ci-bot critical review on PR #6040. * fix(core): use optional chaining in shouldClassifyAllShellForAutoMode Prevents TypeError when config.getAutoModeSettings() returns undefined in tests or edge cases where AutoModeSettings is not initialized. * docs(auto-mode): document classifyAllShell setting Add new section explaining the classifyAllShell option, a tip in the 'How it works' overview, and a commented-out example in the approval-mode configuration reference. * fix(test): add missing getAutoModeSettings mock in AUTO denial counter reset test |
||
|
|
e324104ce8
|
docs: refresh settings, MCP glob, auth alias, and autonomous loop docs (#6090)
Audit docs/ against the current codebase and correct user-facing drift: - Document glob-pattern support (* and ?) for mcp.allowed / mcp.excluded in settings.md and the MCP feature page (feat #6012). - Add missing user-facing settings rows: general.terminalBell, general.preventSystemSleep, general.chatRecording; ui.showStatusInTitle, ui.disableWorkflowKeywordTrigger, ui.enableUserFeedback, ui.compactInline, ui.useTerminalBuffer, ui.hideBuiltinWorktreeIndicator; memory.enableTeamMemory, memory.enableTeamMemorySync; tools.toolSearch.enabled. - Note the QWEN_MODEL alias for OPENAI_MODEL in the auth protocol table. - Document the autonomous (bare /loop) mode in scheduled-tasks (feat #5991). Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
c958e6f6f9
|
feat(channel): add channel loop support (#6073)
* feat(channel): add proactive routines * fix(channel): harden scheduled routines * fix(channel): report Feishu proactive send failures * fix(channel): reject unsupported threaded routines * fix(channel): harden routine scheduler lifecycle * fix(channel): enforce routine quotas atomically * feat(channel): surface routine lifecycle * test(channel): align routine job fixtures * fix(channel): initialize routine lifecycle fields * fix(channel): harden routine scheduler concurrency * fix(channel): evict timed out scheduled sessions * fix(channel): harden routine lifecycle review gaps * feat(channel): add channel loop support * test(channel): fix loop scheduler lint * fix(channel): harden loop lifecycle checks * test(channel): fix loop scheduler lint * fix(channel): harden loop review handling * fix(channel): harden loop timeout handling * fix(channel): cap loop scheduler concurrency * fix(channel): harden loop session recovery * fix(channel): harden loop lifecycle recovery * fix(channel): harden loop recovery gaps * fix(telegram): preserve injected bot on first connect |
||
|
|
cf6323bfb5
|
feat(cli): Add daemon-managed channel worker for serve --channel (#6031)
* feat(cli): add daemon-managed channel worker * codex: address PR review feedback (#5978) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6031) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6031) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6031) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): harden serve channel worker lifecycle Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): cover channel worker edge cases Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address channel worker review followups Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): clear channel pidfile after worker exit Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address serve channel review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): harden serve channel worker review issues Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): preserve channel worker exit errors Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): harden daemon channel worker startup Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address channel worker review cleanup Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): track channel worker exit explicitly Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6031) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address daemon worker startup review (#6031) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6031) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address daemon worker disconnect review (#6031) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address serve channel review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address channel worker review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
e92fcbab46
|
fix(cli): switch TUI prefix ✦→◆ to fix glyph overflow on some terminals (#5974)
* fix(cli): replace ✦ (U+2726) with ◆ (U+25C6) and add ∵/∴ thinking icons - Replace ✦ with ◆ across all TUI components to fix East Asian Ambiguous width misalignment (string-width reports 1 but terminals render 2 columns). - Use ∵ (because) during thinking streaming, ∴ (therefore) when thinking is complete — matches the mathematical reasoning pair. Co-Authored-By: Qwen Code <noreply@alibaba-inc.com> * fix(cli): reduce STATUS_INDICATOR_WIDTH from 3 to 2 after ◆ replacement ◆ (U+25C6) is a consistent width-1 character across all terminals, so the tool status indicator no longer needs the extra column that was reserved for the ambiguous-width ✦ (U+2726). Co-Authored-By: Qwen Code <noreply@alibaba-inc.com> * fix(cli): catch missed ✦→◆ references in tests, docs, and scenarios * fix(cli): shorten tmux spinner frames from 3 to 2 chars to match STATUS_INDICATOR_WIDTH=2 TMUX_SPINNER_FRAMES changed from ['. ', '.. ', '...'] to ['· ', '··'] to prevent 1-column overflow in tmux when STATUS_INDICATOR_WIDTH was reduced from 3 to 2 after the ◆ replacement. * revert(cli): keep narrow '.' tmux spinner frames instead of ambiguous '·' '.' (U+002E) is Narrow (always 1 col), giving a guaranteed fixed-width tmux spinner. '·' (U+00B7) is East Asian Ambiguous, so on ambiguous-width=2 terminals the frames become 3/4 cols and the spinner jitters — the opposite of the "fixed-width frames" the surrounding comment promises. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen Code <noreply@alibaba-inc.com> Co-authored-by: pomelo.lcw <pomelo.lcw@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
7b9e31885b
|
feat(web-shell): add mobile sidebar drawer with session list (#6003)
* feat(web-shell): add mobile sidebar drawer with session list Replace the display:none behavior at viewport <=760px with an overlay drawer pattern. A hamburger menu button appears on mobile, tapping it slides the existing WebShellSidebar in as a fixed overlay with a semi-transparent backdrop. Selecting or creating a session auto-closes the drawer. Desktop layout (>=761px) is unaffected. Closes #6000 * fix(web-shell): address review feedback for mobile sidebar drawer - Use display:contents for desktop wrapper transparency (Critical: sidebar was hidden) - Fix z-index stacking so sidebar renders above backdrop in drawer - Force sidebar expand when mobile drawer is open (collapsed state) - Hide resizeHandle on mobile to prevent touch scroll conflicts - Reset drawer state on viewport resize via matchMedia listener - Add role=dialog, aria-modal, Escape key dismissal, body scroll lock - Add aria-expanded to hamburger button - Close drawer when opening Settings or resuming sessions * fix(web-shell): address second round of review feedback - Remove dead :global(.sidebar) selector (CSS Modules hash class names) - Fix Escape key capture-phase handler to not intercept sidebar inputs - Conditionally apply role=dialog/aria-modal only when drawer is open - Stop toggling collapsed prop on drawer open/close to preserve sidebar state - Add closeMobileDrawer() for bare /resume command path - Fix hamburger button vertical centering in empty chat state on mobile * fix(web-shell): fix stacking context and escape handler in mobile drawer * fix(web-shell): prevent iOS Safari background scroll when drawer is open * chore: remove accidentally committed .qwen-session and gitignore it The .qwen-session file is a developer-local session UUID generated by qwen serve. It was accidentally committed to the repo and should never be tracked. * fix(web-shell): address review feedback for mobile drawer - Don't preventDefault touchmove inside the drawer so the session list can scroll natively; only block scrolling on the page behind it. - Defer Escape to a pending tool/permission approval (reject) instead of closing the drawer when a prompt is visible. - Reuse isEditableTarget from utils/dom and only bail out for editable targets outside the drawer, so the drawer search input still closes on the first Escape. - Close the drawer before awaiting loadSession so it doesn't linger over the old transcript, matching the other session-switch paths. - Keep the drawer panel visible until the backdrop finishes fading out to avoid a one-frame flicker on close. Co-Authored-By: Qwen-Coder <noreply@qwen.ai> * fix(web-shell): mobile drawer ignores collapsed rail + block backdrop scroll - collapsed: a user who collapsed the desktop sidebar got a mobile drawer that still rendered as the icon rail (no session list — the whole point of the drawer). Force the expanded layout while the drawer is open. - touchmove: the allowlist matched the outer [data-mobile-drawer] wrapper, which also contains the full-screen backdrop, so a touchmove starting on the dim backdrop skipped preventDefault and let iOS Safari scroll the page behind. Exclude the backdrop so only the panel keeps native scroll. Co-Authored-By: Qwen-Coder <noreply@qwen.ai> * fix(web-shell): harden mobile drawer collapse, error path, and width cap - Hide the sidebar collapse button while the mobile drawer is open so its no-op toggle can no longer silently persist desktop collapsed state. - Close the drawer before awaiting createSession() so a failed create no longer leaves the drawer stuck open with page scroll locked. - Drop redundant width/min-width/position from .sidebar.mobileOpen and cap it with max-width:100vw so a wide persisted width can't overflow phones. Co-Authored-By: Qwen-Coder <noreply@qwen.ai> --------- Co-authored-by: pomelo-nwu <czynwu@gmail.com> Co-authored-by: Qwen-Coder <noreply@qwen.ai> |
||
|
|
a756676b21
|
fix(cli): replace all emoji with Unicode text symbols in TUI rendering (#5999)
* fix(cli): replace all emoji status icons with Unicode text symbols Comprehensive cleanup of emoji in TUI rendering paths (follow-up to #5787 / #5788). Replaces width-2 emoji with width-1 Unicode text symbols from the project's existing glyph vocabulary. ## Source changes (25 source files) Status indicators: - McpStatus: 🟢→● 🔄→◐ 🔴→● (colors preserved via Text color prop) - ideCommand: 🟢→● 🟡→◐ 🔴→● (both getIdeStatusMessage functions) - Footer: 🔒 removed (sandbox text label is self-explanatory) - Footer: ⚙→▷ (workflow active indicator) - ToolMessage: ⏳→◌ (progress + queued approval) - McpStatus: ⏳→◌ (server startup message) - LiveAgentPanel: ⏸→‖ (pause glyph) Text prefixes and labels: - StatusMessages: 🔎→◎ (vision bridge gutter prefix) - Session: 🚫→✗ (blocked notifications) - useGeminiStream: 🔎→◎ 🚫→✗ 💡→★ (prefixes and headers) - useContextualTips: 💡→★ (tip prefix) - WelcomeBackDialog: 👋🎯📋 removed (text labels are self-explanatory) - CreationSummary: ✅→✓ ❌→✗ - summaryCommand: ❌→✗ - AppContainer: ❌→✗ - AgentEditStep: ❌→✗ - useAutoAcceptIndicator: ℹ️→i - McpStatus Tips: 💡→★ Core package: - agent-statistics: 📋→▸ 🔧→● ⏱️→(stripped) 🔁→● 🔢→● ✅→✓ 🚀→● 💡→★ - shell: 🤖 removed from PR attribution footer - artifact-tool: 📄 removed from publish display - coreToolScheduler: ⚠️→⚠ (strip VS16) Standardized ⚠️ (U+26A0+VS16, width-2) → ⚠ (U+26A0, width-1): - CommandFormatMigrationNudge, command-migration-tool, useGeminiStream i18n: all 9 locale files updated for changed keys. ## Test changes (5 test files) - ideCommand.test.ts: 5 emoji assertions updated - McpStatus.test.tsx: 13 snapshots updated - HistoryItemDisplay.test.tsx: 🔎→◎ assertions - useGeminiStream.test.tsx: VS16 + 🔄 assertions - agent-statistics.test.ts: all emoji assertions updated * fix(cli): address review feedback — fix docs, tests, and glyph issues - Fix McpStatus test snapshots to actually exercise connecting/disconnected states via serverStatus prop instead of ineffective vi.spyOn mock - Update user docs (status-line.md, ide-integration.md) to match new symbols - Change Footer workflow indicator from ⚙ to ▷ for consistency - Fix Duration line leading space in agent-statistics formatCompact/formatDetailed - Revert ‖ (U+2016, EAW=Ambiguous) back to ⏸ (EAW=Narrow) for CJK terminals - Restore 🤖 in PR attribution (GitHub web UI, not TUI) - Add old emoji to lspCommand.test.ts negative assertion - Tighten Footer test to assert full ▷ glyph text * fix(cli): sync JSDoc with code for shell attribution and regenerate McpStatus snapshot * fix(cli): distinct IDE status glyphs + mark LLM-facing retry directives - ideCommand: Connected and Disconnected both rendered an identical bullet differing only by color, indistinguishable under deuteranopia/protanopia, in NO_COLOR terminals, or when copy-pasted. Use the PR's established ✓/✗ vocabulary (✓ Connected, ✗ Disconnected); Connecting keeps ◐. - coreToolScheduler: note that the ⚠ in the two RETRY_LOOP directives is an LLM-facing prompt string, not a TUI glyph, so it isn't 'fixed' for width later. Co-Authored-By: Qwen-Coder <noreply@qwen.ai> --------- Co-authored-by: pomelo.lcw <pomelo.lcw@alibaba-inc.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen-Coder <noreply@qwen.ai> |
||
|
|
f3694dde67
|
feat(ui): add ui.history.collapsePreviewCount to show last N turns when resuming collapsed sessions (#5848)
* feat: add ui.history.collapsePreviewCount to show last N turns on resume * chore: regenerate settings schema for collapsePreviewCount --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
c302e3ec29
|
feat(daemon,sdk): resumable /acp session stream (Last-Event-ID) + opt-in SDK transports export (#5852)
* fix(daemon): resume /acp session stream via Last-Event-ID (recover mid-turn content)
The `/acp` Streamable-HTTP session event stream was live-only: it emitted no
SSE `id:` sequence and ignored a `Last-Event-ID` reconnect header. When a
control-plane proxy idle-closed the long-lived SSE mid-turn, every content
frame the daemon produced during the gap (`session/update` carrying
agent_thought_chunk / agent_message_chunk) was lost — the turn still settled,
so the UI showed "done" with an empty/truncated body, and only a re-send
recovered it (tracked as §1.8 in the integration notes).
The replay engine already exists and is battle-tested on the REST surface:
EventBus assigns a monotonic per-session `id`, keeps a bounded ring, and
`subscribeEvents({ lastEventId })` replays `id > lastEventId` before live
events flow. This wires the `/acp` transport to it — no eventBus/bridge change.
- transport-stream / sse-stream / ws-stream: `send(message, id?)`. SSE emits an
`id:` line when `id` is present (mirrors REST `formatSseFrame`); WS ignores it
(stateful, no replay).
- connection-registry: `sendSession(…, id?)` threads the cursor; the pre-attach
session buffer stores `{ frame, id? }` so a buffered frame keeps its `id:`.
- dispatch: `translateEvent` passes `event.id` for bus events; `pumpSessionEvents`
forwards `lastEventId` to `subscribeEvents`.
- index: the `GET /acp` session branch reads `Last-Event-ID` (strict
decimal-only parse, same rule as REST) and passes it to the pump.
Bus-originated frames (session/update, request_permission, daemon notifies)
carry an `id:`; JSON-RPC responses and synthetic terminal frames do not, so
they don't burn a slot in the resume sequence. Backward compatible: clients
that send no `Last-Event-ID` get live-only behaviour as before, and `id:`
lines are inert for clients that ignore them.
Design: docs/design/daemon-acp-http/sse-resumable-stream.md
* fix(daemon): make /acp resume actually engage — session-stream grace/reclaim + replay guards
Addresses three review Criticals on the §1.8 plumbing: on its own the
`id:`/`Last-Event-ID` wiring never fired in the real close-then-reconnect flow,
and once it does fire two replay-correctness gaps become reachable.
1. Session-stream grace/reclaim (the core fix). A transport-level session-stream
close used to run the FULL `closeSessionStream` teardown — removing
ownership, aborting the in-flight prompt, detaching the bridge client. In the
real EventSource/proxy order (old socket closes first, then reconnect) that
meant the reconnect carrying `Last-Event-ID` was rejected 403 before the
cursor was read, and the prompt was already aborted — so replay had nothing
to resume. Now a transport close DETACHES (`detachSessionStream`): it stops
only the stream + subscription and keeps the binding, ownership, prompt, and
bridge-client alive for a grace window (`SESSION_GRACE_MS`, mirrors
`CONN_GRACE_MS`). A reconnect within the window reclaims (clears the timer);
otherwise the grace timer runs the full teardown, bounding runaway cost. Full
teardown stays immediate for explicit `session/close` and connection destroy.
The GET handler branches on `stream.isClosed` (transport close → grace;
pump-ended-while-open → full close).
2. No double-delivery (buffer ↔ ring overlap). `attachSessionStream` records the
max bus id flushed from the pre-attach buffer; the GET handler advances the
replay cursor to `max(Last-Event-ID, lastFlushedEventId)` so the ring replay
doesn't re-emit an already-flushed frame.
3. Idempotent `permission_request` under replay. `translateEvent` reuses the
existing `conn.pending` entry for a `bridgeRequestId` (re-sends the same
outbound id) instead of minting a second id+entry — no orphan pending, no
duplicate prompt on a ring-replayed permission.
Also: extract `parseLastEventId` to a shared `serve/sse-last-event-id.ts` used
by both REST and `/acp` (no drift; logs the rejected value); log `lastEventId`
in the pump error.
Tests: real close-then-reconnect order (200 not 403 + prompt not aborted);
overflow Last-Event-ID; replayed permission reuses pending id; registry
grace/reclaim + buffer-flush-preserves-id. Full acp-http suite green (216).
* feat(sdk): expose ACP transports via opt-in ./daemon/transports subpath
The resumable ACP-over-HTTP transport (AcpHttpTransport, native
supportsReplay + Last-Event-ID) and the negotiateTransport factory were
reachable only from source paths inside the monorepo — the published
`@qwen-code/sdk/daemon` barrel intentionally omits them to keep its
budget-checked browser bundle lean, so external consumers (agent-web)
had no import path short of forking.
Add a separate opt-in subpath `@qwen-code/sdk/daemon/transports` that
ships AcpHttpTransport / AcpWsTransport / AutoReconnectTransport /
RestSseTransport / negotiateTransport as their own browser+node bundle.
The default `./daemon` barrel and its byte budget are unchanged, so
REST-only consumers stay tree-shaken and pay nothing for the transports.
Also add a `fetchFn` option to NegotiateTransportOptions so callers can
inject auth/proxy/test fetch instead of the hardcoded global.
- build.js: emit dist/daemon/transports.{js,cjs}; reuse the node-builtin
guard for the new browser bundle (no size budget — it legitimately
ships the transports) while keeping the default barrel's budget check.
- daemon/index.ts: update the rationale comment to point at the subpath.
- daemon-transports-surface.test.ts: lock the runtime + type surface and
the package.json exports entry.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon): resume cursor must not skip in-flight-lost frames
Round-3 review (qwen-code-ci-bot) flagged a silent-frame-loss Critical in
the §1.8 resume path I added: `resumeCursor = max(Last-Event-ID,
lastFlushedEventId)` advances the ring-replay cursor past the buffer, but a
frame sent to the now-dead socket yet never received by the client has a bus
id BELOW the buffer's ids and ABOVE the client's cursor — so the max() skips
it and the ring replay never re-emits it. Exactly the proxy idle-close
mid-turn frame §1.8 is meant to recover.
Fix without trading loss for duplicates: a buffered bus event is ALSO in the
EventBus ring (it was published there to get its id), so the ring replay
started at the client's cursor is the single delivery path for every bus
event after the cursor. `attachSessionStream` now takes the resume cursor and,
when resuming, does NOT flush id-bearing buffered frames — the ring owns them,
delivering each exactly once including the in-flight-lost frame. Id-less
frames (JSON-RPC replies via `replySession`, not ring events) are still
flushed — their only delivery path. The GET handler sets
`resumeCursor = lastEventId` verbatim; `lastFlushedEventId` is removed.
Also from the same review:
- sse-last-event-id `safeLogValue`: strip ALL C0 control chars + DEL (not just
CR/LF) so a crafted `Last-Event-ID` can't smuggle ANSI ESC / null bytes onto
an operator's terminal via stderr.
- ws-stream: regression test asserting `send(msg, id)` keeps the WS wire frame
bare JSON (no SSE `id:` framing leak).
- connection-registry: resume-path test (id-bearing frames skipped, id-less
reply still flushed); design doc updated.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* refactor(daemon): inline resumeCursor alias to lastEventId
Review nit (yiliang114): after the prior commit dropped the `max()` logic,
`resumeCursor` is a pure alias for `lastEventId`. Use `lastEventId` directly in
the `pumpSessionEvents` call and the error log; drop the alias.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* docs(daemon): refresh stale resume comments + add gap-delivery test
Round-4 review (qwen-code-ci-bot, against the now-corrected resume model):
- connection-registry: the attachSessionStream CONTRACT comment still cited a
`promptAbort?.abort()` call in the index.ts onClose handler that an earlier
commit removed. Rewrite it to describe the current model — each stream's pump
has its own abort controller and teardown is identity-guarded in
`onPumpSettled`, so installing the new stream first makes the old stream
settle into detach-with-grace rather than tearing down the in-flight prompt.
- dispatch: the stream_error frame comment ("no bus id, so no SSE id: line")
contradicted the code passing `event.id`. Make it truthful: pass the cursor
through if present; a synthetic terminal frame has no id so none is written.
- connection-registry.test: add the explicit detach → produce gap events →
reattach → flush-exactly-once test (the PR's core value prop at the registry
layer), incl. a second reattach asserting the buffer drained.
The two Criticals in the same review referenced `resumeCursor` /
`lastFlushedEventId` / `Math.max`, all removed in prior commits — obsolete
against current code (answered + resolved on the threads).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon): preserve stream order on resume + harden grace/permission paths
Round-5 review (wenshao + qwen-code-ci-bot):
- [Critical, wenshao] Out-of-order completion on resume. attachSessionStream
flushed id-less buffered JSON-RPC replies (e.g. a session/prompt result that
landed during the detach gap) immediately — ahead of the ring replay that
redelivers the content chunks preceding them, so a client could see "prompt
complete" before the body (the truncated-body failure §1.8 fixes). Now on
resume those id-less frames are DEFERRED in the buffer; the event pump
releases them via flushBufferedSessionFrames once the replay boundary
(replay_complete / state_resync_required) passes, preserving original order.
Fresh connects (no cursor, no replay) still flush the whole buffer in order.
- [Critical, ci-bot] Permission auto-denied during the reconnect grace window:
a permission_request arriving while binding.stream is detached cancel-denies,
so a client reconnecting within grace can't vote. The structural fix (defer
the vote across grace) belongs with the §1.7 permission-coordination
follow-up; here, log an operator breadcrumb when it fires during grace, and
document the synchronous-translateEvent INVARIANT the direct binding.stream
.send relies on.
- [ci-bot] Stale-stream detach is now tested (reclaim installs s2; a late s1
close is a no-op — no teardown, no grace re-arm). Grace-expiry teardown now
logs a breadcrumb so a vanished session is distinguishable from explicit
close. TS4111: bracket-access the index-signature exports entry in the SDK
surface test. transports browser bundle now has a size budget
(MAX_TRANSPORTS_BROWSER_BUNDLE_BYTES = 48KB; current ~29KB).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): route session-scoped /acp responses so prompts don't hang
[Critical, wenshao] The published AcpHttpTransport could hang real
session/prompt + config requests. Its subscribeEvents() opened REST
GET /session/:id/events and only sendRequest's connection-scoped stream
resolved responses — but the daemon's replySession() routes session-scoped
JSON-RPC replies onto the session-scoped /acp stream, which the transport
never read. So a session/prompt reply was never observed → the pending
request never settled.
Switch subscribeEvents to the session-scoped /acp stream (GET /acp +
Acp-Session-Id) — the resumable §1.8 stream the daemon puts session replies
on — and dispatch each raw JSON-RPC frame by shape:
- response (id, no method) → resolve the shared pending map (the fix)
- notification (method, no id) → DaemonEvent via denormalizeAcpNotification,
stamped with the real bus id from the SSE
`id:` line (the synthetic denormalizer id is
not resume-compatible; supportsReplay=true
now tracks the authoritative cursor)
- session/request_permission → surfaced as a permission_request event so
consumers still see prompts (responding to
the vote is the §1.7 follow-up)
The connection-scoped stream still carries replies to connection-level
requests (initialize, session/new). Adds 4 subscribeEvents unit tests
(stream selection + headers, notification→event+busId, response consumed-not-
yielded, permission surfaced). Full SDK suite green (1062).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk,daemon): harden /acp SSE parser + grace/flush observability
Round-6 review (qwen-code-ci-bot), all additive / no behavioural side effects:
- [Critical] Unbounded SSE buffer in the new AcpHttpTransport session-stream
parser → OOM (tab crash for browser consumers). Add a 16 MiB cap mirroring
parseSseStream's MAX_BUF_CHARS, and reuse parseSseStream's CRLF-aware
`consumeFrames` splitter (now exported) instead of an inline `\n\n` scan —
closing the CRLF, multi-line `data:` join, and trailing-CR gaps in one go.
- Deferred-flush ordering race: the pump's post-loop safety flushDeferred()
now runs only on a non-aborted exit. An abort means the stream was
detached/reclaimed; flushing there could drain the deferred reply onto a
reclaiming stream ahead of its own replay (reintroducing the out-of-order
delivery the deferral prevents). On error the frames stay buffered for the
next attach — never lost.
- Grace reclaim now logs (detach + grace-expiry already did) so the reconnect
trail is complete for operators.
- sse-last-event-id doc: corrected the "shared by REST and ACP" claim — after
the #5809 serve-route split REST keeps its own copy; unifying them would
touch REST, so it's deferred (this PR keeps REST untouched).
Thread on a full deferred-flush integration test: the ordering invariant is
already locked at the unit layer (flushBufferedSessionFrames defer test +
gap-delivery test); a full-HTTP timing test against the FakeBridge would be
flake-prone, so it's intentionally not added.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon,sdk): harden /acp resumable stream from review round 7
Address review-pr findings on the §1.8 resumable stream, all additive /
backward-compatible (REST untouched, no behavioural side effects):
- connection-registry: guard flushBufferedSessionFrames against a closed
stream so deferred replies stay buffered for the next reconnect instead
of being dropped onto a dead socket. Keep the synchronous in-order
enqueue (SseStream serializes via one writeChain) — an await-per-frame
drain would let a live event interleave between deferred frames and
reorder the very replies this deferral preserves (W1).
- connection-registry: log at the moment of detach so an operator can
measure the real disconnect→reconnect gap against the grace window.
- index: route sessionId through logSafe() in the event-pump error log,
matching every other log line this PR adds (terminal-escape hardening).
- AcpHttpTransport: remove the abort listener in the finally block so a
long-lived signal reused across reconnects doesn't accumulate listeners.
- AcpHttpTransport: parse the SSE `id:` cursor with the server's strict
/^\d+$/ + MAX_SAFE_INTEGER rule instead of lenient Number() (rejects
proxy-mangled hex/exponential/empty cursors).
- AcpHttpTransport: document that an unparseable non-empty data frame is a
corrupt frame (not a heartbeat); tracing it is a follow-up once the SDK
grows a logger (the package lint config forbids console).
- tests: add sse-last-event-id.test.ts (parseLastEventId accept/reject +
safeLogValue control-char stripping/truncation) and a
flushBufferedSessionFrames closed-stream-retains-buffer case.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon,sdk): round-8 review hardening for /acp resumable stream
All additive / backward-compatible (REST untouched, no behavioural side
effects):
- AcpHttpTransport: attach a no-op catch to abortPromise so an
already-aborted signal at entry (loop never enters, Promise.race never
consumes the rejection) can't surface as an unhandled rejection.
- AcpHttpTransport: document that opts.maxQueued does not apply to the
/acp transport (the session stream is backed by the daemon's
server-controlled EventBus ring; there is no client-tunable queue to
forward it to) — intentionally ignored, not silently mis-applied.
- index: run err.message through logSafe() in the event-pump error log
(CR/LF/ANSI in a bridge error string would otherwise reach stderr raw),
and add operator breadcrumbs for the previously-silent onPumpSettled
branches (pump-ended-while-open full close; superseded-stream no-op),
completing the detach/reclaim/grace trail.
- tests: assert subscribeEvents writes Last-Event-ID on the outbound GET
when resuming and omits it on a first connect (the resume cursor must
reach the wire), plus an already-aborted-signal case that would fail on
an unhandled rejection without the catch above.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon): flush deferred /acp replies on replay_complete only
The EventBus emits `state_resync_required` BEFORE the replay frames (the
`epoch_reset` and `ring_evicted` paths both fall through to the replay
loop and still emit `replay_complete` at the end). The pump was releasing
the deferred id-less replies on EITHER boundary, so on a resync-triggering
resume the buffered `session/prompt` result was flushed ahead of the
replayed content chunks — the exact truncated-body reordering §1.8 fixes
(client sees "done" before the body).
Flush on `replay_complete` only. The live-only case (no cursor ⇒ no replay
⇒ no `replay_complete`) is still covered by the pump's post-loop safety
flush. Add an over-the-wire integration test (resume with a reply buffered
during the detach gap, bridge replays resync → content → replay_complete)
asserting the reply lands AFTER the replayed content; verified it fails
against the previous dual-boundary flush. Design doc updated.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon): close two §1.8 grace/replay-ordering holes
Both additive / in-scope / no REST change:
- Replay-window reply ordering (connection-registry, dispatch): the
resumptive-attach deferral only covered id-less replies ALREADY buffered
from the detach gap. A prompt that finished AFTER the new stream attached
but BEFORE replay drained went straight out live via `sendSession`,
overtaking replay frames not yet sent. Add a per-binding `replayPending`
flag (armed on resumptive attach, cleared on `replay_complete` in
`flushBufferedSessionFrames`) and route `replySession`'s out-of-band
replies through a new `sendSessionReply` that defers while it's set.
In-band pump frames keep using `sendSession`, so the `replay_complete`
frame itself can't be deferred (which would deadlock the release).
- Connection reaper vs session grace (index, connection-registry): the
conn-stream-close reaper treated only LIVE session streams as activity,
so a session detached into its own `SESSION_GRACE_MS` window (stream
undefined, graceTimer armed) didn't count — the connection could be
reaped at `CONN_GRACE_MS`, 404-ing the imminent session resume and
aborting the in-flight prompt early. Add `hasRecoverableSession()` and
treat a grace-armed session as activity in the reaper guard.
Unit tests for both at the registry layer.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): thread query params into ACP transport route extractors
The exported ACP HTTP/WS transports reduced request URLs to `pathname`
before the route table built JSON-RPC params, so every query parameter
from the REST-style DaemonClient helpers was dropped — e.g.
`readWorkspaceFile('a.ts', { maxBytes: 123 })` (`/file?path=a.ts&maxBytes=123`)
produced `_qwen/file/read` with `params: {}`. Same for `/file/bytes`,
`/stat`, `/list`, `/glob`, and `context-usage?detail=true`.
Pass `parsedUrl.searchParams` into `extractParams` and coerce each query
value to the type the daemon's ACP handlers require — the daemon validates
`maxBytes`/`line`/`limit`/`offset` as real numbers and `detail` as the
boolean `true`, neither of which a raw query string satisfies. Helpers
`strParam`/`numParam`/`boolParam` keep the per-route extractors terse.
`query` is optional so the existing path-only extractors are unaffected.
(`/workspace/voice/transcribe` has no ACP route at all — separate gap,
binary audio doesn't belong on the JSON-RPC transport.)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): route session-stream replies via a background pump (no-subscriber prompt)
The daemon answers POST /session/:id/prompt (and session/cancel,
set_config_option, set_mode, set_model) with 202 and routes the JSON-RPC
result onto the SESSION stream via replySession — not the connection
stream the transport pumps. So a DaemonClient that calls prompt() but
never iterates subscribeEvents had nothing reading that reply, and
sendRequest()'s pending promise never resolved → prompt() hung forever.
For these session-reply methods, sendRequest now opens a reference-counted
background session-reply pump (GET /acp + Acp-Session-Id) that routes
JSON-RPC responses to `pending`, released when the request settles. It's
suppressed when a subscribeEvents consumer is already iterating that
session (tracked via activeSessionSubscriptions) — the daemon's session
stream is single-reader, so a competing GET would detach the consumer's;
in that case the consumer already routes the reply (the W2 fix). The pump
skips notifications and permission requests (method-bearing frames) so a
permission request id can't be mis-routed onto a pending response slot.
All five methods require an owned session, so the pump's GET is always
authorized. Disposed pumps are aborted in dispose().
Verified the new test times out without the pump and passes with it.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): sequence /acp deferred replies by bus watermark + harden grace/reap
Address review on the §1.8 resumable-stream fixes:
- replayPending is now set from the current attach mode every time
(resume arms, fresh connect clears) so an aborted resume that skipped
its boundary flush can't strand the flag and buffer every later reply
forever (MsyIq, MylZ4).
- Deferred out-of-band replies carry a watermark (anchorId = bus head at
produce time) and release only once the pump delivers through that id,
via per-event releaseDeferredSessionReplies + endReplayDeferral at
replay_complete. A result produced during a slow replay no longer jumps
ahead of tail content still flowing as live events behind the boundary
(MsyIt). Unanchored fallback replies still release at the boundary.
- Connection reap re-evaluates after a session reclaim grace expires
(connGraceExpired + onSessionGraceExpired), so a conn blocked from
reaping by a then-recoverable session no longer lingers to the 30-min
idle sweep (MsyIs).
- Wrap the grace-timer teardown in try/catch so a throwing detach
callback can't crash the daemon from a bare setTimeout (MylZ8).
- sse-last-event-id reuses the shared logSafe sanitizer (covers C1 +
Unicode bidi) instead of a narrower divergent regex (M1isz); refresh
stale replayPending/flush JSDoc (MselO).
Unit tests cover the replayPending reset, watermark ordering, grace
expiry hook, and grace-timer try/catch.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): scope pending sweeps per stream + reset bus cursor on invalid id
Address review on the ACP HTTP transport:
- Tag each pending request with its routing scope (connection vs a
sessionId). A connection-stream failure now sweeps only conn-scoped
pendings, so it can't reject a session/prompt the session stream is
about to resolve; the session reply pump mirrors this for its own
scope (MselM).
- An invalid id: line later in an SSE frame resets the bus cursor to
undefined rather than carrying a stale earlier value into the event
(MselW).
- Strengthen the W2 response-routing test to register a pending request
and assert the frame RESOLVES it, not merely that it isn't yielded
(MylZ-). Add tests for the per-stream sweep partition and the id reset.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): accept `kind`-tagged _qwen/notify envelopes (don't drop resume signals)
The daemon's session-stream translateEvent stamps `_qwen/notify` events
under `kind` (state_resync_required, replay_complete, stream_error,
model_switched, …), but denormalizeAcpNotification read only `type` and
returned undefined for them — so subscribeEvents silently dropped every
such event. During a ring-overflow resume the SDK would never see
state_resync_required and would apply replayed events to stale state.
Read `params['type'] ?? params['kind']` (preferring `type`, so other
producers are unaffected) and add an SDK test feeding a `kind`-tagged
notify through subscribeEvents (M2bvl).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): reject session-scoped pendings when the subscription stream closes
A `session/prompt` reply routed through an active subscribeEvents consumer
(no reply pump is started while a subscription is live) would hang if that
session SSE stream closed before the reply arrived: the connection-stream
catch only sweeps conn-scoped pendings, and subscribeEventsInner's finally
cleaned up the reader but never the pendings.
Sweep session-scoped pendings in that finally too, gated so it only fires
when this is the session's last delivery route (no other active
subscription — the ref-count still includes self here — and no reply pump),
mirroring the reply-pump and connection-stream sweeps. Add a regression
test (M2iHz).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): harden ACP SSE readers + reply-pump handoff + empty query param
Address the latest review wave on the transport:
- pumpConnStream now bounds its unread SSE buffer with the same
MAX_SSE_BUF_CHARS guard the two session readers already have — the OOM
vector (a server that never emits a `\n\n` boundary) was open on 1 of 3
readers — and attaches the no-op abortPromise.catch() crash guard (M3BYQ).
- pumpSessionReplies mirrors subscribeEventsInner's abort handling: named
listener ref removed in finally (no leak on a clean drain of a reused
signal) + abortPromise.catch() so a pre-aborted signal can't surface an
unhandledrejection; and it throws the HTTP status on a non-OK response so
the failure is diagnosable rather than a silent void return (M3BYT, M3BYY).
- subscribeEvents aborts any existing background reply pump for the session
before opening the consumer stream. The single-reader session stream
detaches the pump anyway; aborting it skips its teardown sweep so it can't
spuriously reject the very `session/prompt` the consumer now delivers (M3BYa).
- numParam treats an empty value (`?maxBytes=`) as absent, not Number('')===0
(M3BYd).
Tests: empty-numeric-param omission, and the reply-pump abort-on-subscribe
handoff (pump aborted, its pending not rejected).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): log a breadcrumb when the replySession anchor is unavailable
The getSessionLastEventId fallback (deferring a reply unanchored when the
ACP binding briefly outlives the bridge session) was silent. Emit a scoped
stderr breadcrumb so an operator can tell that benign teardown race apart
from an unexpected bridge regression that starts exercising the fallback
(M3BYf).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): validate content-type in pumpSessionReplies before parsing as SSE
pumpSessionReplies fed any 2xx body straight to the SSE frame parser. A
non-SSE response (an HTML error page / a JSON proxy error injected by a
CDN) would be consumed as garbage or hang the pump waiting for `data:`
lines that never arrive — strictly weaker validation than its sibling
subscribeEventsInner, which already guards content-type.
Mirror that guard: between the res.ok check and getReader(), reject a body
that isn't text/event-stream (cancelling it first). Add a test that a
no-subscriber session/prompt whose reply pump GET returns text/html
rejects instead of hanging (M3pAM).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): close reply-pump handoff strand race + scope-guard reply resolution
Address the latest review wave:
- subscribeEvents now removes the aborted reply pump's map entry
SYNCHRONOUSLY, not just aborting it. Otherwise, if the subscription
exited before the pump's async `.finally` deleted the entry, BOTH
stranded-pending guards missed (the consumer sweep saw the entry still
present and deferred; the pump's sweep skipped on abort) — a live
session/prompt stayed in `pending` forever. Synchronous removal makes the
consumer sweep deterministically responsible (M3w6Y).
- Reply resolution (both the session-reply pump and the subscribeEvents
consumer path) now skips a reply whose pending is scoped to a DIFFERENT
session — defense-in-depth against a future daemon misroute silently
cross-delivering across the SDK boundary (M3w6d).
- denormalizeAcpNotification prefers a NON-EMPTY `type`; an empty-string
`type` no longer wins over a valid `kind` and drops the event (M3w6i).
Tests: reply-pump handoff happy-path (delivers/resolves) + strand case
(rejects, not stranded); empty-`type`→`kind` fallback; the SSE buffer cap
firing; the unanchored-reply hold/release branches; and connGraceExpired
reset on reconnect (M3w6e, M3w6f, M3w6g).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): sweep session pendings in the subscribe wrapper + carry pump error
Two follow-ups on the reply-pump handoff:
- The session-scoped pending sweep moves from subscribeEventsInner's
read-loop finally to the subscribeEvents WRAPPER finally. The read-loop
finally only runs once the pump reaches the loop; a fast failure (fetch
reject / non-OK / wrong content-type, all before the loop) skipped it and
stranded the pending. The wrapper finally always runs, so it covers the
fast-fail path too (M4DWq).
- ensureSessionReplyPump captures the pump's error (HTTP 401/404, wrong
content-type) and rejects swept pendings WITH it instead of a generic
message, so a caller can tell auth failure from a network drop (M4DWx).
Test: a 401 on the session GET (inner throws before its read loop) still
rejects the in-flight session-scoped pending via the wrapper sweep.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): carry the subscription error into the wrapper sweep + guard tests
Follow-ups on the reply-pump handoff:
- The subscribeEvents wrapper sweep now rejects with the actual cause of the
subscription's exit (captured from a try/catch around the inner generator)
instead of a hard-coded generic message. On the fast-fail path (401 /
wrong content-type thrown before the inner read loop) this wrapper finally
is the only sweep that fires, so the caller now sees the real failure —
parity with the reply-pump's pumpError reason (M4W9a).
Tests:
- the fast-fail sweep reason carries the 401 (not a generic message);
- the M3pAM non-SSE rejection asserts the content-type cause reaches the
caller (proves pumpError propagation) (M4W9g);
- cross-session scope guard, both the consumer and the reply-pump
resolution paths: a reply on session A's stream must not resolve a pending
scoped to session B (M4W9e).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): guard onSessionGraceExpired in the grace timer against an uncaught throw
The session grace-expiry setTimeout protected closeSessionStream with a
try/catch but called the owner-supplied onSessionGraceExpired callback
outside it. From a bare setTimeout, an uncaught throw there would crash the
whole daemon — the same hazard the teardown guard exists for. Wrap it in
its own try/catch (separate from teardown, so the conn-reap re-check still
runs even if teardown threw). Add a regression test (M4i9z).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): make conn-stream pump CRLF-aware; align replay opt-in doc
The connection-scoped SSE reply pump split frames with an LF-only
`buf.indexOf('\n\n')`. A server or proxy emitting `\r\n\r\n` frame
separators produces no `\n\n` substring, so the scan never found a
boundary: the unread buffer grew to the OOM cap and the pump threw,
leaving every connection-scoped JSON-RPC reply unresolved. Reuse the
shared CRLF-aware `consumeFrames` splitter (and strip a trailing CR
per data line) so the conn pump frames exactly like the session
readers. Add a regression test that delivers a conn-scoped reply over
`\r\n\r\n` and asserts it resolves.
Also update the design doc: the in-repo SDK `AcpHttpTransport` opts in
to replay in this PR (`supportsReplay = true` + resends Last-Event-ID),
so the backward-compat note no longer reads as "keeps false until it
opts in". Only the external agent-web transport flip stays deferred
(already listed under Out of scope).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon,sdk): log replay-deferral arm; document session-reply routing invariant
Add a stderr breadcrumb when a resume arms `replayPending`: while armed,
`sendSessionReply` defers every out-of-band reply until the pump delivers
`replay_complete`. If that sentinel never arrives (a dropped frame or a
pump error), the replies stay buffered indefinitely with no other trace —
the log gives operators a starting point. Silent on a fresh connect (no
deferral). Covered by a new test.
Also strengthen the `SESSION_STREAM_REPLY_METHODS` doc comment: name the
authoritative daemon call sites (dispatch.ts), spell out the hang failure
mode if the set drifts, and record a build-time grep / shared-constant
enforcement as a follow-up (a cross-package invariant the SDK can't type-check).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): use bracket notation for _meta index-signature access (TS4111)
`extractParams` returns `Record<string, unknown>`, so dot access to
`params._meta` violates `noPropertyAccessFromIndexSignature` (set in the
root tsconfig). The esbuild bundle path doesn't typecheck, so CI's build
stayed green, but strict `tsc --noEmit` reports 6 × TS4111 at these sites
(added with the query-param routing change). Switch all six to
`params['_meta']`. Purely syntactic — runtime behavior is unchanged.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(sdk): don't silently hang conn-scoped requests on a failed conn stream
`pumpConnStream` swallowed two failure paths: a non-2xx / no-body `GET
/acp` did a bare `return`, and read-loop errors were caught and dropped.
Either way the pump promise RESOLVED, so `openConnStream`'s `.catch`
never ran — connection-scoped JSON-RPC pendings stayed in the map
forever, and `connStreamAbort` was never cleared, so `ensureConnStream`
saw it non-null and never reopened the stream (every later 202 request
hung with no pump to deliver its reply).
- A non-2xx / missing-body response now throws (HTTP status in the
message) so the catch sweep rejects the conn-scoped pendings.
- The read-loop catch rethrows real errors and only swallows an
intentional abort (dispose / reconnect, which owns its own cleanup).
- `openConnStream` clears `connStreamAbort` in a `.finally` (guarded on
controller identity) so the stream reopens on the next request after
ANY settle — clean close, error, or abort.
Regression test: a 500 `GET /acp` rejects the conn-scoped pending (leaves
session-scoped ones for their own stream) and the next request reopens.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon,sdk): conn-stream error propagation, listener cleanup, buffer eviction, param parsing
Address review findings on the resumable /acp stream and exported SDK
transports — all additive/backward-compatible, REST untouched:
- openConnStream: reject connection-scoped pendings with the pump's REAL
error (HTTP 401/503, network drop) instead of a generic message, mirroring
ensureSessionReplyPump.
- pumpConnStream: keep the abort listener in a named ref and remove it in
finally so a long-lived signal reused across reconnects doesn't accumulate
listeners (mirrors the session readers).
- sendRequest: remove the abort listener on the happy path (the `{ once: true }`
listener self-removes only when the signal fires), preventing per-call
listener buildup on a shared caller signal.
- pushCapped: under a content flood, evict a REPLAYABLE id-bearing frame (the
ring redelivers it) before an irreplaceable id-less deferred reply — dropping
the latter would hang the session/prompt caller — and log the dropped id.
- acpRouteTable.boolParam: treat a present-but-empty value (`?detail=`) as
absent, matching numParam, so `{ detail: false }` isn't forwarded for an
unset param.
- connection-registry resume flush: hoist the `splice(0)` snapshot into a named
local to make the re-entrant copy-semantics invariant visible.
Tests: boolParam empty-value omission; pre-attach buffer keeps the id-less
reply under a 400-frame content flood. Document two exported-transport
limitations (permission voting; session RPC awaited inside the subscribeEvents
loop) as §1.7-adjacent follow-ups in the design doc.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon): never evict an irreplaceable id-less reply from the pre-attach buffer
The previous pushCapped change preferred evicting id-bearing (ring-replayable)
frames, but left a degenerate hole: when the buffer fills with ONLY id-less
deferred replies (no id-bearing entry exists), findIndex returned -1, dropIndex
fell back to 0, and the oldest deferred JSON-RPC reply was evicted — silently
hanging its session/prompt caller, the exact failure the guard exists to
prevent (wenshao).
Fix: when there is no replayable id-bearing frame to evict, do NOT drop —
append and let the id-less replies exceed the soft cap. The cap is a memory
bound against a CONTENT flood (id-bearing frames); id-less replies are bounded
by the number of in-flight session RPCs the client actually issued
(client-controlled, tiny), so they can't run away in practice. Log once when
over the soft cap. The connection buffer (no id accessor) keeps its FIFO
eviction unchanged.
Test: 300 all-id-less replies buffered past the 256 cap are all delivered on
reconnect, none evicted.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon): hard ceiling + transition-only logging for the id-less reply buffer
Follow-ups on the prior id-less-eviction fix (wenshao):
- Defense-in-depth HARD cap. The soft-cap path never drops id-less replies,
relying on "id-less replies are RPC-bounded" — true today but enforced only
by convention. Add HARD_BUFFERED_FRAMES_CAP (4× soft = 1024): past it, drop
the oldest id-less reply and log loudly, so a future non-RPC-bounded producer
or a buggy client can't grow the daemon heap without limit.
- Log at the soft-cap transition only (buf.length === MAX_BUFFERED_FRAMES), not
on every over-cap push — the comment said "once" but it logged linearly with
over-cap depth (~44 lines for 300 entries).
Tests: assert the soft-cap warning fires exactly once for a 300-entry overflow;
new test that 1100 id-less replies are bounded at the 1024 hard cap (oldest
dropped, newest kept, loud breach log emitted).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon,sdk): release all deferred replies when replay evicted frames; guard conn pump against session-scoped pending
When ring replay overflows and emits state_resync_required, the watermark
anchor guarantee is void (the anchored frame may have been evicted), so
hold-until-watermark could freeze deferred session replies indefinitely.
Track eviction through the pump loop and flush ALL buffered session frames
at replay_complete in that case instead of waiting on the watermark.
Also harden the SDK conn-stream pump: never resolve a session-scoped pending
entry from the connection stream (scope guard), and document the fresh-attach
(non-resumptive) caveat for ensureSessionReplyPump.
Tests: add FakeBridge.getSessionLastEventId so integration replySession no
longer throws (anchorId now reachable); cover the eviction cascade-release
path, the conn-stream session-scope guard, and shared reply-pump ref-counting.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(daemon): flush deferred replies on mid-replay iterator error; cover anchored watermark e2e
On an iterator error mid-replay the catch path re-throws, which drives
onPumpSettled; while the session stream is still open that takes the
closeSessionStream branch (full teardown, not a detach-with-grace), so any
still-deferred session replies in the binding buffer were dropped rather than
preserved. Flush them in the catch before signalling stream_error — same
safety flush as the happy-path completion (the iterator has terminated, so no
content frame can still race ahead of them). Correct the now-inaccurate
happy-path comment that claimed error-path frames stay buffered.
Add an end-to-end transport test for the anchored watermark path: with a real
getSessionLastEventId, a deferred reply is held through pre-watermark content
and released ON its anchor mid-replay, before replay_complete — distinguishing
the watermark release from the unanchored release-at-boundary path.
Document two deferrals in the design doc: response-replay idempotency for an
already-resolved permission (a conformant client dedupes on _meta.requestId;
full re-send belongs with the permission-coordination follow-up) and an
automated guard for the SESSION_STREAM_REPLY_METHODS drift.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(daemon,sdk): log replay effectiveness; de-shadow pump sweep; document resume/permission edges
Add an operator breadcrumb at replay completion (resumed-from cursor, delivery
high-water mark, bus replayed count, eviction flag) so 'did resume recover the
gap?' is answerable from server logs.
Rename the reply-pump sweep loop variable so it no longer shadows the outer
pump-map entry (unrelated types).
Clarify why the resume path drops id-bearing buffered frames (the event pump is
aborted on detach, so only id-less out-of-band replies accumulate during the
gap; ring replay owns id-bearing recovery and eviction is signalled via
state_resync_required). Document two opt-in-transport edges as
permission-coordination follow-ups: the no-subscriber reply pump's GET stream
causing an agent permission_request to be routed to the pump and dropped, and
why an automated SESSION_STREAM_REPLY_METHODS drift guard needs dataflow (the
prompt reply is decoupled from its case block).
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: 秦奇 <gary.gq@alibaba-inc.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>
|
||
|
|
35a8851a38
|
feat(core): add --insecure flag to skip TLS verification for self-signed endpoints (#3535) (#5962)
* feat(core): add --insecure flag to skip TLS verification for self-signed endpoints (#3535) Enable skipping TLS certificate verification for outbound model API connections via a new --insecure CLI flag, QWEN_TLS_INSECURE, or NODE_TLS_REJECT_UNAUTHORIZED=0. The setting is applied to the undici dispatcher Qwen Code installs: a direct connection uses connect TLS options, while a proxied connection disables verification for the upstream origin (requestTls) and a self-signed HTTPS proxy (proxyTls). Off by default; behavior is unchanged when not enabled. Fixes #3535 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(core): address review on --insecure (security + simplification) (#3535) - Block project .env from enabling QWEN_TLS_INSECURE by adding it to PROJECT_ENV_HARDCODED_EXCLUSIONS, so an untrusted repo cannot silently disable TLS verification for all API connections. - Remove the unverifiable Bun fetch `tls` special-casing; Bun users can still opt out via NODE_TLS_REJECT_UNAUTHORIZED=0, which Bun honors natively. - Do not suggest `--insecure` in the TLS error hint when verification is already disabled; show a network/protocol-oriented message instead. - Add tests: env-flag regex/falsy branches, loadCliConfig env side-effect, fetch hint variants, and a security guard for the project .env exclusion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(core): harden --insecure per review (#3535) - Block NODE_TLS_REJECT_UNAUTHORIZED from project .env too (initial load only consults PROJECT_ENV_HARDCODED_EXCLUSIONS), since isTlsVerificationDisabled() honors it. - When opting out, set NODE_TLS_REJECT_UNAUTHORIZED=0 process-wide in loadCliConfig and emit a stderr MITM warning. This makes the opt-out effective on the Bun runtime and the proxy-creation fallback path (which the undici dispatcher does not cover), and gives a user-visible signal. - Avoid evaluating isTlsVerificationDisabled() twice on the proxy path via a default parameter on getOrCreateSharedDispatcher. - Update tests accordingly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cli): broaden --insecure warning and log it; add env-path tests (#3535) - Broaden the TLS-disabled warning to state the process-wide blast radius (API, OAuth, MCP servers, child processes), since NODE_TLS_REJECT_UNAUTHORIZED=0 is set process-wide. - Also emit the warning via debugLogger so the state is discoverable in ~/.qwen/debug/ after terminal scrollback is gone. - Add tests for the env-var-only path (pre-set QWEN_TLS_INSECURE) and the already-disabled guard (no duplicate assignment/warning). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |