Commit graph

256 commits

Author SHA1 Message Date
叶公
b835e03bd4 chore: merge main into timeline PR 2026-07-09 18:55:31 +08:00
Dragon
6dafb330f2
docs: fix model-provider config shape and refresh feature/setting drift (#6552)
Audit findings against the current codebase:

- model-providers.md, auth.md: the documented modelProviders shape used
  the reverted `{ protocol, models }` wrapper. The canonical shape is a
  bare `ModelConfig[]` array per provider id (a wrapped entry in a
  migrated settings file is silently skipped). Update all examples and
  prose, document the separate top-level `providerProtocol` map for
  custom provider ids, and correct the unknown-key behavior.
- settings.md: correct the default for
  `model.chatCompression.screenshotTriggerThreshold` (20, not 50).
- commands.md: add the missing `/reload-plugins` command and note that
  `/dream` and `/forget` are registered only when managed auto-memory
  is available.
- Add a Computer Use feature page (on-by-default desktop automation via
  the cua-driver native driver) and wire it into the features nav and
  the qc-helper doc index.

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-08 23:04:47 +00:00
Nothing Chan
082b3bb3d9
fix(memory): give each linked git worktree its own auto-memory root (#6462)
getAutoMemoryRoot() resolved linked worktrees back to the canonical
repository root, so every worktree of a repo shared one project memory.
Focused worktree sessions polluted the shared MEMORY.md index with
unrelated entries, and chats/, workflows/, and team memory were already
per-worktree — project memory was the only shared exception.

Anchor project memory at the nearest git root (same resolution team
memory already uses) so each worktree gets its own memory directory.
The main checkout resolves to the same path as before, so existing
memory is unaffected.

Fixes #6449

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:34:03 +00:00
ChengHui Chen
1f92787aa0
feat(channels): add dmPolicy config to disable private/DM messages (#6521)
* feat(channels): add dmPolicy config to disable private/DM messages

Add DmGate class mirroring GroupGate to gate DM/private messages in
channel adapters. Operators can now set dmPolicy: 'disabled' in their
channel config to silently drop all DM messages while keeping group
messages active.

Closes #6392

* fix(channels): address review feedback for dmPolicy

- Add dmPolicy: 'open' to all test config factories (8 files) to
  maintain type correctness with required ChannelConfig field
- Add integration tests in ChannelBase.test.ts:
  - preflightInbound: DM dropped + group passes when dmPolicy=disabled
  - isStoredLoopTargetAuthorized: DM loop job disabled + group passes
- Add dmPolicy assertions in config-utils.test.ts (default + explicit)
- Keep dmPolicy as required field (not optional) for strict parity
  with groupPolicy
2026-07-08 12:01:10 +00:00
顾盼
14f02c132a
fix(core): Match hook display-name matchers to tool ids (#6373)
* fix(core): match hook tool display names

* fix(core): tighten hook matcher aliases

* fix(core): align hook matcher aliases
2026-07-08 02:22:55 +00:00
Dragon
394c1a289e
docs(channels): add WeCom to channels overview (#6490)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
The WeCom intelligent robot channel (added in #6436) has its own guide and _meta.ts entry, but the channels overview was never updated. Add WeCom to the platform list, quick-start guide links, the `type` option and `token` exclusion note, new `botId`/`secret` option rows, the media-support note, and the slash-command channel enumeration.

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-07 22:39:06 +00:00
qqqys
e3d7d10d1d
[codex] add natural channel memory intents (#6376)
* feat(channels): add natural channel memory intents

* fix(channels): add explicit guard and exhaustiveness check for clear_confirm intent

The clear_confirm path was handled as implicit fall-through at the bottom
of handleChannelMemoryIntent. If a new intent kind were added to the
ChannelMemoryIntent union, it would silently execute clearChannelMemory
without user confirmation — a data-loss risk.

Add explicit if (intent.kind === 'clear_confirm') guard and a
const _exhaustive: never assertion so TypeScript flags any unhandled
kinds at compile time.

* fix(channels): close session leak in classifier and fix regex separator

- BridgeChannelMemoryIntentClassifier now wraps prompt() in try/finally
  to always call cancelSession(), preventing daemon session leaks on
  every classifier invocation. Cleanup errors are caught so they cannot
  mask a successful classification result.
- Add missing optional punctuation separator to the 以后记住 regex
  pattern for consistency with other Chinese remember patterns.

* fix(channels): enforce pending clear state for channel memory confirmation

The clear_confirm intent executed clearChannelMemory directly without
verifying a prior clear_request was issued for the same chat. Any
authorized user could clear any chat's memory by sending the confirmation
phrase standalone, bypassing the two-step flow.

Add a per-target pending clear map (chatId + threadId, 60s TTL) that
is set during clear_request and verified+consumed during clear_confirm.
Standalone confirmation phrases now get rejected with a prompt to
issue the clear request first.

* fix(channels): include senderId in pendingClears key to prevent cross-user confirmation

User A could initiate clear_request in a group chat and User B could
confirm it, since the pending key only included chatId+threadId.
Add senderId to the key so only the user who initiated the clear
can confirm it.

* fix(channels): harden memory intent review fixes

* fix(channels): cover memory clear sender guard

* fix(channels): block group memory mutations

* fix(channels): avoid ambiguous memory saves

* test(channels): cover memory classifier cleanup

* test(channels): cover memory clear expiry

* fix(channels): restore channel memory slash aliases

* test(channels): cover memory intent edge cases
2026-07-07 15:25:01 +00:00
qqqys
467b292b50
feat(channels): add WeCom intelligent robot channel (#6436)
* feat(channels): add WeCom smart bot channel

* fix(channels): harden wecom review suggestions

* fix(channels): address wecom critical review

* fix(channels): include wecom mixed voice text

* fix(channels): tighten wecom outbound media

* fix(channels): harden wecom outbound sends

* fix(channels): address wecom review blockers

* fix(channels): address wecom review followups

* fix(channels): harden wecom inbound handling

* fix(channels): address wecom auth and media review

* fix(channels): tighten wecom inbound cleanup

* fix(channels): harden wecom media safety

* fix(channels): address wecom review typecheck

* fix(channels): harden wecom media review gaps

* fix(channels): address wecom review blockers

* fix(channels): tighten wecom media edge cases

* fix(channels): address wecom review blockers

* fix(channels): address wecom media review blockers

* fix(channels): address wecom review follow-ups

* fix(channels): address wecom review blockers

* fix(channels): close wecom review blockers

* fix(channels): close wecom preflight dedup race

* fix(channels): close wecom review gaps

* fix(channels): harden wecom kick reconnect

* fix(channels): defer wecom session resolution

* fix(channels): clean wecom session attachments

* fix(channels): harden wecom reconnect and media cleanup

* fix(channels): address wecom review diagnostics

* fix(channels): improve wecom diagnostics

* fix(channels): reset wecom kick retries

* fix(channels): improve wecom diagnostics

* fix(channels): preserve sync cancel preflight

* fix(channels): close wecom connection and ssrf gaps

* fix(channels): clean coalesced wecom attachments

* fix(channels): bound wecom sdk connect wait

* fix(channels): scope wecom untracked attachment cleanup

* fix(channels): block wecom nat64 local-use ssrf

* fix(channels): harden wecom media handling

* fix(channels): harden wecom group gates

* fix(channels): bound wecom kick reconnect cycles

* fix(channels): drain loop collect prompts directly

* fix(channels): align wecom buffer hooks

* fix(channels): harden wecom delivery failures

* fix(channels): recover from wecom attachment write failures

* fix(channels): surface wecom media send failures

* fix(channels): harden wecom replay and reconnect

* fix(channels): clarify wecom partial delivery cleanup

* fix(channels): close wecom rejected downloads

* fix(channels): retain wecom dedup after processing starts

* fix(channels): harden wecom reconnect and media errors

* fix(channels): add wecom media error context

* fix(channels): improve wecom dns diagnostics

* fix(channels): keep wecom kick retry alive

* fix(channels): allow wecom quoted bot replies

* fix(channels): preserve wecom code fences across chunks

* fix(channels): harden wecom reconnect lifecycle

* fix(channels): report wecom media dir setup failures

* fix(channels): harden wecom reconnect recovery

* fix(channels): align wecom review fixes

* fix(channels): harden wecom marker parsing

* fix(channels): keep wecom reconnect timers alive

* fix(channels): handle wecom tilde fences

* fix(channels): preserve wecom fence state

* fix(channels): clean up wecom attachment races

* fix(channels): bind wecom media reads to file handles

* fix(channels): prevent wecom symlink media opens

* fix(channels): address wecom review blockers

* fix(wecom): remove media URL from error messages to prevent credential leakage

The guardedHttpsDownload error messages included rawUrl (truncated to 120
chars), which leaks private WeCom media download URLs into stderr and log
aggregation systems. Remove the URL from redirect and HTTP error messages.

* fix(wecom): address review feedback — tests, security, correctness

- Remove stale URL assertions from media download error tests (the error
  messages no longer include raw URLs after the credential-leak fix)
- Redact sensitive fields (secret, aeskey, token, password, authorization)
  in formatSdkError's JSON.stringify fallback to prevent credential
  leakage in logs
- Add indented code block detection to findCodeRanges so [IMAGE: path]
  inside 4-space/tab-indented code is not stripped as a media marker
- Add disconnectGeneration guard before mkdirSync in downloadAttachments
  to prevent orphaned temp directories when disconnect() races with
  in-flight attachment downloads

* fix(wecom): wrap client.disconnect() in catch block to preserve connection error

In the connect() catch block, client.disconnect() could throw (e.g. if
the WebSocket was already destroyed), masking the original connection
error. Wrap in try/catch so cleanup failures never shadow the root cause.

* fix(channels): address wecom reconnect review blockers

* fix(channels): harden wecom reconnect review fixes

* fix(channels): harden wecom review blockers

* fix(channels): address wecom review blockers

* fix(channels): preserve unsupported wecom media markers

* fix(channels): address wecom reliability suggestions

* fix(channels): allow wecom retry after early drops

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-07 15:24:19 +00:00
Dragon
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>
2026-07-07 03:03:49 +00:00
Dragon
bcdb44c5d3
docs(hooks): document PreToolUse permissionDecision 'ask' behavior (#6411)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-06 22:18:49 +00:00
Dragon
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>
2026-07-06 17:05:48 +00:00
Dragon
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>
2026-07-05 09:49:52 +00:00
Tianyuan
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).
2026-07-03 06:34:37 +00:00
Dragon
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>
2026-07-03 01:55:45 +00:00
Dragon
55e425c1d2
docs: document the /config slash command (#6145)
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-02 13:52:59 +08:00
Dragon
2c95d9383f
feat(core,cli): unified reasoning effort with /effort command (#6072)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* 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>
2026-07-01 20:20:28 +00:00
Dragon
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>
2026-07-01 15:34:20 +00:00
qqqys
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>
2026-07-01 09:59:48 +00:00
qqqys
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
2026-07-01 16:24:09 +08:00
Shaojin Wen
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.
2026-07-01 16:24:02 +08:00
DennisYu07
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
2026-07-01 16:23:24 +08:00
Dragon
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>
2026-07-01 06:43:29 +00:00
jinye
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>
2026-07-01 01:40:54 +00:00
pomelo
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>
2026-06-30 22:44:34 +00:00
pomelo
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>
2026-06-30 15:18:01 +00:00
jinye
c90e6e7ba4
feat(channels): Add channel agent bridge abstraction (#5978)
* feat(channels): add channel agent bridge abstraction

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(channels): handle bridge session lifecycle cleanup

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(channels): close bridge lifecycle review gaps

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5978)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5978)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix: address channel bridge review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5978)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5978)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-29 10:03:28 +00:00
Zqc
8daeb5b1f9
feat(core): add configurable auto-compact threshold and Stop hook context usage (#4025) (#5868)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* feat(core): add configurable auto-compact threshold and Stop hook context usage (#4025)

Add two features requested in issue #4025:

1. Configurable auto-compact threshold via settings.json
   - Add context.autoCompactThreshold setting (0-1, default 0.7)
   - Extend computeThresholds(window, pct?) to accept optional pct parameter
   - Wire all 4 call sites (chatCompressionService, geminiChat, contextCommand, useContextualTips)
   - Large windows (>110K) dominated by absolute branch, custom threshold mainly affects small windows

2. Stop hook stdin payload includes context usage data
   - Add ContextUsageData interface and buildContextUsage helper
   - Extend StopInput with context_usage, context_limit, input_tokens fields
   - Wire 3 callers (Session.ts, client.ts, config.ts)
   - Enables hook scripts to observe context usage and suggest compact strategies

* fix(test): add getAutoCompactThreshold mock to geminiChat.test.ts, add NaN guard to buildContextUsage

* fix(review): address round 4 findings — schema constraints, Partial<ContextUsageData>, buildContextUsage validation

* docs: add context.autoCompactThreshold and Stop hook context usage fields documentation

* fix(review): add contextWindowSize fallback, pct clamp, doc accuracy, threshold propagation test

* fix: correct warn value in contextCommand test comment

* test(chatCompressionService): fix misleading pct=1 test name and assertion

* fix(chatCompressionService): prevent negative warn threshold for low pct values

* docs(chatCompressionService): update JSDoc warn formula to include max(0, ...) floor

* test(chatCompressionService): add pct clamping tests and fix NaN handling

Add tests for out-of-range pct values (-0.5, 1.5, NaN) to verify
computeThresholds clamping behavior. Fix implementation to use
Number.isFinite() check so NaN falls back to DEFAULT_PCT instead
of propagating through Math.max(0, NaN) which yields NaN.

* test(config): add MCP Stop dispatch validation tests

Add tests for buildContextUsage runtime validation in MCP Stop dispatch path:
- Valid numeric inputs produce correct ContextUsageData
- Missing/undefined fields return undefined
- String values rejected by Number.isFinite validation
- Negative values return undefined

Also add Number.isFinite check for contextWindowSize in buildContextUsage
to properly validate MCP input types at runtime.

* fix(chatCompressionService): fix TypeScript type narrowing for pct parameter

Use explicit undefined check before Number.isFinite to properly narrow
the number | undefined type in the ternary expression.

---------

Co-authored-by: 俊良 <zzj542558@alibaba-inc.com>
2026-06-28 10:17:57 +00:00
qqqys
673454839e
feat(memory): add a git-shared team memory tier (#5886)
* feat(memory): add a git-shared team memory tier

Add an opt-in third auto-memory tier — TEAM — stored in the repository at
`.qwen/team-memory/` and shared with collaborators through git. The private
USER and PROJECT tiers are unchanged.

- Enabled via the `memory.enableTeamMemory` setting (default off) or the
  `QWEN_CODE_MEMORY_TEAM` env override; inactive otherwise.
- The model is taught a three-tier prompt and when to route saves to the
  shared tier (project-wide conventions, team-wide references).
- Writes to the team directory are scanned for secrets (a curated
  gitleaks-style ruleset) and hard-blocked, since the directory is committed
  to the repo and visible to everyone with repo access.
- Team writes default to 'ask' permission — not auto-allowed like the private
  tiers — so they are proposed for confirmation in the default approval mode
  and surface in the git diff for review.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(vscode): update settings schema for team memory

* fix(memory): harden team memory review gaps

* fix(memory): address team-memory PR review feedback

- secret-scanner: AWS key suffix is base62 ([A-Z0-9]), not base32
  ([A-Z2-7]) — old class missed IDs with digits 0/1/8/9; add a
  positive test for such a key.
- paths: document that isAnyAutoMemPath deliberately excludes team
  memory (security-load-bearing — team writes must stay 'ask').
- extractionAgentPlanner: name the team-memory dir in the scoped deny
  message so a denied team write is debuggable.
- prompt: spell the tier count via a number-word lookup so a future
  4th tier never silently reads "two".
- team-memory-secret-guard: debug-log matched rule IDs only (never the
  secret content) when a write is blocked.
- edit: when a blocked team write's secret already exists in the
  on-disk file, tell the user to delete the committed secret.
- docs: caveat that a directory-form gitignore (.qwen/) makes the
  !-reinclude a no-op; use the file-glob form.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): harden team-memory secret guard on blocked writes and notebooks

- write-file: blocked team-memory secret write now returns an `error`
  field (INVALID_TOOL_PARAMS) so the framework treats it as a failure
  instead of a silent success, mirroring edit.ts.
- notebook-edit: scan the serialized .ipynb that hits disk through the
  team-memory secret guard before writing, blocking with the same
  `error` field; behavior unchanged for non-team-memory paths.
- store: log non-ENOENT errors in readTeamAutoMemoryIndex before
  re-throwing, so EACCES/EIO/ELOOP no longer vanish via config.ts's
  best-effort `.catch(() => null)`.
- config: emit a debug diagnostic when team memory is enabled but
  suppressed by the untrusted-workspace trust gate.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* feat(memory): auto-generate the team memory index

Generate the team `MEMORY.md` index by scanning the saved memory files
instead of having the model hand-maintain it. The index is derived and
ordered by path, so the committed file is deterministic across machines.
This removes the git merge-conflict surface a hand-edited shared index would
have, keeps the index consistent with the files, and frees the model from the
two-step save for the team tier.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* feat(memory): opt-in git sync for team memory

Add an opt-in, best-effort git sync of team memory at session start so a
daemon can share team memory through the remote without manual git. Off by
default; enabled with `QWEN_CODE_MEMORY_TEAM_SYNC=1`.

It commits the team directory (only that path), pulls fast-forward-only, then
pushes. `--ff-only` is deliberate: it never creates a merge commit or a
conflict — a diverged branch is skipped rather than touching the working tree.
All git is best-effort and never throws, so a failure cannot break a session.
Commits can be attributed to the acting user via an optional author, for
cooperative per-user attribution on a shared daemon.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): close team-memory secret-guard regex + test gaps

- secret-scanner: add `_` to the two T3BlbkFJ-marker char classes so legacy
  base64url OpenAI keys with underscores in the body no longer bypass the
  scanner; extend the OpenAI positive samples with an underscore key body.
- edit: cover the pre-existing-secret branch — seed the on-disk file with a
  full detectable token, assert the distinct "already exists" message and that
  the blocked edit leaves the file untouched.
- team-paths: cover the first-ever write before .qwen/team-memory exists,
  exercising realpathNearestExisting walking up to an existing ancestor.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* feat(core): add Google OAuth client secret and SendGrid secret-scanner rules

Extend the team-memory secret scanner with two more high-confidence,
distinctive-prefix credential patterns (both gitleaks rules):

- gcp-oauth-client-secret: GOCSPX- prefix + 24 base64url chars,
  placed next to the existing GCP AIza API-key rule.
- sendgrid-api-token: SG. + 22-char id + . + 43-char secret.

Both have a near-zero false-positive bar, matching the curated set's
existing criteria. Add a load-bearing positive sample per rule.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): warn when team memory is enabled but not git-shareable

When the team tier is active, emit a one-time startup warning if its
directory is git-ignored (e.g. a directory-form `.qwen/` ignore that a
`!`-reinclude cannot escape) or there is no git root — so the tier never
silently shares nothing. Gated on the active (trusted + enabled) tier;
probes a representative file path with `git check-ignore`, and is
latched so refreshHierarchicalMemory re-runs do not re-emit it.

Closes the last open review thread (wenshao + qwen-code-ci-bot both
flagged the silent-inert case); the docs caveat already shipped.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* feat(memory): dedup team index by description; fix empty-frontmatter parse

The team index now collapses entries that share a (normalized) description
into one line — listing the other files via "(also: …)" — so two people
saving the same shared fact don't surface as duplicates. Nothing is dropped;
every file path is preserved and the grouping is deterministic (input is
path-sorted).

Also fix a pre-existing frontmatter bug: parseFrontmatterValue used `\s*`,
which crosses newlines, so an empty value (`description:`) captured the next
frontmatter line. Bound it to horizontal whitespace.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* feat(memory): add memory.enableTeamMemorySync setting

Team-memory git sync was env-only (QWEN_CODE_MEMORY_TEAM_SYNC). Add a
first-class `memory.enableTeamMemorySync` setting mirroring the
enableTeamMemory plumbing (settingsSchema, cli/core config, acpAgent
type/schema/default/keys/normalizer, desktop mirror), with the env var kept
as a 0/1 override. Off by default. Docs updated.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): audit hardening — resilient scan, deterministic team index

From a multi-round adversarial audit of the team-memory tier:

- scan: a single unreadable memory file (EACCES, or a TOCTOU delete during
  `git pull`) no longer rejects the whole scan and wipe every memory from the
  index — per-file failures are caught and skipped (debug-logged).
- indexer: order the team index by code unit, not `localeCompare()`, so the
  committed MEMORY.md is byte-identical across machines/locales and the
  ff-only sync can't wedge on index churn.
- scan: escape the frontmatter key before building its RegExp (latent hardening).
- config: surface a skipped/failed team-memory sync at debug level instead of
  silently swallowing it.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): deterministic team index cap + surface diverged sync

Round-2 audit follow-ups:

- scan: the >200-file cap selected its subset by mtime + locale basename, which
  differs per machine/locale — so a large team's committed index churned and
  wedged the ff-only sync. Team scans now cap by code-unit path (deterministic);
  private tiers keep mtime-recency (never shared, so it's fine).
- sync: a diverged branch made `pull --ff-only` (and the push) fail with no
  `skippedReason` — a silent no-op for a user who opted into sync. Now reported
  as `pull-failed` / `push-failed` and logged. Docs note pull/push are
  whole-branch, not path-scoped.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): regenerate settings schema for enableTeamMemorySync

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): harden team-memory git sync (reconcile-first, scoped push, safe failure)

The opt-in team-memory git sync could publish unrelated local commits, wedge a
two-writer branch, leave team files staged on commit failure, and hang session
start on a credential prompt. Harden all four maintainer-flagged criticals:

- Reconcile first: run `git pull --ff-only` BEFORE committing so the local team
  commit lands on top of upstream instead of diverging; a diverged branch is
  skipped cleanly as `pull-failed` rather than committing then wedging `--ff-only`.
- Scoped push: push only when THIS sync created the commit and the branch was
  not already ahead of `@{u}` (pre-sync), via an explicit single-branch refspec
  (`HEAD:<merge-ref>`) — never an unqualified `git push`. New `local-ahead` skip.
- Safe failure: unstage the team path when the commit fails (hook/GPG/identity)
  so a user's next manual `git commit` cannot sweep team files in.
- Non-interactive git: force `GIT_TERMINAL_PROMPT=0` + batch-mode SSH, SIGKILL on
  timeout, and a shorter timeout so sync cannot block session start on a prompt.

config refresh: rebuild the team index BEFORE syncing (so a fresh MEMORY.md is
committed/pushed, with a re-build after a pull that brought new files) and key
the shareability latch by projectRoot so a newly entered repo is re-checked.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): harden team-memory index/scan/path security

Address the security/correctness criticals from PR #5886 review. Team
memory is committed to the repo and loaded into every collaborator's
system prompt, so its write/scan/classification paths are trust
boundaries.

- indexer: pass noFollow on the team MEMORY.md write and reject a
  symlinked team root, so a committed `.qwen/team-memory` (or index)
  symlink can't redirect the generated index outside the repo.
- indexer: sanitize attacker-controlled frontmatter title/description
  (strip control/zero-width/bidi chars, collapse newlines, defang
  code/link markdown, cap length) before embedding into MEMORY.md, to
  prevent prompt injection into the shared context.
- indexer: skip the rewrite when the generated index is byte-identical,
  avoiding mtime churn and no-op commit cycles.
- scan: normalize CRLF before parsing so a Windows checkout's team files
  don't silently drop out of the shared index.
- paths: resolve a dangling final-component symlink via lstat/readlink in
  realpathNearestExisting, so a `decoy -> team-memory/leak.md` (missing
  target) is classified as team and the secret scanner can't be bypassed.
- secret-scanner: bound both quantifiers in the openai-api-key T3BlbkFJ
  rule ({20,512}) to remove O(n^2) ReDoS backtracking; real keys still
  match.
- team-memory-git-status: also probe a representative topic path so a
  repo that re-includes the index but ignores topic files is flagged as
  non-shareable.
- notebook-edit: run the team-memory secret scan in validateToolParamValues
  too, for parity with edit/write-file.

Adds load-bearing tests for each fix.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): sanitize team index paths and verify whole-path containment

Refines the earlier team-memory security hardening with two fixes the
first pass left open:

- sanitizeIndexPath now defangs the attacker-controlled relativePath
  rendered into the committed MEMORY.md — at BOTH the `](path)` link
  target and the "(also: ...)" dedup suffix. A git filename may legally
  contain newlines and markdown delimiters, so a raw path could inject a
  second physical line (e.g. "- SYSTEM:") and reopen the prompt-injection
  structure the patch was closing for title/description.
- rebuildTeamAutoMemoryIndex now realpath-resolves the whole team root
  and requires it to equal repoRoot/.qwen/team-memory. The prior lstat
  only inspected the leaf, so a symlinked PARENT component (e.g.
  `.qwen -> /tmp/out`) let scan/write escape the repo undetected while
  the guard believed it had rejected symlinks.

Also adds a regression test pinning the security-load-bearing invariant
that isAnyAutoMemPath excludes team paths (so team writes stay ask-gated).

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): keep team index link targets addressable, not just safe

The earlier injection fix rewrote legal filename chars (`(` `)` `[` `]` and
backticks) to `_` and used that destroyed value as the ACTUAL MEMORY.md link
target. A real file `feedback/a(b).md` was emitted as `](feedback/a_b_.md)`,
which does not exist (or, worse, collides with a different real file) — the
link was injection-safe but no longer addressable.

Replace `sanitizeIndexPath` with `encodeIndexPathTarget`, a reversible
percent-encoder: every char outside an addressable allowlist (alphanumerics
and `/ . - _ ~`) is percent-encoded, so breakout chars become inert ASCII
(newline->%0A, `(`->%28, `)`->%29, space->%20, backtick->%60, ...). The target
is a single line with no `](`/`)` breakout, yet `decodeURIComponent` recovers
the exact original path so the link resolves to the real file. `/` is kept
literal so the path still works as a relative reference. Applied at both index
sites (the main `](path)` link and the `(also: ...)` suffix). The display label
(title) stays non-reversibly sanitized as before — it is not addressable.

Update the path tests to assert the emitted target percent-decodes back to the
real relative path, and add a `feedback/a(b).md` case. The newline+`]`/`)`
injection assertions remain (mutation-verified: they fail if the encoding is
dropped).

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): gate team-memory sync on the index safety check

rebuildTeamAutoMemoryIndex throws when the team root is a symlink that
could redirect the committed index outside the repo, but the surrounding
.catch swallowed the error and syncTeamMemory ran independently — so a
refused symlink/escape still got git add/commit/push'd on the out-of-repo
dir, defeating the indexer's refusal. Capture whether the rebuild
succeeded and only sync when it did, so a failed safety check skips sync
entirely.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): harden team-memory git sync and cover execute-time secret scan

Several robustness fixes to the team-memory git sync, plus a missing test:

- Respect a user's GIT_SSH_COMMAND: append our non-interactive batch guards
  (-oBatchMode=yes -oConnectTimeout=5) onto it instead of clobbering it, so a
  custom identity (-i), proxy jump (-J), or non-standard port is preserved.
- Skip cleanly on a detached HEAD (new `detached-head` skippedReason) instead
  of creating an orphaned commit that can never be pushed. A branch with no
  upstream still commits locally as before.
- Use SIGTERM (not SIGKILL) as the git kill signal so a timeout mid-commit/-pull
  lets git release index.lock and clean up; the non-interactive ssh guards
  already bound the network-hang case.
- docs(memory): correct the sync description to match the implementation
  (rebuild index, ff-pull BEFORE commit, push only the scoped sync commit,
  skip on divergence/no-upstream).
- test(notebook-edit): cover the execute-time team-memory secret scan (the
  full-notebook backstop), distinct from the existing validate-time test.

Co-Authored-By: Qwen-Coder <noreply@qwen.ai>

* fix(memory): split security vs operational team-sync failures; per-op git kill signal

Refine the recent team-memory sync changes per review:

- indexer: throw a typed TeamMemoryRootSecurityError for the symlink/escape
  rejection so the sync gate can tell a SECURITY refusal apart from an
  OPERATIONAL IO failure. config now blocks sync ONLY on the security class;
  an operational rebuild failure (EACCES/ENOSPC/EPERM) is logged but
  self-corrects on the next rebuild and no longer permanently gates legitimate
  team sync. The security invariant (never add/commit/push an escaping root)
  is unchanged.
- team-memory-sync: pass a per-op kill signal to tryGit — SIGKILL for the
  hang-prone network/ref ops (pull/push/rev-parse/symbolic-ref; no index.lock
  to corrupt), SIGTERM for mutating ops (add/commit) so git can release locks
  and clean up. Node's execFile timeout does not escalate to SIGKILL on its
  own, so a child trapping SIGTERM could hang past the timeout.
- team-memory-sync: thread the resolved branch into resolvePushTarget so it no
  longer re-spawns `git symbolic-ref` after the detached-HEAD check.
- docs: fix a broken sentence in the team-memory git-sync section.
- tests: cover both rebuild failure classes (security -> sync skipped;
  operational -> sync still runs) and a positive gate (rebuild ok + sync
  enabled -> sync runs); assert the typed error on the indexer symlink tests.

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: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-06-27 12:29:48 +00:00
Dragon
4efe2c77bc
docs: add vertex-ai auth, missing commands, and qc-helper index entries (#5727)
* docs: add CLI subcommands section with qwen sessions list

- Add section 5 to commands.md for CLI-level subcommands
- Document qwen sessions list with --json and --limit flags
- Include output format, examples, and usage patterns

The sessions list command was added in commit 14e6ae8c2 but not documented.

* docs: add vertex-ai auth, missing commands, and qc-helper index entries

Audit docs/ against current codebase and fix high-impact drift:

- Add vertex-ai to auth.md, model-providers.md, and tos-privacy.md supported
  auth type tables (was missing from all three)
- Add missing slash commands to commands.md: /cd, /import-config, /workflows
- Add /doctor subcommands (memory, cpu-profile, rollback) and /extensions
  subcommands (list, manage, explore, install) to commands.md
- Add undocumented altNames: /clear→/reset,/new; /stats→/usage;
  /auth→/connect,/login; /resume→/continue; /compress→/summarize
- Add 8 missing feature entries to qc-helper SKILL.md topic index:
  code-review, followup-suggestions, tool-use-summaries, markdown-rendering,
  structured-output, dual-output, channels, tips
- Fix CLI binary name in contributing.md (qwen-code → qwen)

* docs: resolve review feedback on auth count, /workflows usage, GOOGLE_MODEL example

- auth.md: correct intro count from 'four' to 'three' methods (3 bullets + 3 Options; Vertex AI is a provider under API Key)
- commands.md: add '/workflows <runId>' to usage column to match argumentHint '[runId]'
- model-providers.md: add required GOOGLE_MODEL to the Vertex AI env example so it matches the prose and modelConfigUtils requirement

* docs: remove duplicate /workflows row introduced by main merge

main already documents /workflows (with the <runId> usage); the branch
merge kept both rows, leaving a duplicate in the Tool and Model
Management table. Drop the redundant row added by this PR.

* docs: resolve review feedback — /extensions explore source arg, CLI-name remnants

- commands.md: /extensions explore requires a <source> (exploreAction errors 'Unknown extensions source' without it)
- contributing.md: finish the qwen-code -> qwen CLI rename on the debug note (binary + .qwen config dir); repo-name references left intact

* docs: resolve review feedback — Vertex AI in tos-privacy, /doctor rollback clarity

- tos-privacy.md: propagate Vertex AI (which the header already counts as the 4th method) into the Data Collection list, FAQ Q1 and Q3, and add a '4. If you are using Vertex AI' section pointing to Google Cloud terms — fixes the four-vs-three internal inconsistency
- commands.md: clarify /doctor rollback rolls back the standalone CLI binary (standalone installs only) and disambiguate from /rewind's rollback alias (doctorCommand.ts gates on isStandalone)

* docs: resolve review feedback — clear semantics, doctor argHints, arrow spacing

- /clear: fix description to 'Clear conversation history and free up context' and drop the misleading '(shortcut: Ctrl+L)' grouping; Ctrl+L only clears the screen (clearScreen), it does not reset the session like /clear (clearCommand.ts)
- Ctrl/cmd+L keyboard row: clarify it clears the visible screen only, not 'Equivalent to /clear'
- /doctor memory and /doctor cpu-profile: surface the full argumentHints ([--sample] [--snapshot], [--duration <seconds>]) from doctorCommand.ts
- normalize section 1.4 arrow subcommands to spaced '→ ' style (approval-mode rows were the lone outliers)

* docs: resolve review feedback — enumerate /extensions explore sources

List the two valid sources (Gemini, ClaudeCode) from EXTENSION_EXPLORE_URL in extensionsCommand.ts so users can discover them without trial and error.

* docs: resolve review feedback — add extensions install security warning

/extensions install (extensionsCommand.ts) installs arbitrary git repos/paths with no confirmation prompt; add a warning that extensions run with full Qwen Code permissions and should only come from trusted sources.

* docs: resolve review feedback — add /stats subcommands, /auth aliases in 1.11

- commands.md: add /stats daily, /stats monthly, /stats export rows (registered in statsCommand.ts with day/month aliases and --format csv|json)
- section 1.11: note /auth's /connect and /login aliases (parallel to section 1.4)

* docs: resolve review feedback — /stats export full args, /summarize note

- /stats export: show the full argumentHint ([date|month] and [--output path]) from statsCommand.ts
- add a note disambiguating /summarize (alias of /compress, destructive) from /summary (project summary)

* docs: resolve review feedback — complete /arena /ide /directory /voice /mcp usage

Add the missing subcommands/arguments shown in the command sources:
- /arena: stop, select (arenaCommand.ts)
- /ide: enable, disable (ideCommand.ts)
- /directory: show (directoryCommand.tsx)
- /voice: hold, tap, off (voice-command.ts argumentHint)
- /mcp: nodesc, schema, auth, noauth (mcpCommand.ts argumentHint)

* docs: resolve review feedback — arena/stats aliases, trim /stats description

- /arena select: note alias 'choose' (arenaCommand.ts)
- /stats daily, /stats monthly: label day/month as aliases (statsCommand.ts)
- /stats: trim the description to a terse behavior-focused line (drop volatile tab names/keyboard shortcuts that belong in the dashboard help)

* docs: resolve review feedback — /copy args, /doctor memory --snapshot warning

- /copy: document language/latex/mermaid/index selection (copyCommand.ts argumentHint)
- add a warning that /doctor memory --snapshot writes a heap snapshot with sensitive data (matches doctorCommand.ts runtime warning)

* docs: resolve review feedback — mcp/approval-mode/copy accuracy, qwen privacy URL

- /mcp: drop deprecated auth/noauth (mcpCommand.ts argumentHint is now desc|nodesc|schema; auth/noauth are stubs)
- /approval-mode: drop nonexistent --project (mode is session-only), show actual invocations, add a safety warning for auto-edit/auto/yolo
- /copy: note N = Nth-last reply (copyCommand.ts)
- tos-privacy: unify Qwen Privacy Policy URL to qwen.ai/privacypolicy

* docs: resolve review feedback — import-config args + feature-gated commands note

- /import-config: show 'all' (default source) and enumerate --scope user|project (importConfigCommand.ts)
- add a note that /workflows, /lsp, /trust register only when their feature setting is enabled (BuiltinCommandLoader.ts gates them, default off)

* docs: fix feature-gating mechanisms + restore /stats tab names

- Correct the /workflows/lsp/trust note: actual gates are QWEN_CODE_ENABLE_WORKFLOWS=1 (env),
  --experimental-lsp (CLI flag), and security.folderTrust.enabled (setting) — the prior
  workflowsEnabled/lsp.enabled/folderTrust keys did not exist
- /stats: restore the Session/Activity/Efficiency tab names (dashboard contents are not
  documented elsewhere); keep volatile keyboard hints out per the earlier review

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-25 06:58:10 +00:00
jinye
87ce3f47a3
feat(cli): Add skill usage stats (#5826)
* feat(cli): Add skill usage stats

Track live session skill invocations in daemon stats and expose /stats skills across interactive and non-interactive flows.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5826)

Add non-interactive skill stats tests for action rejection and prompt hook errors.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-25 00:10:55 +00:00
jinye
9254852211
feat(serve): Add daemon workspace voice and control APIs (#5765)
* feat(daemon): add setup-github route

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* feat(serve): add daemon workspace voice and control APIs

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address daemon voice review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address daemon voice review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): require auth for voice transcription

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): address voice persistence review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: fix CI failure on PR #5765

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): address daemon voice review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): ignore untrusted workspace proxy for setup-github

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve): address daemon workspace review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve): address daemon voice review followups

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve): address workspace voice review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(serve): address settings and git utility review feedback

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* test(serve): align permission cwd expectation

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix: address review feedback on settings logs and sdk types

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): Bound ACP workspace voice model input

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: fix CI failure on PR #5765

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5765)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-06-24 23:48:57 +00:00
jinye
a234860a4a
fix(core): Align MCP OAuth guidance and docs (#5589)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* docs: Align docs with current CLI behavior

Update stale documentation and user-facing MCP OAuth guidance to match the current dialog-based flows, SDK permission semantics, current links, and Qwen OAuth status.

Also replace Ink internal imports with public Ink APIs for the shared text input so the workspace builds against Ink 7.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: fix BaseTextInput Ink import (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* codex: address PR review feedback (#5589)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): surface MCP OAuth credential read failures

Fix SSE OAuth credential pre-check failures by reporting token storage read errors before connecting.

Update SDK coreTools docs and extension release link text from the follow-up review.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): harden MCP OAuth error handling

Handle stderr warning failures as best-effort and keep SSE 401 OAuth guidance when credential re-read fails.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): keep SSE OAuth pre-read best effort

Avoid blocking SSE MCP connections when the diagnostic credential pre-read fails, and cover BaseTextInput absolute-position edge cases.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): handle SSE OAuth validation errors

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): surface MCP OAuth recovery guidance

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): cover MCP OAuth retry paths

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): address OAuth guidance review

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-24 07:09:53 +08:00
Dragon
f1ef9d32b9
docs: fix config/command/auth drift and surface the model-providers page (#5735)
* docs: fix config/command/auth drift and surface model-providers page

Audit docs/ against the current code and correct the highest-impact drift:

- settings.md: move the mis-filed experimental.emitToolUseSummaries row into a
  new experimental section (cron/agentTeam/artifact/emitToolUseSummaries) and
  add general.language/outputLanguage/dynamicCommandTranslation and
  output.showTimestamps.
- commands.md: document /cd, /history, /voice, /import-config and the
  /model --voice and /model <model-id> forms.
- auth.md + model-providers.md: convert all modelProviders examples to the v5
  { protocol, models } object shape, correct the /auth menu (Alibaba
  ModelStudio / Third-party Providers / Custom Provider), fix the default
  OpenAI model (qwen3.5-plus), document the vertex-ai auth type, mark envKey
  optional, and use kebab-case --openai-api-key/--openai-base-url flags.
- overview.md + quickstart.md: rewrite the stale first-run auth flow; fix typo.
- configuration/_meta.ts: surface the orphaned model-providers page in the nav.
- qc-helper SKILL.md: add the 8 missing feature pages to the doc index.

* docs: resolve review feedback — fix provider-name and ModelStudio casing

Align docs with the code's provider labels and UI strings:
- Z.ai -> Z.AI (presets/zai.ts: label 'Z.AI API Key')
- iDeaLab -> Idealab (presets/idealab.ts: label 'Idealab API Key')
- 'Model Studio' -> 'ModelStudio' (UI flowTitle 'Alibaba ModelStudio'; no 'Model Studio' in code)

Applied across auth.md, overview.md, quickstart.md. Used --no-verify to avoid
lint-staged reformatting pre-existing, unrelated (non-CI-enforced) table padding
in auth.md; the five changed lines are individually prettier-clean.

* docs: resolve review feedback — /history subcommands, language type, jsonc fence

- commands.md: add missing '/history expand-on-resume' subcommand (historyCommand.ts registers collapse-on-resume, expand-on-resume, expand-now)
- settings.md: general.language Type string -> enum (settingsSchema.ts declares type: 'enum')
- model-providers.md: relabel the Example fence json -> jsonc (it contains // comments and two JSON docs)

--no-verify: avoids lint-staged re-padding pre-existing, unrelated (non-CI-enforced)
table columns; the three changed lines are content-only.
2026-06-24 06:06:01 +08:00
Shaojin Wen
c1eef15d4b
feat(cli): match MCP resource completions by name and discover servers (#5733)
`@server:` completion previously matched only the resource URI,
case-sensitively, and there was no way to reach a server without typing
its full name first.

- Match the partial after the colon case-insensitively against the
  resource's friendly name/title (`title || name`) as well as the URI,
  ranked URI-prefix > name-prefix > URI-substring > name-substring, and
  surface the name as the suggestion description.
- Before the colon, suggest configured MCP servers that expose resources
  and whose name prefixes the input, prepended to the file results (never
  hiding files). Selecting one expands to `@server:` and drills into the
  resource list (reuses the directory `isDirectory` continuation).

Refs #5601, follows #5635.
2026-06-23 11:25:51 +08:00
tt-a1i
3098717525
docs(mcp): correct mcp add scope default (#5593) 2026-06-22 22:15:17 +08:00
Shaojin Wen
f29f210201
feat(cli): browse MCP server resources in the /mcp dialog (#5635)
* feat(cli): browse MCP server resources in the /mcp dialog

The @server:uri resource reference (read + inject) shipped in #5544, but
the /mcp management dialog only surfaced a numeric "Resources: N" count —
there was no way to list a server's resource URIs, so a user who didn't
already know a URI couldn't discover what to reference.

Mirror the existing tools flow: add a "View resources" action on the
server detail step that opens a resource list, plus a detail view showing
each resource's URI, name, MIME type, size and description, along with the
exact @server:uri reference to paste into a message. Gated on
resourceCount > 0 (so untrusted folders, where MCP is never discovered,
show nothing). Adds en/zh/zh-TW strings, unit tests for both new steps,
and updates the MCP docs.

* fix(cli): wire MCP resource browser into the extensions manager + dedupe ref builder

Addresses review on #5635:

- [Critical] ServerDetailStep is shared with the extensions manager
  (McpServerActionsView), which doesn't pass onViewResources — so the new
  "View resources" action rendered there as a dead no-op. Gate the action on
  the optional handler being present (and add it to the useMemo deps), and
  implement the resource subview in McpServerActionsView so extension MCP
  servers get the same browser as the /mcp dialog.

- [Suggestion] Add a canonical buildMcpResourceRef() next to
  matchMcpServerPrefix() (its exact inverse) and use it for the @server:uri
  hint and the @server: autocomplete value, so the format has one source of
  truth; add a round-trip test.

- [Suggestion] Cover previously-untested paths: ResourceListStep scroll
  window (15 items: row windowing + ↑/↓ + count), ResourceDetailStep size:0
  and showName dedup, and ServerDetailStep action gating (incl. the
  dead-action regression).
2026-06-22 13:11:07 +00:00
Shaojin Wen
e5c01aa353
feat(mcp): support MCP resources and reliably surface prompts (#5544)
* feat(mcp): support MCP resources and reliably surface prompts

Prompts were silently hidden for MCP servers that implement `prompts/list`
but under-declare the `prompts` capability in their initialize response.
Drop the capability gate in `listMcpPrompts` (and apply the same leniency to
resources): always attempt the list call and swallow `Method not found`, so
those prompts now appear as `/` slash commands like in other clients.

Add first-class MCP resource support:

- core: `listMcpResources` / `discoverResources`, a `DiscoveredMCPResource`
  type, a `ResourceRegistry`, and `Config.getResourceRegistry()`. Discovery
  is wired into the standalone `McpClient.discover()` path and the
  connection-pool path (snapshot -> `SessionMcpView.applyResources` -> the
  session's registry, with a `resourcesChanged` event on reconnect). A
  resource-only server now counts as a successful discovery.
- cli: the `/mcp` dialog shows per-server Resources (and Prompts) counts.
- cli: `@server:uri` reads an MCP resource and injects its contents into the
  message (text inline, blobs as inlineData); `@server:` autocompletes the
  server's resource URIs. The `server` prefix must match a configured MCP
  server, so existing `@path` file references are unaffected.

Docs updated; unit tests added across core and cli.

* fix(mcp): reload commands on discovery + harden resource ref parsing

Address review feedback and complete the prompt UX:

- Slash commands now rebuild when an MCP server finishes connecting.
  Discovery is progressive (runs after the UI is interactive), so prompts
  a server exposes via prompts/list were registered too late to appear in
  the `/` menu — the `/mcp` dialog showed the count while the slash menu
  stayed empty. A debounced MCP-status listener now reloads the command
  tree on connect. Verified end-to-end in a real TUI against a mock server
  that under-declares the prompts capability: `/greet` now lists.
- `isMethodNotFound` keys off the JSON-RPC `-32601` code rather than the
  server-supplied message text (which may be localized or worded
  differently); applied to both `listMcpPrompts` and `listMcpResources`.
- `useAtCompletion` uses `Object.hasOwn` so `@__proto__:` / `@constructor:`
  and other inherited keys are not mistaken for configured servers.

* fix(mcp): lenient resource read to match discovery + review polish

- `McpClient.readResource` no longer prechecks `getServerCapabilities()
  ?.resources`. This PR is the first caller to make the read path
  reachable, and the strict precheck meant a server that answers
  `resources/read` but under-declares the `resources` capability — exactly
  the servers the lenient `listMcpResources` discovery targets — would get
  its resources discovered, listed in `/mcp`, and autocompleted, yet fail
  every `@server:uri` with a misleading "does not support resources". The
  read now matches discovery; a server that truly lacks resources answers
  `-32601`, surfaced as the existing error card. Added a regression test.
- `@server:uri` success cards now report what was injected ("Injected N
  chars" / "N attachments") or "(no readable content)" when a read yields
  no text/blob parts, so a partially-empty multi-ref read isn't hidden.
- `useAtCompletion` resource filter reduced to a single `includes` match
  (`startsWith` was subsumed; empty partial matches all via `includes('')`);
  the overstated "prefers prefix" comment is corrected.
- Tests: mixed `@file` + `@server:uri` injection (both parts + both cards),
  empty-content card, and the pool restart fan-out now asserts
  `applyResources` alongside `applyTools`.

* perf+security(mcp): parallelize discovery/reads, cap & frame resources

Review round 3 fold-ins:

- Discovery now runs `listMcpPrompts` / `listMcpResources` / `discoverTools`
  concurrently in `discoverAndReturn` (independent reads; the SDK client
  multiplexes by JSON-RPC id), saving per-server round-trips at startup.
- `@server:uri` resource reads run in parallel (`Promise.allSettled`) instead
  of serially, matching how the file path batches via `readManyFiles`; order
  is preserved so cards/labels line up with refs.
- Resource injection is now capped and framed: text is bounded by
  `MAX_MCP_RESOURCE_TEXT_CHARS` (100k) and oversized blobs are skipped, so a
  misbehaving/hostile server can't overflow the context window or OOM; the
  content is fenced with `--- Content from MCP resource <label> ---` /
  `--- End ---` delimiters so the model can separate untrusted server output
  from the user's prompt. The success card reports truncation.
- File-read error path now merges `resourceLabels` into `filesRead` /
  recording, so a resource read that succeeded before a file read failed is
  not dropped from the audit trail.
- `isMethodNotFound` (JSON-RPC -32601) now also covers `discoverTools` and
  `invokeMcpPrompt`, replacing the remaining message-substring checks.
- `PoolEntry.markActive` `initialResources` is now required (no `= []`
  default), removing a footgun where an omitted arg would wipe a server's
  resources via `applyResources([])`.
- `useAtCompletion` resource suggestions rank prefix matches above mid-string
  matches.
- `'Resources:'` added to the remaining 6 locales (ca, de, fr, ja, pt, ru)
  for parity with `'Prompts:'`.

Tests: attribution framing, text truncation, completion prefix ranking.

* test(mcp): cover resource registration in discover() and resource-only discovery

Closes the two coverage gaps flagged in review: assert discover() registers
discovered resources into the Config ResourceRegistry, and that a resource-only
server (no tools/prompts) is a successful discovery rather than throwing.

* fix(mcp): idempotent resource re-discovery + cumulative blob cap

Review round 4 (all Suggestions):
- discover() now clears a server's resources (removeResourcesByServer) before
  re-registering, so reconnect / incremental re-discovery is idempotent and a
  resource the server dropped doesn't linger in the registry (matches the
  pool path's SessionMcpView.applyResources).
- @server:uri injection now caps CUMULATIVE blob size per resource, not just
  each blob, so many sub-limit blobs in one response can't inject unbounded
  data. Added a test for the oversized-blob skip + card.
- Documented that file/resource content parts are grouped by type (model
  correlates by delimiter labels, not position).

* test(mcp): cover the MCP-status command reload (prompts surfacing in /)

Adds the missing coverage for the discovery-driven reload: a CONNECTED status
fires the listener and rebuilds the command tree (so progressively-discovered
MCP prompts appear as / commands), and a non-CONNECTED status does not.

* fix(mcp): don't wipe resources when resources/list transiently fails

- discover() only clears + replaces a server's resources when listMcpResources
  returns a non-empty set. Because that helper swallows all errors (including
  transient network failures) and returns [], an unconditional clear-then-
  register would silently purge a server's resources on a transient list
  failure while tools/prompts succeed. Guarding on length>0 keeps the existing
  set on failure; a real partial drop still re-registers the fresh set.
- Resource success card now shows '(truncated)' for capped/skipped blobs too,
  not just text. Added a cumulative-blob-cap test (two sub-limit blobs whose
  sum exceeds the cap).

* fix(mcp): guard pool applyResources against transient-failure wipe too

The non-pool discover() guard (resources.length > 0) left the pool path
exposed: on a restart, doRestart -> discoverAndReturn swallows a transient
resources/list failure to [], and applyResources([]) then wiped the session's
resources. applyResources is now a no-op on an empty snapshot (mirrors the
discover() guard; applyTools/applyPrompts keep their pre-existing clear-on-empty
behavior, out of scope). Added tests: applyResources([]) does not clear, and
discover() with an empty resource list does not call removeResourcesByServer.

* fix(mcp): preserve pool resource snapshot on transient restart failure

The applyResources([]) no-op only protected already-attached sessions; doRestart
still overwrote the pool entry's resourcesSnapshot with [] when the restart's
resources/list transiently failed, so any session attaching AFTER the restart
got zero resources. doRestart now only updates resourcesSnapshot when the
re-read is non-empty, preserving it for new and existing subscribers alike.
Tests: applyResources([]) preserves a pre-populated set; a restart whose
resources/list comes back empty still serves the prior resource to a new
session.

* fix(mcp): trust-gate resource completion, colon server names, narrower method-not-found

- useAtCompletion no longer surfaces resource URIs in an untrusted folder
  (the read path is already blocked there); avoids leaking resource existence.
- parseMcpResourceRef / getMcpResourceSuggestions match the LONGEST configured
  server name as a '<name>:' prefix instead of splitting on the first colon,
  so a server whose name contains ':' (a valid settings.json key) resolves.
- isMethodNotFound's message fallback is back to the case-sensitive exact
  'Method not found' substring (the -32601 code is the primary check), not a
  broad /method not found/i that would swallow unrelated errors.
Tests: @my:server:uri resolution, untrusted-folder completion.

* refactor(mcp): extract shared longest-prefix server matcher + doc/test fixes

- Extract matchMcpServerPrefix (new mcpResourceRef.ts) and use it from both
  parseMcpResourceRef (injection) and getMcpResourceSuggestions (completion),
  removing the duplicated longest-prefix logic and its drift risk.
- Update parseMcpResourceRef JSDoc to describe longest-prefix matching.
- Tests: shared-helper unit tests; the @my:server colon test now configures
  both 'my' and 'my:server' to exercise disambiguation; a colon completion
  test; isMethodNotFound message-casing tests (exact 'Method not found'
  swallowed, 'method not found handler' not swallowed).
2026-06-21 19:04:52 +08:00
tt-a1i
21998ee09d
fix(core): require opt-in for plan mode prompt (#5433)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
2026-06-20 17:53:57 +08:00
tt-a1i
73e6c7ef0f
fix(cli): clarify cumulative statusline token labels (#5400) 2026-06-19 17:39:31 +08:00
MikeWang0316tw
26ad36e95b
feat(cli): show follow-up suggestion in input placeholder (#5145)
* feat(cli): show follow-up suggestion in input placeholder

When enableFollowupSuggestions is true, display the generated
follow-up suggestion as the input placeholder text (replacing
the default "Type your message..."). Tab/Enter/Right arrow
accepts the suggestion; typing dismisses it.

Also change the default of enableFollowupSuggestions from false
to true so the feature is on by default.

Key changes:
- AppContainer: dismissPromptSuggestion no longer clears
  promptSuggestion state, preserving it for placeholder restore
  after user types then deletes
- InputPrompt: Tab/Enter/Right arrow/typing handlers check
  promptSuggestion prop as fallback when followup.state is not
  visible (e.g. after 300ms delay or user dismissed)
- Composer: placeholder shows suggestion text when available
- hasTabConsumer: include promptSuggestion to prevent Windows
  bare Tab from cycling approval mode

* chore: update settings.schema.json (enableFollowupSuggestions default: false → true)

* test(cli): add tests for promptSuggestion prop fallback paths (#5145)

- Add unit tests for Tab/Right arrow/Enter accepting promptSuggestion
  when followup.state.suggestion is null (type-then-delete path).
- Add unit test for hasTabConsumer reporting true immediately when
  promptSuggestion prop is set (no followup debounce needed).
- Update stale comment on speculation abort useEffect in AppContainer.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(cli): address PR #5145 review feedback for promptSuggestion

- Fix Enter key to fill buffer instead of submitting suggestion (matches
  Tab/Right-arrow behavior and Claude Code design)
- Add suggestionDismissed state to hasTabConsumer for Windows Tab cycling
- Fix suggestionDismissed to be set to true on user input (paste/typing)
- Add speculation abort to dismissPromptSuggestion callback
- Remove dead placeholder branch from Composer.tsx
- Update tests to reflect Enter no longer auto-submits suggestion

* fix(cli): address PR #5145 review from wenshao + telemetry gap

wenshao's review (posted after the previous fixes) flagged two issues,
both still valid against the current code; doudouOUC's telemetry gap
is addressed too.

- settings description: replace stale "Enter to accept and submit" with
  "Press Tab, Right Arrow, or Enter to accept into the input buffer" in
  both settingsSchema.ts and settings.schema.json (Enter now only fills
  the buffer, and the feature defaults to enabled).

- hasTabConsumer / handler consistency: drop the redundant
  `suggestionDismissed` state and gate hasTabConsumer on
  `buffer.text.length === 0` — the exact condition the Tab/Right/Enter
  handlers already use. Fixes the type-then-delete desync where Windows
  bare Tab would both insert the suggestion and cycle approval mode
  (regression of #4171).

- fallback telemetry: add a `fallbackText` option to the followup
  controller's accept() so the prop-fallback path (no live suggestion,
  e.g. within the show delay or after type-then-delete) routes through
  accept() and logs onOutcome instead of silently bypassing telemetry.
  Tab/Right/Enter handlers now call accept(method, { fallbackText }).

- tests: add core-level coverage for accept() with/without fallbackText,
  and fix the InputPrompt "fallback" tests that advanced 700ms (which
  silently exercised the normal visible-suggestion path) to advance only
  100ms so followup.state.suggestion truly stays null.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(cli): add accept_source telemetry + tests for promptSuggestion fallback

Follow-up to wenshao's second review pass on #5145.

- accept_source telemetry: fallback accepts report time_to_accept_ms: 0
  (the suggestion was never shown via the timer), which is indistinguishable
  from an instant accept. Add an `accept_source: 'live' | 'fallback'` field to
  the followup controller's onOutcome and PromptSuggestionEvent so analytics
  can tell the two apart. The controller derives it from whether a live
  `currentState.suggestion` was present before applying `fallbackText`.

- tests: assert accept_source on the fallback accept; add a test that a live
  suggestion takes priority over fallbackText (guards the `?? fallbackText`
  ordering); add an InputPrompt test pinning the new buffer.text.length === 0
  gate — hasTabConsumer reports false when a promptSuggestion is set but the
  buffer is non-empty (the old Boolean(promptSuggestion) gate wrongly reported
  true). The empty-buffer → true direction stays covered by the existing test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(cli): address doudouOUC review on #5145 (dedupe, rename, telemetry)

Three [Suggestion]-level items from the latest review pass.

- Extract `availableSuggestion`: the compound condition
  `(followup.state.isVisible || promptSuggestion) && (followup.state.suggestion ?? promptSuggestion)`
  was copy-pasted across the Tab/Right/Enter accept guards, both
  typing-dismiss guards, and the placeholder prop. Collapse them into one
  derived value so the sites can't drift apart. Behavior is unchanged
  (the controller keeps `isVisible` and `suggestion` in lockstep).

- Rename `dismissPromptSuggestion` -> `abortPromptSuggestion` across the
  UIState context, AppContainer, Composer, and the MainContent mock. The
  function only aborts in-flight generation/speculation and deliberately
  does NOT clear `promptSuggestion` (so the placeholder can restore it);
  the "dismiss" name implied the suggestion was gone.

- Omit `time_to_first_keystroke_ms` for fallback accepts. With
  `accept_source: 'fallback'` the suggestion was never shown via the timer
  (shownAt stayed 0), so `prevShownAtRef` still holds a previous
  suggestion's timestamp and the delta would be meaningless.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(cli): actually enable followup suggestions by default

PR #5145 changed the schema default to `true`, but `mergeSettings` never
applies SETTINGS_SCHEMA defaults, so the runtime `=== true` gates left the
feature off while the settings panel read it as on (verified by wenshao).

- Flip both runtime gates to treat an unset value as enabled — only an
  explicit `false` opts out: `AppContainer.tsx` and the ACP `Session.ts`
  (`#maybeEmitFollowupSuggestion`).
- Add a Session test for the unset/default-on path.
- Fix the stale `UIStateContext` JSDoc left over from the dismiss→abort
  rename (it no longer clears state).
- Docs: mark the feature on-by-default, correct Enter (fills the input,
  does not submit), ghost-text → placeholder text, and add a cost note that
  `fastModel` forks to a separate cache and can cost more than the default
  main-model + shared-cache path on long conversations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(core): reject control chars and ANSI escapes in prompt suggestions

The follow-up suggestion is influenceable through conversation history
(tool/file/web output) and is rendered verbatim in the input placeholder
now that enableFollowupSuggestions defaults to on. Raw control bytes (CR,
ESC/CSI, C1) reached the terminal because getFilterReason only rejected
newlines and asterisks. Reject them at the source so the displayed and
inserted text always match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(cli): clear promptSuggestion on submit and accept paths

Addresses doudouOUC review on #5145. Since abortPromptSuggestion was
changed to preserve `promptSuggestion` for type-then-delete restore, the
submit and accept paths leaked stale suggestion text:

- handleSubmitAndClear only called followup.dismiss(); after a synchronous
  command (/clear, /help) that never triggers AppContainer's streaming
  transition, the placeholder kept showing the old suggestion.
- Tab/Right/Enter accept never cleared the prop, so clearing the buffer
  without submitting (Ctrl+U) made the accepted suggestion reappear as a
  ghost placeholder.

Both now call onPromptSuggestionDismiss?.() after the followup action. Also
reuse the availableSuggestion single-source-of-truth in hasTabConsumer
instead of an inlined parallel expression, and add useFollowupSuggestions
tests asserting the accept_source guard suppresses time_to_first_keystroke_ms
on fallback accepts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(cli): assert promptSuggestion is cleared on accept and submit

Regression coverage for the state-leak fixed in 04fcffd1c (doudouOUC
Critical #1/#2, confirmed by wenshao's maintainer re-verification): Tab,
Right-arrow and Enter accepts plus message submit must each call
onPromptSuggestionDismiss, so the persisted promptSuggestion can't reappear
as a ghost placeholder when the buffer is next cleared.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 13:39:12 +08:00
曹潇缤
8fcc43943d
feat(channel): add QQ Bot (QQ机器人) channel adapter (#5202)
* feat(channel): add QQ Bot channel adapter

Add @qwen-code/channel-qqbot package implementing QQ Bot WebSocket
Gateway connection via the official QQ Bot API.

Supports:
- WebSocket Gateway (HELLO/IDENTIFY/HEARTBEAT/DISPATCH/RECONNECT)
- C2C single chat (C2C_MESSAGE_CREATE)
- Group @mention (GROUP_AT_MESSAGE_CREATE) — code path exists, unverified
- Streaming output via msg_id + msg_seq multi-block sending
- Auto-reconnect with exponential backoff
- Sandbox environment toggle

TODO (technical debt acknowledged):
- Group chat not verified end-to-end
- Single-file architecture (should split into gateway/send/auth modules
  like weixin channel)
- No tests (weixin has send.test.ts + media.test.ts)
- No typing indicator (onPromptStart/onPromptEnd not yet implemented)
- No channel instructions injection in connect()
- No structured error types

Closes #5201

* feat(qqbot): add QR login, group chat support with typed events

- Add QR code login via @tencent-connect/qqbot-connector with credential persistence
- Add Intent constants for C2C (1<<12) and GROUP_AT_MESSAGE (1<<25)
- Use QQGroupMessageEvent type in handleGroup instead of cast
- Remove resolved TODO comments for group chat verification
- Add msg_seq to send error log for debugging

* fix(qqbot): address PR review — lint errors, token refresh, security

- Use bracket notation for Record<string, unknown> to fix TS4111 lint errors
- Add chmodSync(credsFile, 0o600) for credential file permissions
- Implement token refresh at 80% TTL with expires_in tracking
- Fix RECONNECT opcode: use code 4000 + serverRequestedReconnect flag
- Fix connect() Promise: reject on close before READY via connectReject
- Log empty-token case in sendMessage, drain response body on error
- Clear chatTypeMap/replyMsgId/msgSeqMap in disconnect()
- Capture msgId at send-time to avoid race on replyMsgId
- Switch channel-registry.ts to Promise.allSettled (isolated channel failures)
- Add chatId validation (isValidChatId) to prevent SSRF

* fix(qqbot): add qqbot to build order, fix ESLint default-case

- Add packages/channels/qqbot to scripts/build.js buildOrder
  (CLI imports @qwen-code/channel-qqbot but it wasn't being built)
- Add default case to handleGatewayMessage switch

* feat(qqbot): prepend sender name in group messages for shared context

When sessionScope is set to 'thread', all group members share one
session. Prepending [senderName] helps the agent distinguish who
said what in the shared context.

* feat(qqbot): cross-server context continuation via SessionRouter persistence

- Persist SessionRouter mappings to disk via sessionsPath, surviving daemon restarts
- Persist QQ routing state (chatTypeMap, replyMsgId, msgSeqMap) to {name}-state.json
- Backup/restore global sessions.json on disconnect/connect to survive start.ts cleanup
- fixRestoredSessions() workaround for ACP LoadSessionResponse missing sessionId
- READY handler delays resolve() until restoreSessions() completes, preventing race

* feat(qqbot): add Session Resume + reconnect retry resilience

- Support WS session resume (RESUME opcode 6) on reconnect,
  falling back to full IDENTIFY when session is invalid
- Add reconnectWithRetry() loop: retries gateway fetch up to 5x
  with exponential backoff, then schedules 60s fallback retry
  (fixes silent death after GW HTTP 500)
- connect() now retries up to 3 times on initial failure
- Bump maxReconnectAttempts from 10 to 20
- Refresh token before each reconnect attempt

* fix(qqbot): address review feedback from wenshao

- fixRestoredSessions: use entry.target directly instead of tt.get(undefined)
  (fixes first restored session routing to wrong conversation when 2+ sessions)
- scheduleTokenRefresh: retry in 60s on token refresh failure, not just log
- sendMessage: move saveQQState() after chunk loop, avoid redundant disk I/O
- handleGroup: drop message when group_openid is missing instead of falling
  back to author.id (which would cause 404 on group message send)

* fix(qqbot): address 3rd review from doudouOUC (12 issues)

- QWEN_HOME: use getGlobalQwenDir() instead of homedir()
- name sanitization: prevent path traversal in file paths
- fetch timeouts: AbortSignal.timeout(15s) on all 3 fetch calls
- TOCTOU: writeFileSync with {mode: 0o600} instead of chmodSync after
- msg_seq gaps: only increment seq on send success, break on failure
- message dedup: seenMessages Map with 5min TTL cleanup timer
- disconnect: set disposed flag + flushQQState sync + clear timers
- heartbeat ACK: track lastHeartbeatAck, force close on 2x interval timeout
- reconnect exhaustion: FATAL log when max attempts reached post-connect
- debounced saveQQState: 500ms debounce, flush on disconnect
- handleGroup: skip [senderName] prefix for slash commands, log for audit
- disposed guard: connectGateway checks disposed before creating WS

* fix(qqbot): robustness round — RESUMED, token expiry, SSRF, disposed, typing stubs

- Handle RESUMED event on RESUME success (start heartbeat, restore sessions)
- Check token expiry before sendMessage, refresh if expired
- Tighten isValidChatId regex (remove . and /) to close path traversal
- Reset disposed flag in connect() for reusability
- Add onPromptStart/onPromptEnd stubs (QQ Bot has no typing API)
- Add robustness comments for splitText surrogate pairs, restoreQQState
  corruption, and senderId identity fragmentation across contexts

* refactor(qqbot): split into modules — api, accounts, login

Extract HTTP calls, credential I/O, and QR login into separate files
matching the weixin channel's architecture:

- api.ts: fetchAccessToken, fetchGatewayUrl, getApiBase, sendQQMessage
- accounts.ts: getCredsFilePath, loadCredentials, saveCredentials
- login.ts: qrCodeLogin (qrConnect wrapper)

QQChannel.ts drops inline fetch/credential/qrConnect logic and imports
from the new modules. Net -41 lines in the adapter.

* feat(qqbot): markdown message support (msg_type: 2)

Detect markdown syntax in AI responses and send as msg_type=2
with markdown.content field instead of plain-text msg_type=0.

Detection covers headers, code blocks, bold, italic, strikethrough,
inline code, links, and lists via a single regex.

* fix(qqbot): defensive patches from complete review

- reconnectWithRetry: guard against disposed channel to prevent infinite loop
- handleGroup: broaden @mention regex to match both legacy <@!id> and V2 <@openid>
- handleGroup: set isReplyToBot=true (every group msg is an @mention)
- fixRestoredSessions: document fragile private-field access
- saveCredentials: correct TOCTOU claim in comment
- hasMarkdownSyntax: document false-positive trade-off

* fix(qqbot): guard against empty content in C2C and group handlers

- handleC2C: return early when event.content is null/empty (image/sticker msgs)
- handleGroup: return early when cleanText is empty after @mention stripping

* fix(qqbot): close remaining review gaps — disposed guard, connectReject, token retry, RESUMED restore

* fix(qqbot): address wenshao review — RESUME restore removal, disposed guards, timer tracking, logging, heartbeat floor, requiredConfigFields, channel-registry error labels

* fix(qqbot): markdown fallback to plain text on rejection

* docs(qqbot): clarify markdown permission — Open Platform has no gate, FAQ is a different platform

* feat(qqbot): add Ark (msg_type=3) and Media (msg_type=7) message support

- types.ts: ArkKV, ArkPayload, FileType, MediaUploadRequest/Response, MediaPayload
- api.ts: uploadQQMedia() — file upload for rich media
- QQChannel.ts: sendArk(chatId, templateId, kv) + sendMedia(chatId, fileType, url, text?)
  - C2C/group upload paths separated (file_info not interchangeable)
  - file_type=4 (文件) blocked for groups per QQ API
  - Embed (msg_type=4) skipped — QQ频道专用, not available for Bot Open Platform

* feat(qqbot): auto-route !ark / !media commands from LLM text via sendMessage

LLM outputs text — the channel now parses structured commands inline:
  !ark(24, #TITLE#=标题, #META_DESC#=描述)
  !media(image, https://example.com/photo.jpg, caption text)

parseArkCommand / parseMediaCommand extract at sendMessage entry;
normal text/markdown flow unchanged.

* feat(qqbot): inject channel instructions for ark/media commands

Sets config.instructions on connect() so the LLM learns about:
  !ark(template_id, key=val, ...) — 3 default templates (23/24/37)
  !media(type, url, [caption])   — image/video/voice/file

Fixes known debt: 'No channel instructions'.

* feat(qqbot): gate ark/media behind config flags (enableArk/enableMedia)

Both features default to false — opt-in via settings.json:
  channels.my-qq.enableArk = true
  channels.my-qq.enableMedia = true

Instructions injected conditionally; command routing gated per-flag.

* refactor(qqbot): extract resolveRoute() to eliminate duplication across sendMessage/sendArk/sendMedia

disposed check, token refresh, chatId validation, sandbox path selection
now in one place. All three methods call resolveRoute() instead of
repeating the same 15-line preamble.

* chore(qqbot): remove Ark and Media message support

Remove !ark() / !media() text parsing, sendArk/sendMedia methods,
uploadQQMedia, and all related types. The text-parsing approach
was too fragile against LLM output formatting. Only text/markdown
messaging remains.

* fix(qqbot): robustness patches for review findings

- Add { mode: 0o600 } to all writeFileSync calls (state/session files)
- Guard against stale WebSocket close event nuking new connection
- Add isReconnecting guard to prevent parallel reconnectWithRetry chains
- Reset isReconnecting flag in READY, RESUMED, and exhaustion paths

* docs(channel): add QQ Bot user documentation

Add user-facing documentation for the QQ Bot channel adapter:

- New docs/users/features/channels/qqbot.md covering setup, configuration,
  QR code login, group chat, Markdown support, token management, connection
  resilience, and troubleshooting
- Update docs/users/features/channels/_meta.ts to include QQ Bot in nav
- Update docs/users/features/channels/overview.md to reference QQ Bot
  across the intro, quick start, type options, slash commands, and the
  media platform differences table

* docs(qqbot): fix prerequisites — QR login needs no developer account

QR code login via qrConnect() does not require a developer account or
manual app registration. First qwen channel start is all you need.

* docs(qqbot): emphasize QR login, keep developer portal as secondary path

Both paths work (config → persisted file → QR scan), confirmed against
fetchToken() code. Reposition QR code login as the primary setup flow,
remove redundant tips/troubleshooting entries.

* docs(qqbot): remove Images and Files section — not supported in channel code

handleC2C/handleGroup both skip messages with no text content.
No media download or upload logic exists in this channel adapter.

* test(qqbot): add unit tests for send utilities

Add vitest test suite for QQ Bot channel following the weixin channel
testing patterns. Extract isValidChatId, hasMarkdownSyntax, and splitText
as exported module-level functions to enable direct testing.

- 27 tests covering: chatId SSRF validation, Markdown syntax detection, and
  text chunking for QQ's 2000-char message limit
- Add vitest.config.ts and test script to qqbot package
- Register qqbot in root vitest workspace projects

Refs: #5202

* test(qqbot): add sendMessage flow tests with mocked API

Follow the weixin sendImage test pattern: mock sendQQMessage and
channel-base dependencies to test sendMessage end-to-end.

- C2C/group routing verification
- Markdown msg_type=2 vs plain text msg_type=0
- Markdown rejection fallback to plain text
- Disposed guard and error-stop behavior
- msg_id + msg_seq tracking for multi-chunk streaming

9 new tests, 36 total (all passing)

* test(qqbot): fix review issues — add missing edge cases

Self-review fixes:
- Fix misleading test name: 'returns early when chatId not in chatTypeMap'
  → 'defaults to C2C path for unknown chatId' (code doesn't return early)
- Add SSRF validation test: sendMessage rejects '../traversal' chatId
- Add network error test: thrown sendQQMessage caught by try/catch
- Add token expiration test: expired token + failed refresh → early return
- Hoist mockFetchAccessToken and set default resolved value in beforeEach
  to prevent silent undefined-access failures in accidental token-refresh paths

39 tests, all passing

* test(qqbot): add api and accounts unit tests

Add api.test.ts (13 tests) and accounts.test.ts (8 tests) following
weixin channel vitest patterns: vi.hoisted() mocks, vi.mock() module
replacement, and dynamic import() after mock setup.

api.test.ts covers getApiBase, sendQQMessage, fetchAccessToken, and
fetchGatewayUrl — including HTTP errors, missing fields, and request
body format.

accounts.test.ts covers getCredsFilePath, loadCredentials (missing file,
corrupt JSON, missing fields, valid data), and saveCredentials (dir
creation + 0o600 permissions).

All 60 tests pass (39 existing + 21 new). tsc --build and eslint clean.

* chore(qqbot): suppress CodeQL ReDoS false positives

Add codeql[js/polynomial-redos] suppression comments for two
regexes flagged by CodeQL:

- hasMarkdownSyntax(): input is LLM-generated reply text,
  never attacker-controlled in Qwen Code Channel context.
- handleGroup(): <@...> prefix is injected by QQ servers;
  openid is assigned by QQ, not attacker-chosen.

Both paths have no practical exploit vector — an adversary
would need to either control an LLM's output or register a
malicious openid with QQ, neither of which is achievable.

* fix(qqbot): allow QR-code-only login and guard qrConnect return

- requiredConfigFields: [] — fetchToken() already resolves credentials
  from config → persisted file → QR fallback chain. Blocking at config
  validation prevented QR-code-only users from starting the channel.
- qrCodeLogin(): add bounds check for empty qrConnect() return value.
  If the external library returns an empty array, throw descriptive
  error instead of crashing with TypeError on creds.appId.

* chore(qqbot): add comments for requiredConfigFields and qrConnect guard

- index.ts: explain why requiredConfigFields is empty — fetchToken()
  already resolves credentials via config → file → QR fallback chain.
  Requiring appID/appSecret at config level would block QR-only users
  from reaching the fallback through the built-in channel path.

- login.ts: clarify qrConnect() guard is a defensive robustness patch,
  not a response to an observed failure. Verified by removing appID
  from config and running qwen channel start — QR login triggers
  correctly and returns valid credentials.

* fix(qqbot): replace quadratic regexes with linear patterns, remove failed suppress comments

* fix(qqbot): split hasMarkdownSyntax into individual tests to pass CodeQL

* fix(qqbot): replace markdown link regex with indexOf to eliminate CodeQL ReDoS
2026-06-19 06:32:52 +08:00
Shile Zhang
daec44a4f3
feat(hooks): pass original API call ID (toolCallId) to hook system (#4918)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
- Add toolCallId field to PreToolUse/PostToolUse/PostToolUseFailure hook inputs
- Thread toolCallId from LLM API response (toolCall.id) through to hook subprocess stdin
- Fix message bus handler in config.ts to forward toolCallId (was silently dropped)
- toolCallId is optional — only included when the LLM provider supplies an ID

Signed-off-by: Shile Zhang <shile.zhang@linux.alibaba.com>
2026-06-18 08:20:38 +00:00
Dragon
5c87b5649e
docs: fix SSE ring size errors and add /workflows command (#5205)
* docs: fix SSE ring size errors and add /workflows command

- qwen-serve.md: fix SSE ring size (4000 → 8000) in 4 locations
- qwen-serve-protocol.md: fix SSE ring size (4000 → 8000)
- commands.md: add /workflows command to tool management table

The SSE ring default is 8000 frames (not 4000) per the implementation
in packages/cli/src/serve/server.ts. The /workflows command inspects
workflow runs from the WorkflowRunRegistry.

* docs: fix missed SSE ring size reference (4000 -> 8000)

Address review feedback on #5205: daemon-client-quickstart.md still
referenced the old 4000-event ring buffer. The daemon ring buffer
default is DEFAULT_RING_SIZE = 8000 (packages/acp-bridge/src/eventBus.ts).

* docs: resolve circular ring-size wording in Stage 1.5 list

Address review feedback on #5205: after the 4000->8000 correction, item 5
read circularly (default 8000 ... need 8000+). The ring default has shipped
at 8000 (DEFAULT_RING_SIZE / eventRingSize default), so mark the size bump
done (strikethrough, matching item 3) and scope remaining work to
per-session configurability, which is still open (eventRingSize is a single
daemon-construction value, not settable per session).
2026-06-18 09:43:38 +08:00
Dragon
b6febe0856
docs: add CLI subcommands section with qwen sessions list (#5254)
- Add section 5 to commands.md for CLI-level subcommands
- Document qwen sessions list with --json and --limit flags
- Include output format, examples, and usage patterns

The sessions list command was added in commit 14e6ae8c2 but not documented.
2026-06-18 08:59:09 +08:00
joeytoday
f27e0afa45
docs(channels): add screenshots to Feishu setup guide (#4983) 2026-06-16 04:34:03 +00:00
Dragon
3408c32110
docs: fix MCP token path, daemon UI event count, add Feishu channel (#5172)
* docs: fix MCP token path, daemon UI event count, add Feishu channel

- mcp.md: update OAuth token storage path to v2 encrypted file
  (mcp-oauth-tokens-v2.json with AES-256-GCM, keychain preferred)
- daemon/00-index.md: fix UI event type count (36 → 37)
- daemon/01-architecture.md: add Feishu to channel bots diagram
- daemon/15-channel-adapters.md: add Feishu adapter (WebSocket/webhook,
  config keys, permission UX, reverse-call caveat)

* docs: correct Feishu config keys and permission UX caveat

Address review feedback on 15-channel-adapters.md:
- Feishu config keys are clientId/clientSecret (not appId/appSecret),
  per FeishuAdapter.ts:116-118; add encryptKey (required for webhook
  mode, enforced at FeishuAdapter.ts:165-168).
- Note that channel permission UX currently auto-approves via AcpBridge
  (AcpBridge.ts:111); interactive approval is planned.

* docs: fix missed sibling references for audit consistency

Address review feedback (wenshao) — apply the same corrections to other
doc locations that referenced the stale values:
- MCP token path: also fix docs/developers/tools/mcp-server.md (was still
  ~/.qwen/mcp-oauth-tokens.json).
- UI event type count 36 -> 37: also fix 14-cli-tui-adapter.md (3 spots),
  13-sdk-daemon-client.md.
- Feishu channel: add Feishu rows to both tables in 15-channel-adapters.md
  (Per-channel adapters + Adapter matrix) and the package-level Adapters
  subgraph in 01-architecture.md, so prose/tables/diagrams all agree.
- Add a note under the Adapter matrix clarifying that Permission UX /
  approvalMode are not wired yet (all channels auto-approve via AcpBridge).
2026-06-16 11:37:43 +08:00
Shaojin Wen
e97be6c669
fix(agent): stop forking result-bearing work; keep omitted subagent_type awaitable (#5155)
Enabling fork-by-default for every interactive session (#4963) made an omitted `subagent_type` mean a fire-and-forget background fork. A fork's findings never flow back into the parent turn, so any caller that spawns parallel agents and then aggregates their results breaks — the bundled `review`/`simplify` skills, third-party skills, and the model's own delegation. The orchestrator waits forever for results that never arrive; nudged by the periodic todo reminder it can also spin on identical tool calls until DashScope rejects the request with `InternalError.Algo.InvalidParameter: Repetitive tool calls detected` (HTTP 400).

Make forking an explicit, deliberate choice and stop steering the model toward it when it needs the results:
- Dispatch: `subagent_type: "fork"` selects a fork (interactive only; otherwise general-purpose). Omitting `subagent_type` always resolves to the awaitable general-purpose subagent whose result returns inline — never a fork. `/fork` passes `subagent_type: "fork"` explicitly.
- Prompt: drop the "launch parallel forks for research" guidance, reframe the tool description and "When to fork" around "never fork work whose output you need", and stop advertising `fork` in the subagent_type enum (it stays valid via validation for `/fork` and intentional use).
- Skills: `review` and `simplify` launch their parallel agents with `subagent_type: "general-purpose"` and are told explicitly not to fork, since they aggregate findings.

Forking stays a first-class, default-available feature for genuinely fire-and-forget work — it just isn't the silent default, an enum pick, or something the model is nudged toward when it must read the results.
2026-06-16 09:38:10 +08:00
ChiGao
91476134ae
fix(dual-output): prevent FIFO blocking on startup when no reader connected (#4894)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* fix(dual-output): prevent FIFO blocking on startup when no reader connected

DualOutputBridge's ENXIO fallback used a blocking createWriteStream on
FIFOs, causing the TUI to hang indefinitely when launched with
`--json-file <fifo>` before a reader connects (issue #4727).

Fix: use O_RDWR | O_NONBLOCK for the FIFO fallback path. This POSIX
trick satisfies the kernel's "at least one reader" requirement without
blocking. A buffer high-water-mark (1 MB) self-disables the bridge if
no consumer ever drains the pipe.

Also updates Quick start docs to recommend regular files as the default,
with FIFOs documented as an advanced option that now works without
ordering constraints.

Generated with AI

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(dual-output): address review feedback — guardActive, stream.destroy, tests

- Rename isBufferOverflowing() to guardActive() (command-query separation)
- Apply buffer guard to all write methods, not just processEvent
- Call stream.destroy() on overflow so FIFO consumers get EOF
- Handle destroyed stream in shutdown() to prevent hanging
- Add test: bridge disables on buffer overflow + stream is destroyed
- Fix doc: --input-file requires regular file (not FIFO), stat.size=0

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>
2026-06-15 06:04:06 +08:00
kkhomej33-netizen
3a224d1efe
feat(skills): support user-invocable frontmatter (#5037) 2026-06-13 06:09:23 +08:00
qqqys
4ae788623e
feat(core,cli): bubble background subagent permission prompts to the parent session (#4955)
* feat(core,cli): bubble background subagent permission prompts to the parent session

Background subagents auto-deny any tool call that needs interactive confirmation, so a single permission-gated step (a git push, an rm, a network call) silently fails and the work bounces back to the parent turn — defeating the point of backgrounding. This adds an opt-in approvalMode value for subagent definitions, `bubble`: instead of denying, the call is parked on the BackgroundTaskRegistry and surfaced in the Background tasks dialog, where the user answers it through the shared ToolConfirmationMessage; the agent then resumes.

- `bubble` is a subagent-only approvalMode (deliberately not a session-level ApprovalMode value); it resolves to `default` run behavior and only flips the background path from deny to surface, in interactive sessions. Headless / non-interactive contexts keep auto-deny.
- BackgroundTaskRegistry grows a parked-approval queue (add/resolve/clear), an approval-change callback, and an event bridge (TOOL_WAITING_APPROVAL parks, TOOL_RESULT clears stale prompts). Every terminal transition auto-rejects parked calls so the agent loop never hangs on an unanswerable prompt; cancel() rejects before aborting so respond(Cancel) actually fires ahead of the abort-driven queue clear. Auto-reject failures are caught on the promise, not via try/catch around a voided async call.
- The launch path (agent.ts) and the resume path (background-agent-resume.ts) share the same gate, so a resumed agent of the same definition keeps bubbling instead of silently reverting to auto-deny.
- TUI: the footer pill shows a "needs approval" marker, dialog list rows are flagged, and the detail view embeds the confirmation prompt. While a prompt is up, left (back) and x (stop agent) remain available as escape hatches so a re-parking agent cannot trap the keyboard.

Closes #4928

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(i18n): add zh-TW translations for background approval strings

check-i18n requires every zh key to have a zh-TW counterpart; the three
strings added for permission bubbling were registered in en/zh only.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): harden background approval edge cases from review

- resolvePendingApproval: if respond() rejects, the tool call is still parked in the scheduler, so re-add the approval and re-emit instead of silently clearing the prompt (which left the UI showing nothing pending while the agent hung). Returns false on failure.
- reset() and finalizeCancellationIfPending(): reject parked approvals defensively. The /resume and /clear paths already gate on hasBlockingBackgroundWork() so these only run on terminal entries today, but rejecting here means a future caller dropping that guard can't strand an unanswered respond() callback.
- resolveSubagentApprovalMode: resolve the subagent-only 'bubble' mode to Default explicitly rather than via approvalModeToPermissionMode's default fall-through, so a future ApprovalMode.BUBBLE enum member can't silently change it.

Adds tests for the fail / finalizeCancelled / reset auto-reject paths and the respond()-rejection re-park.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(core): include args in approval test events

* test(core): update nested yaml parser expectations

* fix(cli): reuse selected background agent id

* fix(core): fail consumed background approval retries

* fix(core): prevent persistent bubbled approvals

* fix(core): harden bubbled approval handling

* fix(core): cover background approval edge cases

* fix(cli): isolate bubbled question approval keys

* fix(cli): localize background approval labels

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-13 01:50:26 +08:00