Commit graph

6250 commits

Author SHA1 Message Date
Shaojin Wen
6677ca1dd3
feat(web-shell): per-task token & time detail on completed todos (#5118)
* feat(web-shell): per-task token & time detail on completed todos

Expanding a completed task in the todo list now reveals when it ran (start /
end / duration) and what it spent: input / output / cached tokens, API time,
and tool time.

The agent stamps a cumulative-usage snapshot onto each todo (plan) update via
`_meta.stats`; the SDK normalizer carries it into the TodoWrite tool call's
rawOutput, and the web-shell diffs consecutive snapshots for tokens and API
time while summing transcript tool durations for tool time.

Works live (no polling race) and on /resume: tokens and tool time are
reconstructed from persisted usage metadata, while API time is live-only since
per-turn durations are not replayed. Sessions whose agent never stamped a
snapshot degrade gracefully to start/end + tool time.

* refactor(web-shell): show task duration inline on the end-time row

Trail the elapsed duration after the end time as a dimmed parenthetical
("12:34:15 (4m 14s)") instead of a separate row, since it's derived from the
start/end pair.

* fix(web-shell): correct per-task detail for reused todo ids; review follow-ups

- computeTodoDetails: when a completed id+content key restarts as in_progress (positional plan-N ids repeat across plans), reset the window so the new task diffs its own start instead of the prior task's far-earlier boundary — which rendered a cross-plan window with wildly inflated token/time numbers. Correct the todoStateKey JSDoc accordingly.
- Tool time: sort spans once and binary-search the task window instead of an O(todos x spans) scan per completed task.
- Tests: add the reuse-reset case, a windowed tool-time case, an SDK-normalizer -> extractTodoStats contract test (locks the stats passthrough so a field rename fails loudly), and a stopPropagation test (expander click must not bubble to the tool-row header).

* fix(web-shell): reset todo detail window on reopen via pending, not just direct

Track keys that have ever reached 'completed' instead of checking prev === 'completed': a reopened task can pass through 'pending' (completed → pending → in_progress), where prev at the re-activation is 'pending' and the direct check missed it, leaving a stale baseline that diffs across both runs. A pause/resume that never completed (in_progress → pending → in_progress) still keeps its first baseline, so its diff captures the whole task.

* fix(web-shell,cli): harden todo stats against NaN poisoning and partial snapshots

- MessageEmitter: only fold finite usage/duration values into the cumulative accumulator. A NaN/Infinity (incl. a NaN that survives `?? 0`) would poison the running total forever, making every later snapshot fail extractTodoStats and silently show 'not captured' for the rest of the session.
- extractTodoStats: require the token fields but default the live-only apiTimeMs to 0 when absent/non-finite, so a snapshot that omits it keeps its valid token counts instead of being dropped whole.
- computeTodoDetails: gate the start baseline on the stored value, not Map.has — a stats-less start (e.g. a plain plan message) recorded undefined, which Map.has treated as already-set, blocking a later stats-bearing snapshot from upgrading the baseline.
- Document the MessageEmitter-before-PlanEmitter ordering invariant in both emitters.
2026-06-15 15:40:06 +08:00
Dragon
a969c84620
fix(desktop): isolate update feed from CLI releases (#5139) 2026-06-15 15:32:39 +08:00
jinye
57a90f7302
fix(core): Bound active tool result history (#5111)
* fix(core): bound active tool result history

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

* fix(core): keep tool result budget defaults lightweight

Move the new tool-result history budget default into a lightweight config defaults module so microcompaction does not load the full Config graph during service tests. Update the ACP worktree test mock to include the public default export used by settings schema imports.

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

* fix(core): address tool result budget review

Handle negative legacy idle thresholds consistently, clarify size compaction diagnostics for pending tool results, promote successful microcompaction logs to info, and strengthen tests/docs around skipped results and soft thresholds.

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

* fix(core): log protected tool result overages

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-15 15:29:12 +08:00
顾盼
bb1e71911c
feat(computer-use): configurable screenshot max dimension (setting + env) (#5122)
* feat(computer-use): configurable screenshot max dimension (setting + env)

Add a user-level knob for cua-driver's screenshot longest-edge cap. The
old open-computer-use backend exposed this via OPEN_COMPUTER_USE_IMAGE_*
env vars; the cua-driver migration dropped them, leaving only the
model-driven set_config tool. This restores deterministic user control.

- Setting tools.computerUse.maxImageDimension (number; default -1 = keep
  cua-driver's built-in default of 1568; 0 disables resizing / full
  resolution; a positive value caps the longest edge).
- Env override QWEN_COMPUTER_USE_MAX_IMAGE_DIMENSION (takes precedence
  over the setting; invalid/negative values fall through).
- Resolution lives in resolveMaxImageDimension(); applied via the
  cua-driver set_config tool once per (re)connect in
  ComputerUseClient.doStart — best-effort, never aborts startup, and
  re-applied after a daemon-restart reconnect.
- Docs: document tools.computerUse.{enabled,maxImageDimension} in
  settings.md (the block was previously undocumented). Refresh stale
  ocu/npx comments left in client.ts + install-state.ts by the migration.

Precedence: env var > setting > cua-driver default.

* chore(computer-use): finish ocu→cua-driver cleanup in schema-sync script

The cua-driver migration (#5051) left scripts/sync-computer-use-schemas.ts
pointing at the old open-computer-use backend: it npx'd
@qwen-code/open-computer-use, hard-coded the 9-tool ocu surface, and emitted an
"open-computer-use" header. Re-running it — which constants.ts' version-bump
procedure tells maintainers to do — would have clobbered the migrated 35-tool
cua-driver schemas.ts.

- Drive the locally-pinned `cua-driver mcp` binary (binaryPath /
  CUA_DRIVER_VERSION from constants.ts) instead of npx'ing ocu; expect 35
  tools and warn (don't fail) on drift.
- Emit the cua-driver-flavored schemas.ts header.
- Refresh install-state.test.ts fixtures from ocu package specs to the
  cua-driver-rs approval-key form the field actually stores now.

Verified the fixed script reproduces the committed 35-tool surface exactly
(modulo prettier formatting). No dead env-var handling remained — the module
reads only QWEN_COMPUTER_USE_{AUTO_APPROVE,DOWNLOAD_HOST,MAX_IMAGE_DIMENSION}.
2026-06-15 15:25:27 +08:00
pomelo
5715b3450d
docs: rewrite CLAUDE.md to point to AGENTS.md as authoritative source (#5138) 2026-06-15 15:23:26 +08:00
Shaojin Wen
a74f70b3d6
fix(core): honor skipLoopDetection for the deterministic tool-call loop (#5128)
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
#5036 carved the deterministic identical-tool-call check out of the
`model.skipLoopDetection` gate, turning it into a hard-stop that fires
even when loop detection is disabled. Because `skipLoopDetection`
defaults to true (settingsSchema: "to avoid false-positive
interruptions"), this silently re-enabled loop halts for the default
configuration and broke the documented escape hatch — the
non-interactive guidance in nonInteractiveCli.ts told users to set
`model.skipLoopDetection: true`, which no longer disabled the halt and
is unreachable in non-interactive mode (no disable dialog).

Gate both the deterministic and heuristic detector paths behind the
single flag again. The deterministic split, retry-reset, and pending
tool-call splice introduced by #5036 still apply once detection is
explicitly enabled (skipLoopDetection: false), so the runaway guard
remains available as opt-in without overriding the default-off contract.
2026-06-15 15:04:42 +08:00
qqqys
02bd82abfd
ci: add scheduled autofix workflow for stale bug issues (#4989)
* feat(workflow): add Qwen Scheduled Issue Autofix workflow for automated issue handling

* fix(ci): harden scheduled autofix workflow

* fix(ci): harden scheduled autofix recovery

* fix(ci): keep autofix cleanup best effort
2026-06-15 09:43:27 +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
JerryLee
c70eed353b
fix(core): include response tokens in prompt estimate (#4525)
* fix(core): include response tokens in prompt estimate

* fix(core): avoid double-counting reasoning output tokens

* fix(core): restore response token estimate on resume

* test(core): cover output token reset paths

* refactor(core): share output token estimate helper

* refactor(core): seed resume output tokens atomically

* fix(core): tighten resume output token accounting

* fix(core): count equal reasoning tokens conservatively

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: JerryLee <Jerry2003826@users.noreply.github.com>
2026-06-15 02:45:05 +08:00
Yufeng He
4748705bc8
fix(core): ignore agent names without active teams (#5115) 2026-06-15 02:39:32 +08:00
jinye
75e8f259c4
docs: Refresh daemon developer docs (#4412)
* docs: Refresh daemon developer docs

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

* docs: Address daemon review feedback

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

* docs: Address daemon review suggestions

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

* codex: address PR review feedback (#4412)

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

* codex: address PR review feedback (#4412)

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

* codex: address PR review feedback (#4412)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-15 02:01:37 +08:00
JerryLee
d979e3d58c
fix(core): compress when usage metadata is missing (#4528)
* fix(core): compress when usage metadata is missing

* fix(core): harden missing compression usage fallback

* test(core): cover missing-usage inflated compression fallback

* fix(core): clarify missing usage compression fallback

* test(core): align compression fallback assertion with summary trailer

* test(core): use ascii cjk fixture escape

* test(core): tighten missing-usage compression assertions
2026-06-15 01:46:08 +08:00
JerryLee
4abc8ff360
fix(core): bound foreground shell output capture (#4524) 2026-06-15 01:45:18 +08:00
JerryLee
f81c147d00
fix(core): bound hard rescue compression retries (#4526)
* fix(core): bound hard rescue compression retries

* fix(core): count rejected hard rescue noops

* fix(core): clarify hard rescue retry accounting

* test(core): cover hard rescue reactive fallback

* test(core): remove duplicate debug logger mock

* fix(core): include hard rescue count in stop log
2026-06-15 01:44:43 +08:00
Yufeng He
e043587d24
fix(core): keep token escalation warm across agent rounds (#5062)
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
2026-06-15 00:56:54 +08:00
Yufeng He
e2fc1616de
fix(core): hard-stop repeated identical tool calls (#5036) 2026-06-15 00:47:03 +08:00
Yufeng He
5689d29b58
test: stabilize simple MCP integration check (#5072) 2026-06-15 00:45:57 +08:00
Shaojin Wen
9f2168f78e
feat(web-shell): collapsible TodoWrite history with status diff (#5109)
* feat(web-shell): collapsible TodoWrite history with status diff

Inline todo_write updates rendered as a generic, non-collapsible tool row
crammed in with surrounding tool calls — the todo-specific renderer was
effectively dead code because the detector matched the literal "todowrite"
while the wire name is "todo_write" (kind "think").

- Detect the todo tool by name (todo_write / todowrite) instead of relying
  on the unrelated tool kind, reviving the rich rendering and also
  populating the floating todo panel that was empty on the daemon path.
- Render each update as its own standalone group, collapsed by default to
  the per-snapshot diff (just-completed / just-started items) or the current
  step, expanding to the full checklist; the header shows completed/total.
- Use consistent status glyphs (●/◐/○) in both collapsed and expanded views
  via a shared TodoView component used by ToolGroup and PlanMessage.

* fix(web-shell): scope todo diff per task identity; address review

- [Critical] computeTodoTimeline keyed its running state on todo.id alone, but
  ids aren't globally unique (ACP assigns positional ids, models renumber per
  plan), so a later plan diffed against a previous plan's stale terminal status
  and silently dropped events in the collapsed view. Key on id+content so
  distinct tasks stay separate; add an id-reuse regression test.
- Stabilize the TodoTimelineContext value with a signature-cached Map so
  streaming ticks that don't touch a todo snapshot no longer re-render every
  todo/plan row.
- Drop the unused completed/total from TodoSnapshotDiff (consumers compute the
  count locally — single source of truth).
- Fall back to the raw result summary when a todo_write payload is unparseable.
- Add PlanMessage rendering tests and extractTodosFromToolCall coverage; remove
  stale "step time" comments left after dropping per-step timing.

* test(web-shell): document todo-diff keying limits; cover signature

Follow-up to review on the id+content keying in computeTodoTimeline:
- Document the two rare trade-offs in the todoStateKey doc (a mid-task reword on
  a stable id, and unrelated plans reusing both id and content), both degrading
  to "the collapsed diff omits one event" while the expanded list stays correct.
- Pin behavior with tests: an item carried over and completed in a later turn
  (which id+content handles but a user-turn reset would drop), plus the two
  documented gaps.
- Add todoTimelineSignature tests: stable across non-todo edits; changes on any
  id/status/content change.

* refactor(web-shell): isolate plan context read; expand todo test coverage

Address follow-up review:
- Extract PlanEventSummary as the sole TodoTimelineContext consumer, mirroring
  ToolGroup's TodoToolBody so the memo-shielded PlanMessage stays stable when
  the timeline Map reference changes.
- Note the spurious-`started` axis of the reword trade-off in the todoStateKey
  doc (a reword can drop a completion or emit a stray start).
- Add direct isTodoWriteToolName tests (incl. the `todowrite` ACP variant) and
  a todoTimelineSignature empty-transcript test.
2026-06-15 00:44:15 +08:00
胡玮文
34f7f91282
feat(cli): improve /copy command argumentHint and description (#5110)
The /copy command supports code block selection with language filtering,
LaTeX, and Mermaid — but the TUI autocomplete only showed "[N]" with a
description about copying the full AI reply. Users had no way to discover
these capabilities from the prompt.

Update argumentHint to "[N] [<lang>|code|latex|mermaid] [<index>]"
and description to mention all supported targets, noting N counts from
the last message.
2026-06-15 00:33:19 +08:00
jinye
5b3d7f7055
fix(core): Repair duplicate tool call IDs (#5107)
* fix(core): Repair duplicate tool call IDs

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

* fix(core): Preserve streaming tool replay metadata

Avoid mutating completed streaming tool calls when a provider replays the same tool call ID with another arguments chunk. This keeps the first surviving call metadata and buffer intact while preserving fragmented JSON accumulation before completion.

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

* fix(core): Address duplicate call id review feedback

Use the shared tool-call dedupe helper from ACP Session, preserve distinct empty call IDs in scheduler and non-interactive batches, and log dropped duplicate scheduler requests for diagnosis.

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

* fix(core): Address duplicate tool call review feedback

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-14 23:53:36 +08:00
tt-a1i
77acfd8ce1
feat(cli): import Claude MCP servers (#5095)
* feat(cli): import Claude MCP servers

* fix(cli): polish Claude MCP import feedback
2026-06-14 23:47:54 +08:00
jinye
058bca208e
fix(daemon): Avoid replaying truncated session diffs (#5108)
* fix(daemon): avoid replaying truncated session diffs

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

* codex: address PR review feedback (#5108)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-14 23:40:29 +08:00
jinye
31281f6a7d
feat(acp): dedicated agent permission dialog via _meta.toolName (follow-up to #5085) (#5105)
* feat(acp): carry _meta.toolName on permission frame; agent drawer (vscode)

WIP: producer mirrors _meta.toolName onto session/request_permission;
webui PermissionDrawer + vscode webview map it to render 'Launch this
agent?' for the Agent tool without a protocol kind. Daemon/web-shell
surface + tests follow.

* feat(web-shell): dedicated agent permission prompt via _meta.toolName

Thread the canonical tool name from the permission frame's _meta.toolName
through web-shell's PermissionRequest so ToolApproval renders 'Launch this
agent?' for the Agent tool, mirroring the vscode PermissionDrawer. Add
tests for the toolName extraction and the agent drawer title.

* fix(acp): mirror _meta.toolName on second producer path + address review

Address @wenshao's review on #5105:

- [Critical] SubAgentTracker's approval handler builds its own
  RequestPermissionRequest (the second producer path, for nested sub-agent
  tool calls) and was missing `_meta: { toolName }`. Session.ts adds it on
  the primary path; mirror it here so nested agents (and any future tool
  relying on _meta.toolName for specialized UI) don't fall back to the
  generic prompt. Locked with a _meta assertion in the approval test.

- Dedupe the three hardcoded 'agent' string matches behind a single shared
  `AGENT_TOOL_NAME` / `isAgentTool` in @qwen-code/webui (re-exported via
  daemon-react-sdk, same pattern as DAEMON_APPROVAL_MODES), consumed by
  PermissionDrawer (webui) and ToolApproval (web-shell).

- Move the agent check to the top of PermissionDrawer.getTitle() so it wins
  over kind-based checks, matching ToolApproval's isAgent-first ordering
  across the two surfaces.

- Extract the _meta.toolName lifting logic in useWebViewMessages into a
  testable `liftToolNameFromMeta` helper and cover the three cases wenshao
  flagged: lift onto toolName, preserve a pre-existing toolName, no-op when
  _meta is absent (plus undefined-toolCall guard).

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
2026-06-14 23:27:38 +08:00
易良
cf56874f47
refactor(core): unify retry delay policy (#3827)
* refactor(core): unify retry delay policy

* refactor(core): tighten retry policy after review

Address PR review feedback on the unified retry policy:

- Document RetryAfterMode semantics (ignore/minimum/prefer) and the implicit
  rule that jitter is not applied when Retry-After is honored.
- Dedupe the 5-minute single-wait constant — INTERACTIVE_RETRY_AFTER_CAP_MS
  now reuses PERSISTENT_MAX_BACKOFF_MS so a future tweak does not silently
  desync the two.
- Drop inert fields at the two 'prefer'-mode call sites in retry.ts; with the
  Retry-After value already gated upstream, the delay reduces to a direct
  Math.min against the cap (also avoids re-parsing the header inside the
  policy).

* test(core): cover retry policy edge cases

* fix(core): preserve retry-after waits in http retry

* fix(core): make retry waits abort-aware

* refactor(core): remove unused retry-after prefer mode

* refactor(core): classify retry errors

Add shared retry error classifier with structured diagnostic fields for
HTTP, SSE, provider-code, transport, abort, and provider-business failures.

Cherry-picked from PR #3850 clean state (2239efa62), replacing the
corrupted squash merge that replaced source files with pointer strings.

* fix(core): close TOCTOU race in delay() abort handling

Move the initial signal.aborted check inside the Promise constructor
and add a re-check after addEventListener to close the race window
where an abort between the check and listener registration would be
silently lost. Also add clarifying comments on the intentional
attempt:1 usage in retry delay calculations.

* fix(core): align retry diagnostics with retry policy

* test(core): cover pre-aborted retry waits

* fix(core): propagate custom retry codes

* fix: update copyright year to 2026 in new files [skip ci]

* fix(core): cap retry delays at the setTimeout ceiling

Clamp both the Retry-After-derived delay (seconds and HTTP-date paths) and
the exponential backoff to the signed 32-bit setTimeout limit. An oversized
Retry-After previously overflowed the timer and fired immediately, turning a
long server-directed wait into a 0ms tight retry loop; the exponent could also
reach Infinity for very large persistent attempt counts.

Also parse Retry-After with an RFC 7231 decimal-only matcher so non-RFC shapes
Number() would accept (0x10, 1e3) fall through instead of producing a wrong
delay, while fractional seconds remain honored.

Addresses review feedback on #3827.

* fix(core): refine retry error classification accuracy

Prefer a transport-level cause over the HTTP status when the status is itself
transient (5xx) or absent, so a socket reset surfaced as the cause of an SDK
error is classified as transport rather than a provider server error. A
definitive 4xx status stays authoritative — a transient cause must not relabel
a permanent failure as retryable.

Relabel 529 from 'fallback-eligible' to 'retryable': this PR retries 529 and
does not implement model/provider fallback, so the old label implied behavior
that does not exist.

Addresses review feedback on #3827.

* fix(core): honor 503 Retry-After and bound fail-fast persistent retry

Parse Retry-After for 503 as well as 429 (both carry it per RFC 7231 and the
stream-side path already honors both), via a shared hasRetryAfterStatus helper
used by the persistent and normal paths.

Keep permanent business errors out of the unbounded persistent loop: an error
classified as 'fail-fast' (e.g. DashScope Throttling.AllocationQuota, which
surfaces as HTTP 429) now falls back to the maxAttempts-bounded retry path
instead of retrying for hours. This wires the classifier into a single control
decision; normal retry control still follows shouldRetryOnError.

Also classify and log the original error before the Qwen quota fast-fail so its
status/request-id/body is never discarded, and drop the redundant status===429
check in defaultShouldRetry (isRateLimitError already covers 429/503).

Addresses review feedback on #3827.

* fix(core): honor provider retry codes in stream retry predicate

generateContentStream's custom shouldRetryOnError only checked HTTP status, so
provider-specific rate-limit codes (e.g. 1302/1305 or caller-supplied
retryErrorCodes) that lack a 429/5xx status were silently dropped. Delegate to
isRateLimitError so they trigger retry, matching defaultShouldRetry. Also hoist
the duplicated getContentGeneratorConfig() call into a local cgConfig.

Addresses review feedback on #3827.

* fix(core): bound all retry delays by the setTimeout ceiling and fast-fail aborts

Address Copilot review on the prior commits:
- getRetryDelayMs now clamps the exponential and jittered delays (and the
  Retry-After cap) by min(maxDelayMs, MAX_TIMEOUT_MS), so an oversized
  caller-supplied maxDelayMs cannot let a computed delay overflow setTimeout.
- retryWithBackoff fast-fails on an abort/cancel error regardless of a
  permissive shouldRetryOnError — cancellation is authoritative.
- Update the classifyRetryError JSDoc to note it drives the single fail-fast
  persistent-loop exclusion.

Addresses review feedback on #3827.

* fix(core): return best-effort response when content retries exhaust

When shouldRetryOnContent keeps rejecting the response until the attempt budget
is spent, the loop previously fell through to a context-free
"Retry attempts exhausted" error, discarding the actual response. Track the
last rejected response and return it (best-effort) so the caller keeps the real
content and its context. The bare throw remains only as a defensive type-safety
fallback.

Addresses post-merge review feedback on #3827.

* fix(core): polish retry logging and provider-code classification

Address remaining review suggestions on #3827:
- logRetryAttempt now includes the computed backoff delay in its message.
- Normal-path Retry-After retries log at error level for 5xx (e.g. 503) and
  warn for 429, via a shared logRetryAtStatusLevel helper.
- Content-retry path now emits a diagnostic log before sleeping.
- getProviderFields no longer echoes a numeric HTTP-status code (e.g.
  { status: 429, code: 429 }) as providerCode.
- Add a client.test.ts test that retryErrorCodes is forwarded to
  retryWithBackoff, plus classification/logging regression tests.
2026-06-14 21:21:34 +08:00
Shaojin Wen
bb0db932fa
fix(core): default GLM-5.2+ and GLM-6.x onward to 1M context (#5103)
GLM-5.2 ships a 1M context window, and 1M is becoming the norm for newer
GLM releases. The previous `/^glm-5/` rule capped the whole GLM-5 line at
202752, so every new model would need a code change.

Make 1M the forward default for GLM-5.2+, GLM-6.x..9.x and two-digit
majors, while pinning the confirmed 200K families (GLM-5 / 5.0 / 5.1 and
GLM-4.x or older) explicitly. Third-party deploy prefixes (e.g.
`pai/glm-5.3`) are already stripped by normalize(), so they match the same
rules. Non-numeric names (e.g. glm-z1) stay on the conservative fallback.
2026-06-14 21:17:45 +08:00
ytahdn
9be731ce75
feat(cli,web-shell): persist goal status in daemon transcript events (#5098)
Previously /goal state lived only in frontend memory — page refresh or
multi-device sessions lost the active goal. Now the CLI emits goal status
updates as structured daemon events (_meta.goalStatus), which flow through
the transcript as status blocks (source: 'goal', data: {...}). The web-shell
rebuilds goal state from transcript blocks on connect, making goal status
survivable across page refreshes and syncable across devices.

- CLI: emitGoalStatus on goal set/clear, pass outputHistoryItems through
  nonInteractiveCliCommands, add setAt to goalCommand output
- SDK: widen DaemonUiStatusEvent source/data types, preserve them in
  transcript blocks
- webui: normalize _meta.goalStatus in DaemonSessionProvider, replace
  sentinel-prefix text encoding with structured data
- web-shell: derive activeGoal from transcript blocks (getLatestActiveGoalFromBlocks),
  remove optimistic client-side goal dispatch, parse structured goal data
  in GoalStatusMessage/SystemMessage
- Tests: cover emitGoalStatus, outputHistoryItems passthrough, transcript
  block serialization, DaemonSessionProvider event conversion
- Also: harden McpDialog restart result type check with isRestartEntriesResult

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-06-14 21:08:06 +08:00
易良
4694d11c5f
fix(ci): fail PR review job when the run aborts mid-review (#5053)
The review-pr job only checked qwen's exit code, the tee status, the
timeout sentinel, and an empty log. When the model connection drops
mid-review, qwen still exits 0 and emits a terminal stream-json result
event with subtype=success / is_error=false whose text carries the
inlined '[API Error: ...]'. All existing checks pass, so the job goes
green without ever posting a review comment.

Inspect the terminal result event explicitly: fail when it is missing,
when is_error is true or subtype is not success, or when the result text
contains an inlined API error. A failed check now triggers the existing
fallback-comment step instead of a silent green success.

Refs #5052
2026-06-14 20:28:25 +08:00
Shaojin Wen
dc0706dc18
feat(web-shell): make input shortcuts discoverable and clickable (#5096)
* feat(web-shell): make input shortcuts discoverable and clickable

- Add an always-on, clickable hint row below the input: ↑ previous / ↓ next history, ctrl+r search, / commands, @ files — each click runs the matching action.
- Make the status bar "? for shortcuts" a persistent clickable button shown next to the mode indicator.
- Dismiss the shortcuts panel by clicking outside it or pressing Esc (no dedicated close button).
- Cancel reverse-i-search by clicking outside the panel (same as Esc), restoring the original draft.
- Distinguish ctrl+r (search history) from up/down (cycle history) in the shortcuts panel.
- Auto-prepend a space for mid-word @-mentions, and make the / and @ triggers idempotent so a second click re-opens the menu instead of producing "//" or "@ @".

* fix(web-shell): address PR review — touch dismissal, primary-button guard, @ idempotency

- Outside-press dismissal for the shortcuts panel and reverse-i-search now also listens for touchstart, ignores non-primary (middle/right) buttons, and respects defaultPrevented — matching the Settings/Mode inline panels.
- insertText('@') no longer inserts a duplicate '@' when the cursor sits directly before an existing '@'; it steps over the existing one and opens the menu.

* fix(web-shell): close shortcuts panel idempotently on outside-press

Touch fires touchstart plus a synthesized mousedown; a toggle onClose would reopen the panel right after closing. Use a dedicated close (set false) for the outside-press / Escape dismissal, matching the other inline panels.

* fix(web-shell): address /review — disabled guard, mid-line slash, focus, dedup

- Hide the hint row while the editor is disabled and bail the history/search callbacks on disabledRef, so the buttons can't bypass the disabled guard. insertText stays usable for App-driven injection; the now-hidden row is the only internal path that passes '/' or '@'.
- Clicking the / hint on non-empty, non-slash text replaces the content with '/' so the command menu actually opens, instead of leaving a stray mid-line '/'.
- Outside-click search dismissal no longer steals focus from the clicked target (closeSearch keepFocus=false).
- Extract a shared hintProps() helper for the five hint buttons.

* fix(web-shell): address /qreview — capture-phase Escape, hint aria-haspopup

- The shortcuts panel's Escape now wins over the App-level global Escape (capture phase + preventDefault/stopPropagation), so Esc closes the panel instead of being swallowed (clearing queued prompts / cancelling the stream) while it's open.
- Add aria-haspopup to the popup-opening hint buttons (dialog for ctrl+r, listbox for / and @), matching the sibling toolbar buttons.

* fix(web-shell): theme the slash-completion popup scrollbar

The CodeMirror autocomplete list and info panel used the browser-default (light) scrollbar, clashing with the dark theme. Apply the repo's scrollbar convention (thin + var(--border-color) thumb over a transparent track, with the -webkit fallback) so it matches in both themes.

* fix(web-shell): don't destroy the draft when clicking / on non-empty input

Replacing the document with '/' (the previous /review fix) silently wiped a typed draft. Instead, no-op when the line isn't already a command and the editor is non-empty — the slash menu needs a line-leading '/', which can't be added without either a stray mid-line '/' or clobbering the draft. Empty input still inserts '/' and opens the menu.

* fix(web-shell): guard hint history nav on multi-line input; hide hints behind dialogs

- navigatePrev/NextHistory now early-return on doc.lines > 1, matching the ArrowUp/ArrowDown keymap, so clicking the up/down hints no longer replaces a multi-line draft with a single history entry.
- showShortcutHints also requires !dialogOpen, matching the Ctrl+R keymap guard, so the hint buttons aren't interactive while a dialog is open.

* feat(web-shell): undo click-inserted / or @ on cancel; align hint nav with keymap

- Clicking the / or @ hint then pressing Escape (without typing past the inserted char) now removes the trigger too — it was clicked in, not typed. Editing past it cancels this.
- navigatePrev/NextHistory move the completion selection when the menu is open, matching the ArrowUp/ArrowDown keymap.
- The / hint no-op (non-empty draft) no longer fires startCompletion, which would pop an empty menu; a line-leading / still re-opens it.

* fix(web-shell): undo click-inserted trigger on any completion dismissal, not just Escape

Only the Escape keymap undid the click-inserted / or @. Clicking away or blurring also closes the menu but left a stray trigger behind. Watch the completion status (active -> closed) via an updateListener instead, so every dismissal path (Escape, click-away, blur) removes an untouched click-inserted trigger; the per-key special-case in the Escape handler and restarter is removed.

* feat(web-shell): grey out history hint arrows when there's nowhere to go

useInputHistory now exposes a nav { canUp, canDown } state, kept in sync on push/navigate/reset. The up/down hint buttons are disabled when there's no older entry to recall, or when not currently browsing history. Keyboard and mouse share the same state, so the affordance stays accurate however history is navigated.
2026-06-14 16:41:37 +08:00
jinye
64a1efb20f
fix(acp): add internal Kind.Agent, keep ACP wire on 'other' (no-regression) (#5085)
* feat(core): add Kind.Agent for Agent tool to improve UI categorization

The Agent tool was using Kind.Other as a catch-all, causing WebUI
permission dialogs to show generic titles and descriptions. Adding a
dedicated Kind.Agent value enables agent-specific UI rendering in
permission drawers, tool labels, and export normalization.

🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)

* test(webui): lock agent label mapping in labelUtils test

Address wenshao review on PR #5085 — add assertion that
getToolDisplayLabel({ kind: 'agent' }) returns 'Agent', matching the
sibling task/skill cases.

* test(cli): cover agent kind in export normalization + document acp-sdk cast

Address wenshao review on PR #5085:
- Add normalize.test.ts case asserting an agent-kind tool call is
  preserved as 'agent' through normalizeSessionData.
- Add TODO(acp-sdk) comment on the KIND_MAP 'agent' cast explaining why
  we emit 'agent' (webui SSE consumer) rather than mapping to 'other'.

* fix(acp): map Kind.Agent to 'other' on the wire; drop unusable 'agent' kind

wenshao's daemon-level A/B verification showed emitting kind:'agent' over
ACP is a regression: the daemon's ClientSideConnection Zod-validates every
session/update + session/request_permission from the qwen --acp child before
SSE fan-out, and @agentclientprotocol/sdk has no 'agent' ToolKind (verified
through 0.25.1), so the frame is rejected (invalid_union) and dropped — the
agent tool_call + permission dialog that previously reached SSE clients as
kind:'other' now never arrive.

Shrink to a no-regression safe version:
- ToolCallEmitter maps the internal Kind.Agent to 'other' on the wire (drops
  the 'agent' as ToolKind cast that only fooled tsc, not the runtime schema).
- Revert the wire/UI/protocol 'agent' additions that depended on a value the
  protocol can't carry: PermissionDrawer/labelUtils kind branches, web-shell
  DaemonMessageToolKind + inferToolKind, Java SDK schema/enum, export allowlist.

Kind.Agent stays in core as the internal tool category. The dedicated agent
permission dialog will be delivered via _meta.toolName (which already rides the
validated wire) in a follow-up PR, not via a protocol kind.
2026-06-14 14:59:24 +08:00
tt-a1i
8471b6d254
fix(cli): wrap long status lines (#5093)
Some checks failed
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
Sync cua-driver to Aliyun OSS / Mirror cua-driver binaries to Aliyun OSS (push) Has been cancelled
* fix(cli): wrap long status lines

* test(cli): cover wrapped status line token
2026-06-14 13:28:50 +08:00
yao
f9080e44fb
fix(cli,core): harden OOM prevention — idempotent compaction tests, explicit GC, debug log defaults (#4914)
* test(cli): add compactOldItems idempotency regression tests

Cover the scenario fixed in commit 595701096 where already-compacted
tool groups (resultDisplay === UI_COMPACT_CLEARED_MESSAGE) were
incorrectly counted as having real output, causing over-compaction.

Three new test cases:
- Already-compacted groups are not re-compacted; second call is a no-op
- All tool groups already compacted → no-op
- Mixed tool group (some tools real, some cleared) → only groups with
  real output are compacted

* fix(cli,core): enable explicit GC and disable debug log by default

- enableExplicitGC defaults to true, --expose-gc added to start/dev scripts
- isDebugLogFileEnabled() defaults to false (opt-in via QWEN_DEBUG_LOG_FILE=1)
- Add safety tests: trigger_gc only in critical tier, global.gc() only in
  memoryPressureMonitor.ts trigger_gc case

* fix: address R1 review comments for memory pressure monitor

- Replace brittle source-parsing test with behavioral tests for global.gc()
- Export UI_COMPACT_CLEARED_MESSAGE constant and use in tests
- Remove redundant NODE_OPTIONS override from start script
- Add production bin wrapper with --expose-gc for OOM protection
- Remove unused path import from memoryPressureMonitor.test.ts

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>

* fix: forward --expose-gc to all deployment modes

Standalone package shims and daemon-spawned sessions (AcpBridge,
httpAcpBridge) were missing --expose-gc, causing explicit GC to
silently fail under critical memory pressure.

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>

* fix: forward child process signal in cli-entry wrapper

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>

* fix(cli,channels): filter --inspect flags when forwarding execArgv to daemon children

* fix: make cli-entry.js executable (mode 100755)

* fix(core): reject whitespace-only QWEN_DEBUG_LOG_FILE and add QWEN_MEMORY_ENABLE_GC=0 opt-out

* fix(scripts): include cli-entry.js wrapper in dist package for npm publish

* fix(acp-bridge): forward --expose-gc and filter --inspect in spawnChannel

- Add --expose-gc to getAcpMemoryArgs() so daemon-spawned ACP children
  have global.gc() available for critical memory pressure cleanup
- Filter --inspect/-brk flags from process.execArgv to prevent port
  conflicts in multi-session daemon mode
- Update spawnChannel.test.ts for new getAcpMemoryArgs() return shape

This change was previously in httpAcpBridge.ts but lost during the
daemon refactor merge (#4490) that moved spawn logic to acp-bridge.

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-06-14 10:40:53 +08:00
Shaojin Wen
800507598c
feat(web-shell): reveal full tool detail and auto-collapse finished tools (#5088)
* feat(web-shell): reveal full tool detail and auto-collapse finished tools

Long tool descriptions were hard-capped at 120 characters and finished
tools (shell/edit/write) stayed expanded indefinitely, so commands were
unreadable and the transcript filled with stale output.

- Lift the 120-char description cap so the full command/path reaches the
  DOM; collapsed rows ellipsise via CSS (adapts to width) and a click
  reflows the full text into a wrapped block below the header.
- Add a leading disclosure chevron; any row with detail output or a long
  description is now expandable.
- Auto-collapse a tool to its one-line summary once it completes
  successfully. Running tools stay expanded (live output) and failures
  stay expanded (error visible); agents keep their own manual expand state.

* fix(web-shell): preserve manual expand on completion; correct auto-expand comment

Review feedback on #5088:

- shouldAutoExpand: rewrite the comment to match the code. Only the verbose
  kinds (shell/edit/write/ask) auto-expand and stay expanded on failure; other
  kinds are collapsed by default (their summary line shows the outcome and they
  stay click-to-expand). Force-expanding every failed tool was rejected because
  tools without an expanded-detail renderer would then hide the summary line
  and show an empty body — i.e. hide the error.
- Auto-collapse-on-completion no longer overrides an explicit user toggle: a
  userToggledRef latch (set on header click, reset on tool-identity change)
  guards the collapse effect, so a row the user expanded/collapsed keeps its
  state when the tool finishes.

* test(web-shell): assert tool-detail relocation via DOM, not textContent

Review feedback (#5088): the expand test asserted container.textContent
contains the command before and after the click, which passes regardless of
whether the description is relocated from the header span to the wrapped
block (textContent concatenates the whole subtree). Assert the DOM move
instead — the command is in a leaf <span> while collapsed and in none while
expanded — so a regression dropping the relocation now fails the test.
2026-06-14 10:11:13 +08:00
顾盼
e8342715e5
feat(core): migrate Computer Use to cua-driver (cross-platform) (#5051)
Migrate the built-in Computer Use tool surface from open-computer-use
(npm/npx) to cua-driver-rs (native Rust driver, trycua/cua).

- Replace the 9-tool ocu surface with the full 35-tool cua-driver surface
  (page/CDP, cursor, session, recording, config, app lifecycle, …),
  generated from the live `cua-driver mcp` tools/list — pinned to v0.5.2.
- Per-platform signed + notarized binary distribution: download into
  ~/.qwen/computer-use/ with SHA-256 integrity verification, a three-tier
  Windows unzip fallback, headers/idle timeouts, and a bounded retry loop.
- macOS TCC permission flow via CuaDriver.app (com.trycua.driver), polled
  one-at-a-time through the no-gate status daemon.
- Mirror cua-driver assets on the qwen-code-assets OSS bucket with a
  push-to-main sync workflow guarded by checksums.
- Gate high-risk tools (kill_app, launch_app, start_recording, set_config,
  replay_trajectory, page JS execution) to an explicit confirmation type so
  AUTO_EDIT cannot silently auto-approve them; AUTO defers to the classifier,
  YOLO auto-approves.
2026-06-14 09:26:09 +08:00
Shaojin Wen
8472c6fcea
fix(webui): defer DaemonClient disposal to survive React StrictMode (#5091)
Under StrictMode (dev default), DaemonWorkspaceProvider's useEffect
cleanup called client.dispose() synchronously, destroying the memoized
DaemonClient that the second effect invocation reused. This left the
transport closed before the session provider could attach, surfacing as
"Transport connection closed" and a permanent "Loading..." / disconnected
state in the web-shell.

Defer disposal by one microtask. StrictMode's synchronous re-mount
cancels the pending disposal before the microtask fires, preserving the
shared client. Real unmounts and client replacements still dispose
normally since no cancellation occurs in those paths.
2026-06-14 08:12:36 +08:00
yao
75cc3ce15e
fix(cli): add OSC 52 clipboard fallback for SSH environments (#4929)
* fix(cli): add OSC 52 clipboard fallback for SSH environments

- Add writeOsc52() helper in commandUtils.ts and vim.ts
- Fall back to OSC 52 escape sequence when xclip/xsel/wl-copy unavailable
- Fixes /copy command and vim yank (yy, yw, etc.) over SSH without X11

* refactor(cli): extract writeOsc52 to shared clipboardUtils

- Add writeOsc52() export in clipboardUtils.ts with TTY check, error handling,
  and boolean return for success/failure
- Remove duplicate writeOsc52 from commandUtils.ts and vim.ts
- Update both to import from clipboardUtils.ts
- Add 6 tests verifying OSC 52 escape sequence output to stdout/stderr,
  TTY detection, special chars, empty string, and error handling
- Fix commandUtils test to assert OSC 52 fallback instead of throwing

Fixes code duplication and inconsistent error handling noted in PR review.

* test(clipboard): fix spawn timeout test hanging with fake timers

Remove vi.useFakeTimers() which is incompatible with process.nextTick
in the mock pattern. Use real timers with 10s test timeout instead.

* fix(cli): check writeOsc52 return value in callers

Warn when OSC 52 clipboard write fails (no TTY available) instead of
silently ignoring the failure.

* fix(test): clear wl-paste image type cache before BMP-to-PNG tests

BMP-to-PNG test block was missing its own beforeEach to reset the
cachedWlPasteImageTypes cache, causing the 'prefer PNG over BMP' test
to fail intermittently in CI when a previous test had populated the cache.

* fix(test): use dynamic import instead of vi.resetModules() to fix test pollution

* fix(test): correct spawn call assertion in BMP-to-PNG clipboard test

* fix(test): clean up /tmp/test before BMP-to-PNG clipboard test to fix flaky assertion

* fix(test): restore vi.resetModules() with dynamic imports for test isolation

Replace stale top-level imports of clipboardUtils with describe-level
variables populated by dynamic import() after vi.resetModules() in
beforeEach. This ensures every test gets a fresh module instance,
eliminating cross-test state pollution from cachedWlPasteImageTypes
and linuxClipboardTool.

* fix(cli): throw error when all clipboard methods fail and restore vi.resetModules for test isolation

* fix(cli): try OSC 52 before throwing when xclip/xsel fail

Ensures consistent behavior between /copy and vim yank commands
in SSH environments where xclip/xsel are installed but fail
due to missing X display.

* test(cli): add OSC 52 fallback tests for xclip/xsel failure scenario

Adds two tests to verify OSC 52 behavior when clipboard tools
exist but fail (e.g., SSH without X forwarding):
- Verifies OSC 52 is attempted and fails gracefully when no TTY
- Verifies OSC 52 succeeds when TTY is available

* test(cli): mock isTTY in OSC 52 no-TTY fallback test to prevent flakiness

* fix(cli): wrap OSC 52 sequence with wrapForMultiplexer for tmux/screen support

* fix(cli): try OSC 52 fallback in vim writeClipboard when cached tool fails at runtime

* fix(cli): prefer stderr over stdout for OSC 52 to avoid Ink rendering pipeline interference

* fix(cli): mention OSC 52 attempt in error message when xclip/xsel and OSC 52 both fail

* fix(cli): harden OSC 52 clipboard with size limit and async error handling

     - Cap OSC 52 payload at 75KB (~100KB base64) to prevent terminal crashes/hangs
       from oversized escape sequences (iTerm2 ~100KB, xterm ~8KB limits)
     - Add write callback to capture async failures on stdout/stderr streams
     - Apply same hardening to duplicate implementation in AuthenticateStep.tsx
     - Strengthen tests: stub TMUX/STY for determinism, add tmux/screen DCS wrap tests
2026-06-14 07:55:24 +08:00
jinye
87dc1a3932
test(cli): Cover rewind selection and confirm flow (#5044)
* test(cli): Cover rewind selection and confirm flow

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

* codex: address PR review feedback (#5044)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-14 07:07:08 +08:00
jinye
24a9828b2e
fix(core): Persist file history snapshot updates (#5057)
* fix(core): persist file history snapshot updates

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

* codex: address PR review feedback (#5057)

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

* fix(core): Address file history persistence review feedback

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

* fix(core): Address file history review follow-ups

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-14 06:47:03 +08:00
Yufeng He
b6b15e45e8
fix(cli): drop tool calls after cancellation (#5020) 2026-06-14 06:36:48 +08:00
Yufeng He
533fafa2d2
fix(cli): ignore expired live agents in focus navigation (#5070)
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
2026-06-14 03:19:30 +08:00
顾盼
06345a2fe9
feat(core): Workflow P3 — agent({schema, agentType, model, isolation:'worktree'}) (#4721) (#5034)
* feat(core): Workflow P3 — agent({schema, agentType, model, isolation:'worktree'}) (#4721)

Adds the P3 dispatch options to the workflow runtime, completing the
contract qwen-code's workflow tool matches against upstream Claude Code
2.1.168. P1/P2 stubs (workflow-sandbox.ts:508-527) are replaced with
production paths routed through `SubagentManager.createAgentHeadless` so
per-call model overrides go through `buildRuntimeContentGeneratorView`
(provider routing), per-agent MCP servers / hooks get isolated
lifecycles, and worktree-isolated subagents run against a rebound Config.

- agent({agentType: 'X'}) resolves against the declarative-agents
  registry (#4842 + #4996) via findSubagentByName; unresolved names throw
  "agent({agentType}): agent type 'X' not found" verbatim from upstream.
- agent({model: 'qwen3-max'}) is threaded into SubagentConfig.model so
  the runtime view sees it (modelConfigOverrides alone would only swap
  the model name within the existing provider's view).
- Workflow's disallowed-tool floor [SendMessage, ExitPlanMode] is unioned
  with the agentType's own disallowedTools so a permissive agentType
  cannot re-enable them for a workflow subagent.
- agent({isolation: 'worktree'}) provisions a fresh worktree via
  GitWorktreeService.createUserWorktree (slug agent-<7hex>, mirrors
  AgentTool 1849-1963), rebinds cwd/getTargetDir/getFileService/
  getWorkspaceContext on a prototype-chained Config override, and on
  completion auto-removes the worktree if clean or preserves the path +
  branch (appended to the result string) when the subagent left changes.
  Parent-dirty trees are refused with a clear error to avoid silently
  running the subagent against a stale HEAD.
- agent({isolation: 'remote'}) throws "agent({isolation:'remote'}) is
  not available in this build" verbatim (upstream 2.1.168 parity).
- agent({schema: S}) injects a per-call SyntheticOutputTool (existing
  tools/syntheticOutput.ts, AJV-backed) into a fresh per-subagent
  ToolRegistry built via rebuildToolRegistryOnOverride, then watches
  AgentEventEmitter TOOL_CALL/TOOL_RESULT events for `structured_output`
  invocations. A successful call's args are captured as the dispatch
  return value (object, not string); after two failed attempts the
  third failure aborts the dispatch and throws "subagent completed
  without calling StructuredOutput (after 2 in-conversation nudges)"
  verbatim. No agent-core.ts changes — the entire 2-nudge counter
  lives in the dispatch layer so the shared subagent loop is unaffected.

The sandbox's agent() wrapper now revives per-call object returns into
the vm realm (JSON round-trip inside the vm runInContext block), closing
the same T1/T8/T14 host-prototype-escape vector that P2's per-element
revival closed for parallel/pipeline. Two new sandbox security tests
(constructor-chain probe + non-JSON-serializable collapse) regress this.

WorkflowAgentResult widens from `string` to `string | object`; the
fast-path (no agentType/model/isolation/schema) is preserved byte-for-byte
to keep P1/P2 zero-overhead.

Tests: 159 workflow-suite tests + 217 adjacent (subagents / syntheticOutput /
agent-override) all green. Real-LLM E2E follow-up planned (mirroring P2's
13/13 qwen3-max validation).

Related #4721 (parent design — multi-phase, not closed by this PR)
Related #4732 (P1 merged) #4947 (P2 merged) #4842 #4996 (declarative agents)

* chore(core): P3 self-review R1 — align worktree suffix wording + 6 test gaps

R1 of pre-push adversarial self-review on PR #5034 surfaced 6 confirmed
findings across 6 diverse lenses (correctness / security / reuse-altitude
/ self-invariant / consumer-breakage / test-gaps). Each finding faced 2
independent skeptics defaulting to refuted=true; 6 survived majority
challenge.

Source code:
- Worktree-preserved suffix wording now matches AgentTool's
  formatWorktreeSuffix (agent.ts:1700-1719) verbatim, including the
  `git worktree add <path> <branch>` recovery hint for the directory-
  removed-but-branch-preserved race.

Test gaps closed:
- schema-mode success after 1 nudge (round-2 args captured)
- schema-mode success after 2 nudges (round-3 args captured)
- schema-mode + agentType together — floor disallowedTools still unioned
- schema-mode caller-abort takes priority over the StructuredOutput
  terminal error (signal.aborted check at workflow-orchestrator.ts:489-490)
- override path dispose() runs in finally on the success path
- override path dispose() runs in finally on the terminate-mode-error path

Declined R1 finding: negative tests for invalid opt types (schema/model/
agentType passed null/number/empty-string). Adding upfront type
validation is scope creep — upstream does not, P1/P2 do not, and the
workflow tool is model-authored where these inputs are extremely
unlikely. Existing AJV / SubagentManager downstream errors are descriptive
enough. Will revisit if R2 makes a stronger case.

166/166 tests pass (workflow suite + adjacent + workflow-orchestrator).
typecheck + lint clean across packages/core, packages/cli,
integration-tests, sdk, webui.

* chore(core): P3 self-review R2 — vm-realm opts revive + error-msg sanitize + 12 tests

R2 of pre-push adversarial self-review on PR #5034. 6 diverse-lens
finders (60 agents, ~2.5M tokens, 24 min) over the R1-fix-applied
code, with 2 independent skeptics defaulting to refuted=true.
12 confirmed survivors after adversarial verify; decisions below.

Security (FIX):
- agent() wrapper in workflow-sandbox.ts now JSON-revives agentOpts
  inside the vm runInContext block BEFORE passing them to the host
  dispatch. Closes a Proxy/inherited-getter escape that P3 introduced
  along with the user-supplied schema object: a script could have
  wrapped agentOpts.schema in a Proxy whose getter ran host-side code
  during SyntheticOutputTool construction / AJV compile. Same
  mechanism as args / parallel-result revival.
- runOverridePath now sanitizes opts.agentType through
  sanitizeForErrorMessage() (control chars → space) before
  interpolation into the "agent type 'X' not found" error message.
  Prevents a model-authored agentType containing CRLF / NUL from
  fragmenting a single-line error across log records / OTLP fields.

Reuse-altitude (FIX):
- Added JSDoc block to WorkflowWorktreeIsolation interface
  documenting each field's role for cleanup.

Test gaps (FIX, 12 new tests):
- agentType control-char sanitization regression
- dispose() runs in finally when subagent.execute throws
- isolation:'worktree' provision error branches (5):
  nested parent / git unavailable / not a git repo / parent dirty /
  createUserWorktree returns failure
- isolation:'worktree' cleanup branches (3):
  removeUserWorktree fails / branchPreserved race / removeUserWorktree
  throws — each preserves the worktree (or branch) with the right
  user-facing suffix
- combinations (2): model + isolation:'worktree' threads model AND
  provisions worktree; schema + isolation:'worktree' returns
  structured payload verbatim (preserved suffix only on string return)

Test infrastructure: vi.mock'd GitWorktreeService at the module level
(partial mock; preserves the existing exports the unrelated
worktreeCleanup.ts depends on) with a per-test beforeEach reset.

Declined R2 findings (kept the R1 line):
- [major] Schema parameter upfront validation: same scope-creep
  decline as R1. Upstream doesn't do it; AJV's downstream error is
  descriptive enough.
- [major] Worktree provision extracted to shared util with AgentTool:
  agreed in principle but out of P3 scope. A separate refactor PR
  should land that with AgentTool maintainers in the loop.

178/178 tests pass (workflow + adjacent suites). typecheck + lint
clean across packages/core, packages/cli, integration-tests, sdk,
webui.

* fix(core): address wenshao R1+R2 review on Workflow P3 (PR #5034)

Round 1 (15:41) + Round 2 (17:24) review from wenshao surfaced 7 inline
findings across schema-mode dispatch correctness, worktree cleanup
coverage, and error attribution. Each fix is paired with a regression
test that was RED before the change landed.

T0 [Critical] Worktree leak when schema setup throws after provision
  workflow-orchestrator.ts: outer try MOVED to start immediately after
  provisionWorkflowWorktree. Previously the try opened only after
  createSchemaConfigOverride / createSchemaModeState / signal listener
  attachment — so any throw in those three (broken MCP server during
  the per-call ToolRegistry rebuild was the trigger wenshao cited)
  orphaned the just-provisioned worktree under .qwen/worktrees/.
  Test: "isolation:'worktree' + schema setup throws → worktree is
  still cleaned up" — simulates createToolRegistry failure during
  createSchemaConfigOverride; asserts removeUserWorktree was called.

T1 [Critical] / T4 [H1] agentType + schema silently dead-ended
  workflow-orchestrator.ts: schema-mode augmented config now (a)
  appends ToolNames.STRUCTURED_OUTPUT to baseConfig.tools when the
  allowlist is restricted (no '*' and doesn't already contain it), so
  prepareTools / getFunctionDeclarationsFiltered doesn't filter
  structured_output out of the subagent's surface; (b) preserves the
  resolved agentType's persona by APPENDING the schema-contract
  instruction block instead of replacing the systemPrompt outright.
  Replace remains only on the ephemeral no-agentType path where
  baseConfig.systemPrompt IS WORKFLOW_SUBAGENT_SYSTEM_PROMPT (schema
  variant is its strict superset; avoids two near-identical prompts).
  Tests: structured_output appears in the allowlist alongside the
  agentType's existing tools; persona prompt is contained in the
  effective systemPrompt.

T2 [Suggestion] / T5 [M1] Parent-abort listener leaked per schema call
  workflow-orchestrator.ts: named listener stored at outer scope,
  removed in the outer finally regardless of how the dispatch ended.
  Previous `{ once: true }` only auto-removed on actual parent abort;
  the happy-path schema dispatch — success capture / 3-failure abort
  fires the CHILD controller without the parent ever aborting — left
  the listener stuck on the per-run signal. With N schema calls per
  workflow N listeners + N child-controller closures accumulated.
  Test: 5 sequential schema dispatches over the same parent signal
  end with zero live listeners.

T6 [M2] Terminate mode misdiagnosed as nudge exhaustion
  workflow-orchestrator.ts: schema path now distinguishes
  terminateMode before attributing failure to schema mode. TIMEOUT /
  MAX_TURNS / ERROR throw the existing "did not complete (terminate
  mode: X)" message that the non-schema path uses. Only the actual
  schema-failure cases produce schema wording, and those are split:
  attempts > 2 keeps the upstream-verbatim "(after 2 in-conversation
  nudges)" wording; attempts === 0 throws an accurate "no validation
  attempt — model produced plain-text content" instead of misleadingly
  citing nudges that never happened. (The existing 0-call test was
  updated to match the new accurate message; the 3-failure test
  retains the verbatim wording.)
  Tests: parametric over TIMEOUT/MAX_TURNS/ERROR asserting "did not
  complete"; companion test pinning the verbatim wording to the
  3-failure path.

T3 [Suggestion] Schema-mode JSON revival sentinel — clarified
  workflow-sandbox.ts: added a block comment documenting that the
  JSON-round-trip + null-on-throw is a SECURITY backstop (errors-as-data
  convention from parallel/pipeline) rather than a contract path —
  unreachable in production schema mode because the host return is
  LLM tool_call args, always JSON-serializable. No behavior change.

Tests: 75/75 orchestrator + 111/111 sandbox/tool/limiter green.
typecheck + lint clean across packages/core and packages/cli.

R1+R2 self-review commits (e1c5ec79c / 62624a994) precede this commit
on the same branch — they predate wenshao's review and address
distinct findings; reviewer L1 (worktree-lifecycle unit coverage) is
already closed by R2's 11 worktree tests.
2026-06-14 03:16:39 +08:00
Yufeng He
0db3273174
fix(cli): submit fast tool results after stream end (#5071) 2026-06-14 02:44:11 +08:00
ChiGao
ce4b0cf629
feat(sdk,serve): DaemonTransport abstraction + ACP standard compliance (#5040)
* feat(sdk): DaemonTransport abstraction — pluggable transport for REST/ACP-HTTP/ACP-WS

- DaemonTransport interface with fetch + subscribeEvents
- RestSseTransport: extract current SSE logic from DaemonClient
- AcpWsTransport: WebSocket multiplexer + URL-to-JSON-RPC mapping
- AcpHttpTransport: POST /acp + session-scoped SSE
- AcpEventDenormalizer: JSON-RPC notification -> DaemonEvent
- AutoReconnectTransport: opt-in reconnect + fallback wrapper
- negotiateTransport(): auto-detect best transport via GET /capabilities
- Provider: DaemonWorkspaceProvider gains transport prop
- Server: GET /capabilities advertises supported transports
- Zero breaking changes: no transport = current REST behavior

Generated with AI

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

* docs(design): include DaemonTransport design doc in implementation PR

* fix(sdk): address 6 verification findings — bundle size, WS hang, error types, ACP compat

- Remove ACP transport class re-exports from barrel (index.ts) to avoid
  ~19.7KB browser bundle bloat; keep type-only exports
- Fix WS dial hang: reject connect promise in onerror when not yet
  connected (Node WebSocket may only fire error, not close)
- Fix parked generators: maintain _activeGenerators set, abort all on
  WS close so generators throw DaemonTransportClosedError
- Forward abort signal through AcpHttpTransport.sendRequest to fetch
- Restore DaemonHttpError in RestSseTransport (was plain Error)
- ACP endpoint compat: extract connectionId from initialize, send
  Acp-Connection-Id header, add _qwen/ prefix for vendor methods,
  preserve real HTTP status in error mapping, fetch /capabilities
  from REST endpoint for correct shape

Generated with AI

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

* fix(sdk): address 16 review findings + CI bundle size

- CI: move negotiateTransport to separate file, extract DaemonHttpError
  to break static import chain from barrel -> DaemonClient. Browser
  bundle drops from 136KB to 115KB, under the 116KB budget.
- Route table: extract shared acpRouteTable.ts, used by both transports.
  Unify method naming (remove _qwen/ prefix inconsistency).
- Token: move from URL query to Authorization header on WS upgrade
- Error type: DaemonHttpError extracted to DaemonHttpError.ts; import
  in RestSseTransport no longer pulls in DaemonClient.
- Init retry: reset failed initPromise so next call retries
- Reconnect mutex: prevent concurrent reconnect storms
- Generator queue: cap at 256, drop-oldest
- WS init timeout: 30s default
- negotiate: clear timer on all paths, catch dispose rejection
- Headers: forward init.headers in ACP transports via mergeHeaders()
- Dead code: remove unused pendingRequests/sseAbort fields
- Provider: dispose client on unmount
- Helpers: extract matchRoute/synthesizeResponse/jsonRpcErrorToHttpStatus/
  isRecord/composeAbortSignals to shared acpTransportUtils.ts
- Package exports: add deep import paths for ACP transports
- Tests: add AcpEventDenormalizer unit tests (17 cases)

Generated with AI

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

* fix(sdk): ESLint array-type rule — ReadonlyArray<T> → readonly T[]

Generated with AI

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

* fix(sdk): fix 3 ACP wire bugs + bundle size + npm exports

Wire bugs (verified broken against real daemon):
1. AcpHttpTransport: read connectionId from response header + correct JSON path
2. AcpWsTransport: send token via Authorization header, not URL query
3. AcpEventDenormalizer: read params.update.sessionUpdate, not params.type

Bundle: remove negotiateTransport from barrel-reachable imports
Exports: add package.json deep import paths for ACP transports

Generated with AI

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

* test(sdk): comprehensive ACP transport test suite (~175 tests)

- RestSseTransport: fetch delegation, SSE subscribe, auth, timeout, signal
- AcpWsTransport: route mapping, token auth, event filtering, queue cap
- AcpHttpTransport: connectionId extraction, header injection, init retry
- AutoReconnectTransport: reconnect mutex, fallback, delegation
- negotiateTransport: capability probing, timeout, fallback
- acpRouteTable: URL→method mapping, param extraction

Generated with AI

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

* fix(sdk): route table coverage, browser WS auth, header forwarding, capabilities type

- Route table: add file/stat/list/glob/write/edit paths (all DaemonClient URLs)
- Route table: add session diagnostic routes (context, tasks, stats, rewind, language)
- Route table: add bulk sessions/delete
- WS auth: document browser limitation, Node uses headers, browser needs proxy
- Headers: forward X-Qwen-Client-Id via JSON-RPC _meta in WS transport
- DaemonCapabilities: add transports field to SDK type
- Package exports: remove unreachable deep exports, document monorepo usage
- Provider bypass: document limitation for glob/stat/list in workspace actions

Generated with AI

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

* fix(sdk): add missing detach + hooks routes per QA doc

Cross-referenced with daemon-acp-integration-qa.md route table.
Added POST /session/:id/detach and GET /session/:id/hooks.

Generated with AI

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

* fix(serve,sdk): enforce ACP standard session/new — always isolated session

ACP standard mandates session/new MUST create a new isolated session.

Server-side (dispatch.ts):
- Force sessionScope='thread' on /acp session/new, ignoring client params
- REST POST /session retains 'single' default for backward compat

SDK-side (acpRouteTable.ts):
- Strip sessionScope from session/new params in ACP transports
- Document that ACP follows the standard (no extensions)

Generated with AI

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

* fix(serve): ACP session/new returns standard models/modes fields

ACP standard NewSessionResponse includes optional `models` and `modes`
top-level fields alongside `configOptions`. Extract model/mode state
from configOptions and surface them as standard-shaped objects:
- models: { currentModelId, availableModels: [{id}] }
- modes:  { currentModeId, availableModes: [{id}] }

Also update test to verify sessionScope is always forced to 'thread'
(ACP standard compliance — session/new always creates isolated session).

Generated with AI

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

* feat(serve): add standard ACP methods session/set_mode, session/set_model, session/fork

Align /acp endpoint with ACP standard protocol:

- session/set_mode: dedicated method for mode changes (standard)
  Maps to bridge.setSessionApprovalMode(). Params: {modeId, sessionId}
- session/set_model: dedicated method for model changes (unstable)
  Maps to bridge.setSessionModel(). Params: {modelId, sessionId}
- session/fork: create a branched copy of an existing session
  Maps to bridge.branchSession(). Response includes configOptions,
  models, modes per ACP standard.
- session/load, session/resume: responses now include configOptions,
  models, modes (per ACP LoadSessionResponse/ResumeSessionResponse)

Generated with AI

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

* fix(serve): TS2345 — pass persist: false to setSessionApprovalMode

Generated with AI

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

* fix(webui): add dispose() to MockDaemonClient in provider tests

DaemonClient now has dispose() (called in provider cleanup effect).
Mock clients in test files need to implement it.

Generated with AI

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

* fix(serve): add sessionId pre-validation + remove type assertion

- session/set_mode, session/set_model: add explicit sessionId empty
  check before requireOwned (consistent with session/fork)
- session/set_model: remove `as unknown as` type assertion, pass
  proper {modelId, sessionId} matching SetSessionModelRequest

Generated with AI

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

* fix(sdk,serve): align route table with dispatcher + AcpHttp SSE response correlation

Route table:
- Add _qwen/ prefix to all vendor session/workspace methods
- Split workspace catch-all into granular dispatcher methods
- Fix session/branch → session/fork, model → session/set_model
- Remove routes with no dispatcher handler

AcpHttpTransport:
- Implement conn-scoped SSE stream for response correlation
- POST returns 202 (ack), real response rides SSE stream
- Map<id, {resolve, reject}> for pending request correlation

dispatch.ts:
- Remove session/set_mode, session/set_model from CONN_ROUTED_METHODS

Generated with AI

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

* fix(sdk): bump browser bundle budget 116KB→118KB for transport abstraction

Main uses 117,753 bytes (99.1% of 116KB budget). The transport
abstraction adds ~1.5KB (DaemonTransport interface + RestSseTransport
default constructor in DaemonClient). Bump to 118KB (120,832 bytes).

Also change RestSseTransport to type-only export from barrel (class
is constructed internally by DaemonClient, not needed as a value
export for consumers).

Generated with AI

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

* fix(sdk): fix 2 test failures — SSE error message + workspace catch-all route

- RestSseTransport: error message 'SSE response has no body' → 'No SSE body'
  (matches existing DaemonClient.test.ts assertion)
- acpRouteTable: re-add GET/POST /workspace/* catch-all after granular routes
  (AcpWsTransport.test.ts expects generic workspace path to resolve)

Generated with AI

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

* fix(sdk): align RestSseTransport test with updated error message

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-14 02:37:06 +08:00
Shaojin Wen
dc6edcd523
feat(web-shell): show time on parallel-agents box and sub-agent tools (#5084)
The message-time-on-hover feature (#5079) wraps each transcript message
with MessageTimestamp, but two sub-agent surfaces were left out and so
looked inconsistent with the rest of the transcript:

- The "Parallel agents · N/N done" box (ParallelAgentsGroup) renders
  directly in MessageList, bypassing MessageItem/MessageTimestamp, so it
  showed no time. Carry the first grouped launch's timestamp onto the
  parallel_agents display item and wrap the box in MessageTimestamp.

- Each sub-tool row inside a SubAgentPanel's Tools list showed no time.
  Wrap each row in a scoped hover tooltip (.toolTimeRow/.toolTimeTip,
  kept separate from MessageTimestamp's .row/.tip so the nested tooltip
  stays independent of the enclosing message's) keyed off the tool's
  startTime.

Both reuse formatTimestamp for an identical HH:mm:ss (or dated) format
revealed on hover in the top-right corner, matching the main transcript.
2026-06-14 00:46:42 +08:00
Yufeng He
c631d3af58
fix(cli): show plan gate failures with full plan (#5077) 2026-06-13 23:29:51 +08:00
tanzhenxin
2ba4ca90ad
feat(core): durable cron jobs — /loop tasks that survive restarts (#5004)
Some checks are pending
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Classify PR (push) Waiting to run
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
Persist /loop tasks per-project under ~/.qwen/tmp/<project-hash>/ so they survive restarts; the default stays session-only. Missed one-shots are surfaced at startup confirm-first; overdue recurring jobs catch up once then resume. A per-project lock elects a single firing session across concurrent sessions, with takeover on owner exit. Recurring jobs expire after 7 days (final fire), and never-matching cron expressions are rejected at creation. Durable storage lives in the user runtime dir, not the working tree, so it is never committed or shared via the repo.
2026-06-13 19:30:40 +08:00
jinye
acb0275ecd
fix(serve): Add prompt queue backpressure (#5033)
* fix(serve): add prompt queue backpressure

Add per-session prompt admission limits across the bridge, REST and ACP entrypoints, and SDK clients. The server now rejects full prompt queues before returning accepted semantics, advertises the active limit through capabilities, and documents the behavior with focused tests and design artifacts.

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

* test(sdk): stabilize pending prompt cleanup

Close the mocked SSE stream explicitly in the pending prompt cap test so cleanup does not rely on abort-driven stream cancellation timing in CI.

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

* test(sdk): stabilize subscription prompt race

Reject accepted subscription prompts if the event stream has already ended, and make the prompt-cap tests wait for the pending registration before closing or injecting SSE frames.

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

* fix(sdk): map prompt queue full responses

Map server-side prompt_queue_full responses to DaemonPendingPromptLimitError for both blocking and non-blocking prompt calls, include the session id in the local limit error, and cross-reference the duplicated default prompt cap constants.

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

* test: keep qwen planning docs ignored

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

* fix(serve): address prompt backpressure review

Log synchronous prompt queue rejections, document the sync admission contract, clean up SDK prompt-slot release, and cover the reviewed backpressure edge cases.

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

* fix(sdk): restore daemon bundle budget headroom

Reduce the generated daemon client bundle slightly and raise the browser daemon SDK bundle budget to 116 KiB so the PR merge ref has practical headroom.

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-13 18:46:01 +08:00
Shaojin Wen
84d01e7070
feat(web-shell): show message time on hover (#5079)
* fix(acp-bridge): preserve original timestamp when replaying session history

History replay re-emits each persisted record with its original epoch-ms time nested in update._meta, but BridgeClient.sessionUpdate published the frame without lifting it to the envelope. EventBus.publish then stamped envelope _meta.serverTimestamp with publish-time Date.now(), which the client's extractServerTimestamp picks up at higher priority than the nested original — so a resumed session rendered every historical message at the resume moment instead of when it was sent.

Lift update._meta.timestamp (or serverTimestamp) to the envelope serverTimestamp so EventBus preserves it. Live updates without such a timestamp keep the Date.now() fallback unchanged.

* feat(web-shell): show each history message's time on hover

Carry each transcript block's wall-clock time (serverTimestamp ?? clientReceivedAt) onto every message and reveal it as a CSS-only hover tooltip in the message list. Same-day messages show HH:mm:ss; older ones show yyyy-MM-dd HH:mm:ss (local time, zero-padded).
2026-06-13 15:30:44 +08:00
ytahdn
aebf82cd29
fix(web-shell): improve slash command panel layering (#5078)
Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-06-13 14:41:48 +08:00
Shaojin Wen
b748ef4b73
feat(web-shell): revamp floating todo panel interactions (#5069)
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
The "Current tasks" panel above the composer was a static display:
always expanded, rotate-to-front ordering with jumbled numbering,
no progress summary, and it vanished the instant the last item
completed.

- Collapsible header (persisted in localStorage); collapsed mode is a
  single line showing progress + the current in-progress item
- Progress counter (completed/total) in the header
- Natural-order window anchored on the in-progress item replaces the
  rotation: one completed context line above, pendings below, with
  clickable "N completed" / "N more" summary lines that expand the
  full list (and "Show less" to return)
- All-done moment: a finished list stays visible as "All tasks
  completed" until the next user prompt instead of disappearing
  instantly; historical finished lists stay hidden on session restore
- Locate button scrolls the transcript to the source TodoWrite/plan
  message with a flash highlight (new MessageList imperative
  scrollToMessage, callId fallback for compact-merged tool groups)
- Visual consistency: in_progress uses the accent color, PlanMessage
  adopts the shared icon set, items ellipsize to one line with a
  hover tooltip, and the number column scales past 9 items so the
  status icons stay aligned

getFloatingTodos moves to utils/todos.ts and now reports
{todos, allCompleted, sourceMessageId, sourceCallId}; panel visibility
is a render-time state machine so the active-to-completed transition
does not unmount the panel for a frame. New i18n keys for en/zh-CN
and 17 new unit tests.
2026-06-13 11:51:03 +08:00
ytahdn
c61006b978
feat(web-shell): daemon web-shell improvements — token usage, settings, retry, streaming metrics, hidden commands (#5066)
* feat(web-shell): daemon web-shell improvements

- Align daemon token usage with structured DaemonTokenUsage type
- Optimize settings panel with i18n, theme/language pickers, compact mode
- Handle missing session recovery (404/410) with configurable behavior
- Restore settings event signal bump for workspace changes
- Prevent queued prompt loss on useEffect dependency change
- Align streaming loading indicator with CLI metrics logic
- Add Ctrl+Y retry for turn_error with daemon support
- Hide non-essential UI elements on narrow screens (≤700px)
- Prevent loading indicator flicker on page refresh
- Hydrate displayName from persisted session title on load

* fix(web-shell): harden retry affordance

* fix(web-shell): gate retry handling

---------

Co-authored-by: ytahdn <ytahdn@gmail.com>
2026-06-13 02:58:08 +00:00