Commit graph

4 commits

Author SHA1 Message Date
Tianyuan
71165fce0f
feat(cli): improve subagent observability — untruncated live commands, transcript path, approval context (#6580)
* feat(cli): improve subagent observability in detail view and approval prompts

The agent detail view now renders the newest in-progress tool call in full (wrapped) instead of truncating it to one line, shows up to 10 recent activities (registry buffer raised from 5), and surfaces the JSONL transcript path so the full execution trace can be followed live or reviewed post-mortem. Inline subagent approval prompts now show the agent's last three prior tool calls as context for the permission decision.

Refs #6569

* refactor(cli): address review — shared tool display map, C0 sanitization, named caps, boundary tests

Extract the four duplicated tool-name→display-name maps into a single shared module; export the activity buffer cap from core so the detail view's slice bound can't drift; sanitize bare C0 control bytes in the live activity row and approval context lines; name the live-label and approval-context magic numbers; add boundary tests for the progress buffer and approval-context edge cases (empty context, executing glyph, unknown tool fallback, empty description).

Refs #6569

* fix(cli): keep live-row prefix spacing under MaxSizedBox wrap layout

The wrapped live activity row rendered as `>Shell(...` — MaxSizedBox's wrap layout drops the prefix's trailing space when prefix and label arrive as separate Text segments. Concatenate them into a single string child so the glyph renders as `> Shell(...`.

* test(integration): add deterministic capture scenario for subagent observability evidence

Drives the fake OpenAI server through an 11-read + long-shell-command subagent run and screenshots the detail view (yolo) and the inline approval banner (default mode) — the reproducible source of PR #6580's Before/After captures.

* refactor(cli): address follow-up review — consistent C0 sanitization, named web-shell activity cap

Switch the two remaining activity-label surfaces (live panel row, inline parallel-agents row) to the same bare-C0-stripping sanitizer the dialog and approval context use; name web-shell's 5-row activity display budget with a comment marking the intentional divergence from core's 10-entry retention cap.

Refs #6569

* fix(cli): budget subagent observability surfaces against short terminals

Addresses the review verification report on #6580 (issue #6569):

- Approval context (Finding 1, blocking): the prior-call context lines
  added above a subagent approval prompt were unbudgeted, so on a short
  terminal they pushed the question and its options off-screen and Enter
  approved blind. Reserve one line per rendered context call out of the
  confirmation's height budget so the confirmation prompt always wins.
- Detail view live row (Finding 2): MaxSizedBox clips from the bottom, so
  a full 10-row history could push the live command and Transcript pointer
  off a short terminal. Render the live row separately and budget the
  always-valuable sections first, dropping the OLDEST history rows when
  space is tight so the live row survives. Move Transcript above Prompt.
- Transcript path (Finding 3): render the path with wrap="wrap" instead of
  truncate-end so the full ~130-char path can actually be copied / tail -f'd.
- Minor: correct the MAX_LIVE_LABEL_CHARS comment (a very large terminal
  could display past the cap; on such a description the row is truncated).

Also addresses inline suggestions: cache hot-path string-width measurements
in the detail view, sanitize the transcript path for defense-in-depth,
update the height-budget comment to mention the Transcript section, and add
a tool-display-map parity test that fails on core ToolNames/ToolDisplayNames
drift. New regression tests cover the confirmation-height reservation, the
short-terminal live-row survival, and the full transcript path.

* fix(cli): harden LiveAgentPanel task-description label against bare C0 controls

The glance-panel `label` (the subagent task description) still used
escapeAnsiCtrlCodes while the sibling `activity` line and every other
subagent surface were hardened to sanitizeMultilineForDisplay. The task
description is model-generated, so bare C0 controls (\r, BS, BEL) could
pass through the ANSI-sequence escape and corrupt the panel chrome. Route
it through sanitizeMultilineForDisplay to match.

* fix(cli): resolve duplicate i18n 'Transcript' key after main merge

Merging main brought in its own `Transcript` locale key (the conversation
TranscriptView), colliding with the one this PR added for the subagent
detail view — `no-dupe-keys` failed CI's ESLint step across en/zh/zh-TW.
Drop the PR's duplicate entries; `t('Transcript')` now resolves to the
shared canonical key (main's), so both surfaces render one consistent label.

* feat(web-shell): align tasks panel with CLI subagent surfaces

Address the three web-shell review suggestions on #6580. The web-shell is a
browser bundle with no dependency on @qwen-code/qwen-code-core, so it can't
import the CLI's shared helpers — mirror them locally instead:

- Sanitize LLM-generated activity descriptions: add `sanitizeControlChars`
  (mirrors the CLI's `sanitizeMultilineForDisplay`) and apply it in
  `formatActivityLabel`, so a stray \r/BEL/ESC can't garble the panel.
- Reduce display-name drift: document `TOOL_DISPLAY_NAMES` as a deliberate
  standalone copy of core's `ToolDisplayNames`, and sync the entries it was
  missing (loop_wakeup, create_sub_session, read_mcp_resource,
  team_plan_approval, artifact, record_artifact) with their zh translations.
- Add test coverage: sanitizeControlChars unit tests, and a render test
  asserting the detail progress list caps at the newest
  MAX_DISPLAYED_ACTIVITIES rows.

* test(web-shell): polyfill Range.getClientRects for CodeMirror in jsdom

CodeMirror's text measurement calls `range.getClientRects()` from a
requestAnimationFrame measure pass. jsdom (26.1.0) doesn't implement
`Range.prototype.getClientRects` / `getBoundingClientRect`, so that async
callback throws `TypeError: getClientRects is not a function`. Vitest
surfaces it as an *unhandled error* that fails the whole run (exit 1) even
though every test passes — and because it depends on rAF timing it's flaky
(it fired on CI, passed locally). `useComposerCore.dom.test.tsx` (added via
the main merge) is the trigger. Polyfill both Range methods in the shared
test setup with empty rects; CodeMirror already handles the no-layout case.

* fix(cli): also reserve the approval header line in the confirmation budget

Follow-up to the approval-context budgeting: the "Approval requested by"
header renders one line above the context block, but only `contextLines`
was subtracted from the confirmation's `availableTerminalHeight`. Total
rendered height was `1 (header) + contextLines + (availableHeight -
contextLines)` = `availableHeight + 1`, so on a short terminal the
confirmation options overflowed by one line — the exact blind-approval
failure this budgeting prevents. Subtract the header line too.

* test(cli): prove the live row survives the short-terminal budget (no transcript)

The re-review flagged a suspected off-by-one in `reservedLines` (missing the
Progress spacer+header). It isn't: the `2` in the `liveActivity` reservation
IS the Progress `<Box/>` spacer + bold header, and the live row has no spacer
of its own — so the accounting already balances (reservedLines + historyBudget
== maxHeight). Add the exact reviewer scenario as a regression test — 10
activities, no outputFile, maxHeight=14 — asserting the live command still
renders. It passes, confirming no clip.

* test(web-shell): guard TOOL_DISPLAY_NAMES against drift from core ToolNames

The web-shell map is a standalone copy of core's tool list (the browser
bundle can't depend on core). Add a build-time drift guard that reads core's
tool-names.ts source and asserts this map covers every enumerated wire name,
so a tool added to core can't silently leak a raw internal name into the web
panel via the fallback. Mirrors the CLI's tool-display-map.test.ts parity
check without pulling core into the bundle.

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-07-11 11:21:23 +00:00
Tianyuan
075c3f03e5
feat(cli): forward ask_user_question answers from SDK can_use_tool (#6655)
* feat(cli): forward ask_user_question answers from SDK can_use_tool

SDK-hosted agents could receive ask_user_question calls through the
can_use_tool callback and approve them, but the user's answers never
reached the tool: the CLI called onConfirm(ProceedOnce) with no payload,
so the tool read an empty answers map and the model never got the
decisions.

Route updatedInput.answers from the SDK's allow response into the tool
confirmation payload so the collected answers reach the tool. Reuses the
existing updatedInput channel — no new SDK API or types. Document the
pattern in the TypeScript and Python SDK READMEs.

* fix(cli): forward ask_user_question answers on teammate approval path

Address review feedback on #6655:

- handleTeammateApproval now mirrors the leader path and promotes the
  user's answers from updatedInput into the confirmation payload, so
  ask_user_question calls approved through a teammate no longer drop the
  user's choices (wenshao).
- Extract a shared buildAllowConfirmationPayload helper used by both the
  leader and teammate paths, and only promote `answers` for
  ask_user_question so a same-named field on any other tool's input can't
  leak into the payload.
- Add tests for the teammate path and the defensive guards (array
  updatedInput, array/null/empty answers, foreign answers field).

* test(web-shell): stub Range client-rect methods to fix flaky CI

CodeMirror's async measure pass (scheduled via requestAnimationFrame)
calls getClientRects()/getBoundingClientRect() on a text Range. jsdom
implements these on Element but not on Range, so the call throws
"textRange(...).getClientRects is not a function" from a rAF callback
after the test completed. Vitest surfaces it as an unhandled error and
fails the whole run with exit code 1 even though every assertion passed
(seen intermittently in useComposerCore.dom.test.tsx).

Polyfill both methods on Range.prototype in the shared test setup,
mirroring the existing ResizeObserver/scrollIntoView stubs.

* refactor(cli): use ToolNames constant and broaden permission tests

Address review suggestions on #6655:

- buildAllowConfirmationPayload now gates answers-promotion on the
  ToolNames.ASK_USER_QUESTION constant instead of a bare string literal,
  so a future rename of the tool name is a compile-time break rather than
  a silent regression.
- Add an it.each case for a non-object primitive updatedInput (string) to
  cover the `typeof updatedInput !== 'object'` guard branch.
- Assert the leader path overrides toolCall.request.args with the host's
  sanitized updatedInput before confirming.
- Add a teammate-path test for an allow response with no updatedInput,
  asserting respond is called with (ProceedOnce, undefined).

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-10 23:53:46 +00:00
易良
f06e932260
fix(web-shell): polyfill Range layout APIs in tests (#6677)
* fix(web-shell): polyfill range layout in tests

* test(webui): stabilize heartbeat prompt cleanup
2026-07-10 15:18:48 +00:00
ermin.zem
5c82857fea
Add harness infrastructure for web-shell package (#6517)
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
E2E Tests / web-shell Browser Regression (push) Waiting to run
* test(web-shell): add browser and lint harness

* test(web-shell): harden browser smoke harness

* fix(web-shell): guard mock daemon model state

* test(web-shell): remove unused scenario harness

* fix(web-shell): remove stale lint disables

* test(web-shell): make matchMedia stub writable

* fix(web-shell): exclude tests from package typecheck

* test(web-shell): tighten mock daemon route contract

* Update packages/web-shell/client/e2e/utils/mockDaemon.ts

Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>

* test(web-shell): clear stale SSE connections

* ci(web-shell): gate smoke on full CI profile

---------

Co-authored-by: ermin.zem <ermin.zem@alibaba-inc.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
Co-authored-by: 易良 <1204183885@qq.com>
2026-07-09 08:11:58 +00:00