Commit Graph
11847 Commits
Author SHA1 Message Date
易良andQwen-Coder b142071ab2 fix(core): report redirects on cd segments (#16129)
* fix(core): report redirects on cd segments

Preserve redirect-derived operations when resolving cd, pushd, and popd segments so Write rules see targets opened before the cwd changes.

Co-authored-by: Qwen-Coder <[email protected]>

* test(core): rename stale cd redirect test title

The title said 'ignores redirects attached to cd' but that is the
behavior this PR removes; the row survives only because >/dev/null is
filtered. Rename it to describe what it actually pins now.

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 22:03:14 +08:00
易良 c2921217ef ci: gate on the OS temp directory and shorten /tmp retention to 1d (#16126)
* ci: gate on the OS temp directory and shorten /tmp retention to 1d

The 2026-10-31 hz incident: /tmp tmpfs hit 100% inodes while the
workspace and RUNNER_TEMP sat on a data disk with millions free, so
every disk-floor gate was green and jobs died on mkdtemp ENOSPC
mid-suite. The gate now always checks the OS temp directory
(deduplicated against caller arguments); the tmpfiles policy drops
7d->1d because the measured 120-260k files/day steady state exceeds
the tmpfs inode ceiling at 7d. Mutation-verified: dropping the
os.tmpdir() check reds the suite.

* test(ci): cover the TMPDIR-unset default and pin the 1d retention

Review findings: the new OS-temp test never executed the production
incident path (TMPDIR unset -> /tmp default) — a stripped default
survived the suite green; and the 7d->1d retention change had no test
coverage — a revert shipped the incident again. Add the unset-TMPDIR
sub-run, a set-equality dedup assertion, and a qwen-docker-cleanup
suite pin on the exact tmpfiles line. Mutations verified red.

* test(ci): pin RUNNER_TEMP apart so the /tmp fallback test bites

The first version of the TMPDIR-unset test let the RUNNER_TEMP default
(/tmp) satisfy the assertion, so a stripped ${TMPDIR:-/tmp} default
survived green — the exact regression the test exists to catch. Pin
RUNNER_TEMP to a distinct directory; the /tmp sample can then only come
from the OS-temp append. Mutation now reds.

* test(ci): unset TMPDIR as empty string; destructure was a no-op

The previous fix still failed on clean trees: spreading a destructured
env cannot DELETE a key — process.env.TMPDIR re-entered via runGate's
own spread, so the /tmp fallback never fired and the test failed
against the correct script. An empty-string TMPDIR is bash-unset for
${TMPDIR:-/tmp} (and bare for the regression), which is the exact
distinction under test. Clean 8/8; stripped-default mutation reds.

* ci(ecs-runner): enforce the /tmp retention from the owned daily service

Review finding: the 1d policy's enforcement agent was the distro's
systemd-tmpfiles-clean.timer, which can be masked on hardened images and
reports 'static' (unverifiable via is-enabled) on stock Ubuntu — with
nothing in the qwen stack running systemd-tmpfiles --clean, the measured
120-260k files/day steady state regrows past the tmpfs inode ceiling and
the 2026-10-31 incident class recurs with every test green. Add
ExecStartPost=systemd-tmpfiles --clean --prefix=/tmp to the daily
qwen-docker-cleanup.service, pin it in its test suite, and document the
enforcement owner in the README verification block. Mutation-verified.
2026-11-02 22:02:36 +08:00
a983503f02 fix(core): attribute shell notifications to the registering turn (#16125)
A background shell that settles mid-turn never reached the model until the
turn ended: ShellNotificationMeta carried no sourceTurnId, so
Session.#takeCurrentTurnBackgroundParts — which skips queued items whose
sourceTurnId differs from the current turn — could never match the item,
and delivery fell through to the end-of-turn drain.

Capture the registering turn in BackgroundShellRegistry.register(),
mirroring BackgroundTaskRegistry (getInvocationContext()?.promptId ??
promptIdContext.getStore()), declare it on ShellTask, and forward it
through ShellNotificationMeta into the Session shell callback. Both the
direct is_background: true spawn and the foreground→background promote
funnel through register(), so one assignment covers both; a promoted
shell is attributed to the promoting turn, which is the turn that ends up
waiting for the result.

Monitor and workflow callbacks have the same gap; left as a follow-up.

Guarded by two registry tests: the completion meta carries the
registering turn, and a shell registered with no turn context stays
unattributed — and still emits — rather than inventing an attribution.


Patrol-Run: qwen-issue-patrol/jmvz3emo2tv

Co-authored-by: yiliang114 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 22:02:00 +08:00
jinyeandQwen-Coder f643e579c5 fix(managed-agent): Restore profile-owned runtime verification (#16134)
Keep the shared Failsafe configuration in pluginManagement so ordinary
release verification does not activate environment-specific integration tests.
Preserve the dedicated profiles and executable artifact assertions.

Backport the same POM repair to the pinned 0.26.5 source with checked patch
digests, and verify both patches before applying either one.

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 13:50:11 +00:00
易良andQwen-Coder e7b86ae32d fix(mcp): warn about disabled tool typos on every discovery path (#16120)
Refs #16078

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 21:41:17 +08:00
Shaojin Wen 9336986835 feat(web-shell): make GitHub channel configuration usable (#16096)
* feat(web-shell): make GitHub channel configuration usable

The channel editor rendered every platform through one generic form, which
left the GitHub channel hard to configure and persisted settings its adapter
never reads.

- Render string-list fields that declare options as a checkbox list, so the
  fifteen notification reasons are pickable and explained instead of typed
  from memory. Unknown values written by hand stay visible and removable
  rather than disappearing on the next save.
- Lock the token input while local gh authentication is selected, clear a
  stored token when that switch is turned on, and drop a token that an
  existing useLocalGh selection has already overridden.
- Hide and stop persisting the direct-message policy for repository-scoped
  channels: the registry injects privatePolicy into every manageable channel,
  but the GitHub adapter only produces group envelopes, so the value never
  gates anything and only misleads.
- Name allowlisted groups repositories, validate owner/repo, and show the
  GitHub Enterprise API base URL shape.
- Split the catch-all credentials section into credentials, connection, and
  notification routing.

* test(web-shell): pin the GitHub editor capture to the reason checklist

The screenshot scenario now fails if the filter falls back to a free-text
input, so the published picture cannot drift away from the behaviour the
pull request claims.

* test(web-shell): pin the section split on a non-GitHub channel

The connection and routing sections key on field names, so they regroup gitlab and wecom fields too. Assert that split on a gitlab descriptor instead of leaving it implied by the GitHub assertions.

* fix(web-shell): match GitHub channel values the way the adapter reads them

Review round 1 on the editor found three places where the new controls
disagreed with the runtime or left dead configuration behind.

- The reason checklist compared values case-sensitively while the adapter
  lowercases both sides, so a stored COMMENT rendered as a stray sixteenth
  box beside an unchecked canonical one; ticking only mention then left
  comment notifications flowing. Compare case-insensitively and keep unknown
  entries in their original spelling.
- The pairing guard had no test witness: deleting it left the whole suite
  green while a legacy privatePolicy reopened the direct-message panel for a
  repository channel. Pin both directions — legacy private pairing must not
  open the panel, group pairing still must.
- Only privatePolicy was cleaned up; the equally unread allowedUsers survived
  every save and was still presented as an authoritative allowlist in the
  pairing panel. Drop the whole private-access set and stop showing it there.

* fix(channels): match GitHub repository groups regardless of case

The editor started validating repository allowlists as owner/repo, but a
repository path is case-insensitive while the runtime matched the group key
verbatim. An operator who typed qwenlm/qwen-code passed validation, saved, and
got a channel that connected and then dropped every notification as
not_allowlisted — the exact failure the validation was added to prevent.

Normalize on both sides so neither can drift:

- The editor writes the canonical lower-case key and shows stored allowlists
  in that form, so re-saving an older mixed-case entry migrates it. The
  per-repository settings are looked up under the normalized key, so retyping
  the case no longer discards that repository's sender policy.
- GroupGate and ChannelBase accept a platform group-ID normalizer, used for
  the allowlist, the sender policy and the per-group overrides alike.
  Normalizing only admission would have let a case-mismatched repository fall
  back to the open sender default, which is worse than dropping it.
- The GitHub adapter supplies the lower-casing normalizer; every other
  platform keeps matching group IDs verbatim.

Pairing approvals are unaffected: they are keyed by the chat ID the platform
reports, so they were already self-consistent.

* fix(channels): resolve the group history limit through the same lookup

Self-audit of the repository-casing fix found a third direct read of
config.groups: the per-group history limit still matched the key verbatim, so
a platform with case-insensitive IDs silently lost that override while the
allowlist and sender policy honoured it. Route it through groupConfigFor and
pin it with a case-insensitive channel test.

* fix(web-shell): keep the gh switch reversible and refuse ambiguous repositories

Review round 2 plus the independent verification round found three defects.

- Turning local gh authentication on queues the stored token for removal,
  because a token outranks gh. Turning it back off left that intent in place,
  so Save demanded a credential the operator never chose to delete and the
  switch could not be undone without retyping a PAT. Flipping it back now
  restores the stored token.
- Collapsing repository aliases could widen access. A stored allowlist may
  hold Owner/repo and owner/repo with different settings; the runtime resolves
  an exact key first, so keeping only one of them on an unrelated save dropped
  the effective sender restriction. The editor now refuses to save until the
  operator resolves the duplicate, and shows the allowlist without repeating
  the same repository twice.
- The reason descriptions described event types instead of the subscription
  reasons GitHub actually reports: comment means the account commented on the
  thread, state_change that the account changed its state, ci_activity that a
  workflow it triggered completed, and manual that it subscribed by hand.
  Rewritten in both languages against the REST notification contract.

The visual scenario also captured the dialog without scrolling, so the
checklist sat below the fold and the published picture did not show the
feature. It now scrolls the filter into view, asserts it is fully on screen,
and captures it separately.
2026-11-02 13:11:33 +00:00
qqqys 73474ef094 fix(mods): skip hooks on prompt.paint and ui.place instead of failing (#16124)
A Mod that hooked either event failed to initialize, which stopped a headless run before any model request. Both are now skipped with the startup line, like other events Qwen Code does not run yet. $.session.version() reports 2.1.316.
2026-11-02 12:50:30 +00:00
38b71299c8 feat(desktop): build and publish rpm installers (#16060)
* feat(desktop): build and publish rpm installers

Add Fedora 44 and RHEL 10 release roots, strict RPM smoke checks, and artifact naming that keeps each root distinct.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(desktop): declare rpm runtime requirements and clear the yaml gate

The rpm legs built artifacts with no `Requires:` entries: Tauri's rpm bundler
writes them only from `bundle.linux.rpm.depends`, which nothing set, so
`verifyRpmDependencies` had no webkit soname and no glibc floor to find and the
release could not pass its own gate.

- Add `rpm-fedora44.conf.json` / `rpm-rhel10.conf.json` overlays declaring the
  webkit soname and each root's glibc floor (2.43 vs 2.39 cannot share one
  config), and pass the per-leg overlay to `tauri build --config`.
- Quote-free `container: ${{ matrix.container || null }}` stays as is with an
  inline yamllint disable: quoting the expression would turn a null container
  into an empty string. The shape test pinning that literal moves with it.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy1jgjo76

* ci(desktop): record the rpm legs in the workflow size baseline

`desktop-release.yml` is now 49858 bytes, past its recorded 45089 plus the
4096 allowance, so `Lint & Static` fails on the ratchet before any linter runs.
The growth is the two rpm legs: their matrix entries, the per-leg overlay
wiring, and the roots' comments. Moving that prose out would not pay for it —
the legs themselves are the bytes — so the number moves with the file in the
same PR, as the gate asks.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy1jgjo76

* style(desktop): apply prettier to the rpm legs and realign the workflow shape pin

Prettier flagged .github/workflows/desktop-release.yml and
packages/desktop/scripts/smoke-linux-installers.js on head 10acaadeba.
test-release.js pinned that container line with two spaces before the inline
yamllint disable comment, which prettier rewrites to one; .yamllint.yml has
min-spaces-from-content: 1, so the pin now follows the formatter instead of
blocking it.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy3ombr79

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: yiliang114 <[email protected]>
2026-11-02 20:50:05 +08:00
jinyeandQwen-Coder d9d6fc4a4b refactor(managed-agent): split Agent and Session stores (#16090)
* test(managed-agent): audit lock order across store collaborators

Record the bilingual Store decomposition contract and entry ownership,
transaction, lock and caller inventories. Follow final collaborator fields
across source files while preserving owner-bound facade populations and
existing ambiguity, transaction and compiled-member checks.

Co-authored-by: Qwen-Coder <[email protected]>

* refactor(managed-agent): split Agent and Session stores

Move Agent and Journal responsibilities into immutable internal collaborators while retaining transaction facades and compatibility bridges. Resolve Journal dependencies at construction and migrate factories, fixtures, lock auditing and the bilingual implementation contract.

Record focused verification and outstanding full-gate requirements separately.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(managed-agent): repair store decomposition verification

Keep declared field receivers visible across method-local type scopes while retaining fail-closed nested class handling. Preserve the Hosted CSI dependency identity assertion at its new owner.

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 12:46:43 +00:00
易良andQwen-Coder 8aea64d5f7 fix(desktop): verify AppImage library closure (#16062)
Read every ELF's DT_NEEDED entries from the extracted AppImage and fail release when a dependency is neither bundled nor part of AppImage's host-provided baseline. Document the contract and cover the failing and repaired cases.

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 19:43:23 +08:00
BaboBen 84c4c70789 fix(channels): allow request owners to answer one-time tool permissions (#16087)
* fix(channels): allow request owners to answer one-time tool permissions

* docs(channels): record permission-owner verification
2026-11-02 19:41:43 +08:00
易良andQwen-Coder 5df87f7080 fix(desktop): stop the bundled runtime from the Windows installer (#16071)
An update started by a desktop build released before the app-side runtime stop can still leave the bundled Node daemon holding node.exe while NSIS replaces the runtime. Clear that lock from the installer pre-install hook by image path, wait boundedly for the image to become writable, and abort before copying unless PowerShell returns an explicit success. Pin the behavior in the desktop release gate.

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 19:40:13 +08:00
Shaojin WenandQwen-Coder 60f5c1ed95 docs(managed-agent): record the completed store constructor injection in the ledger (#16118)
#16061 replaced the seven ManagedAgentStore public setters with a single
@Autowired constructor holding final references, but the parent
structure-review ledger still described the setters as current state and
framed the conversion as future work. Update the D1 row note, the prose and
the P1 store narrative to the implemented state in both languages, keep the
setter investigation as pre-conversion context, and leave the physical
aggregate split as the remaining work.

Figures re-measured against origin/main (0 public setters, 1 @Autowired
constructor, final fields, 7657 lines); prettier and bilingual heading/link
checks pass. Documentation-only.

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 17:38:19 +08:00
qqqysandShaojin Wen 9c96f5aa82 feat(mods): run prompt.submit hooks in headless runs (#15905)
* feat(mods): run prompt.submit hooks in headless runs

Mods' prompt.submit hooks can rewrite, add context to, or drop each headless prompt before UserPromptSubmit, @ processing and the transcript see it. Terminals keep skipping these hooks until they raise the event. Includes the design document for the four steps.

* test(mods): update bundled worker load response expectation

* fix(mods): preserve downstream prompt submit outcomes

* fix(mods): isolate prompt fallback from host continuations

* fix(mods): preserve latest local prompt outcome in recovery

* fix(mods): hear only a message's text and take any answer after next

A stream-json message with an image block reached the prompt.submit
hooks as its serialized blocks, so they were skipped as too long or saw
JSON noise, and a rewrite carried that JSON into humanInput and
turn.start. The hooks now hear its text blocks; a rewrite replaces them
and the other blocks follow it.

A hook's answer after its next changes nothing, but it was still
validated, so a hook that only watches and returns nothing, or spreads
the event back, failed and logged a skip on every prompt. Only a drop
there is now checked.

* fix(mods): keep prompt.submit to typed text and drop empty rewrites

- Show only the rewritten text in the transcript and UserPromptSubmit,
  not the serialized non-text blocks of a stream-json message.
- Skip prompt.submit for SDK messages that answer a tool call or carry
  tool blocks, and for messages without text.
- Drop a prompt the hooks rewrote to nothing instead of sending the
  model an empty prompt.

* fix(mods): send each prompt context once on its own

The Mods' prompt.submit context and UserPromptSubmit's additionalContext
share one hidden block, and the block was only skipped when the
conversation already held it whole. A Mod whose context changes every
turn made every block new, so an unchanged UserPromptSubmit context went
to the model again each turn.

The injected part now records the contexts it joins, and a context counts
as delivered when an earlier block holds it, so only the contexts the
conversation lacks are sent. A block without that record, as after
resume, still counts as one context.

* fix(mods): keep a message's blocks in the transcript after a rewrite

A stream-json message with blocks other than text is recorded with those
blocks serialized after its text. A prompt.submit rewrite recorded the
rewritten text alone, so the same kind of message showed two shapes in the
transcript. The rewritten prompt is now recorded with the blocks after it.

Adds the headless E2E test plan and its results.

---------

Co-authored-by: Shaojin Wen <[email protected]>
2026-11-02 09:14:14 +00:00
qqqys 1e250b391b feat(acp): run re-entered, channel and Goal-proposal turns on the shared loop (#15902)
* feat(acp): run re-entered, channel and Goal-proposal turns on the shared loop

With experimental.unifiedAgentLoop on, channel prompts and Goal-proposal
approval turns run as a UserQuery on the shared round, the shared tool
batch and the client's stop point, and a retry, a continue and the
restore of an unanswered ask_user_question run there with a Retry first
round. ACP still strips and restores the re-entered attempt, still runs
UserPromptSubmit on a retry through admitUserPrompt, and still answers a
restored question with its own tool step.

Core: userTurnRecordedByHost also covers a Retry's todo work chain,
promptAdmission is accepted on a Retry, and the new input
historyPromptId gives a Retry's request the identity of the attempt it
replaces.

The ACP parity suite gains a channel prompt and a retry after a model
error.

* fix(acp): treat a re-entered turn's pushed first request as sent on every exit

On the shared round, whether the first request reached history is now
read when the round ends, on every exit. A retry whose request was
pushed and whose response then failed no longer puts the stripped
attempt back beside it, and a restore's one-shot notices are taken once
its answer is in history.

A continue whose first request never goes out, cancelled at the loop
top or failed, puts the interrupted run back as it was and keeps the
worktree notice its dropped message took pending. A restore cancelled
before its request went out keeps the full answer and takes its
notices.

The stripped-run restore and the Goal-proposal settlement block are
shared helpers. The parity suite runs the channel scenario from a
trusted parent so the channel mark survives, and adds a retry that
fails again. Docs and comments are corrected.

* fix(acp): keep a Goal proposal unsettled when its turn gives way or ends early

In plan mode, the Todo Stop Guard on the shared round now still yields
to a queued prompt or to input the round took, so a Goal proposal is not
started from a turn that gave way.

The client no longer drops a host-owned Goal proposal when a send ends
abnormally. The host takes it when its turn ends, so a cancelled
proposal turn still reports that the Goal did not start.

A Retry whose admitted prompt is blocked puts back the attempt the
client stripped from history.
2026-11-02 17:14:05 +08:00
jinyeandQwen-Coder 85943db2b3 fix(ci): repair Javadoc for the 0.26.5 managed runtime release (#16114)
Apply the already merged documentation-only repair to the original release source before the unchanged full verification and missing-only publication gates.

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 17:12:10 +08:00
qqqys 9d2ee877c2 feat(web-shell): plot per-request speed and failures in token usage (#16111)
* feat(web-shell): show request success rate and distinct model colors in token usage

The token usage panel tracked failed API calls but never showed them, and
models were hard to tell apart: the first two series colors were both blue,
every share bar started from the same blue, and the card had no color mark.

- Add a success rate to the overview and to each model card, with a status
  icon (ok / warning below 100% / error below 90%) and the failed count.
  Each retry counts as its own call, as in `api.totalErrors`.
- Add session output speed to the overview, weighted over all timed models.
- Replace the model palette with six categorical colors checked for
  color-vision-deficiency separation, with dark-theme steps. Colors follow
  the order models were first used, so a model keeps its color when the
  token ranking changes; models past the sixth share a neutral color.
- Add a stacked per-model share bar with a legend, and a color strip on
  each model card. Share text keeps the foreground color.

* feat(web-shell): plot per-request speed and failures in token usage

Token usage showed one average output speed per model, so a slowdown
mid-session or a few slow outliers looked the same as a steady rate,
and failed calls had no time or kind.

- Core keeps, per model in the stats buckets, the latest 200 timed
  responses (time, first token, generation window, output tokens,
  subagent) and failed calls by status code or error type with the
  latest 50. Session replay rebuilds both from stored events.
- The daemon stats route returns them as optional `timingSamples` and
  `failures`; older daemons omit them.
- Web Shell adds a speed-over-time chart: one dot per request in the
  model's color, a five-point median line, a toggle to first-token
  latency, failure marks, a nearest-request tooltip on hover or tap,
  and a legend that hides series.
- Model cards add an output-speed spread (p50, p10-p90 on a shared
  scale) and failures by kind.
- Add the design doc in English and Chinese, and point the token usage
  hierarchy doc at it for the model palette.
2026-11-02 08:26:53 +00:00
易良andQwen-Coder 292c0da525 test(install): pin Windows PATH persistence contract (#16072)
* test(install): pin Windows PATH persistence contract

Co-authored-by: Qwen-Coder <[email protected]>

* test(install): format the pinned PATH assertion

Prettier collapses the single-string expect(...).toContain(...) call at 76
columns, which the incremental lint gate rejected on the previous head.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvygjkzk7s

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 15:44:05 +08:00
qqqys 6b6df2a8b4 feat(web-shell): show a dismissible Workflow chip and menu summary (#16107)
* feat(web-shell): show a dismissible Workflow chip and menu summary

Workflow moved into the composer + menu, so once it was on the toolbar no
longer showed it. Show a Workflow chip in the toolbar while the saved
preference is on, the same way Plan does: hovering reveals a close mark,
clicking turns the mode off (also during a running response), and an
on-but-unavailable preference is tinted with the warning color. The menu
entry gains a one-line summary of what the mode does, or of its state when
it is not simply available.

* fix(web-shell): keep the Workflow chip warning off the Plan chip selectors
2026-11-02 07:32:44 +00:00
易良andQwen-Coder 73a7268447 test(cli): guard settings reference against schema drift (#16085)
Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 15:29:30 +08:00
0ebbe15221 fix(review): name the model half in the anchor re-run and drop the retired range model (#16094)
* fix(review): name the model half in the anchor re-run and drop the retired range model

F1 (rc:3808708929): the marker-recovery re-run sentence named only
`--since <sha>`, while the cache path two paragraphs above requires the
pair `--since <lastCommitSha> --since-model <lastModelId>`. A reader who
applied just the re-run sentence handed `fetch-pr` a sha with no model
half, which is exactly the anchor the same-model gate exists to rule on.
The sentence now carries `--since <sha> --since-model <model>` and says
the model half rides the same replacement rule, so the existing
"REPLACING any --since it already carries, never appending a second one"
semantics are unchanged.

F2 (rc:3810474869): the `effective: true` bullet still called the
report's diff and plan `since..head` — the full-range model the
incremental narrowing retired. It now names the sliced scope the report
publishes (`deltaFiles`, widened by `interaction[]`), consistent with the
`incremental.scope` wording in the same bullet.

Both are pinned by new assertions in SKILL.test.ts: the re-run sentence
is sliced to itself before asking for `--since-model` (the same words
occur in the cache path, where they were already correct), and the
`effective: true` bullet is sliced to the next bullet and asserted free
of `since..head` while still naming the three scope classes.

Refs #9654

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmvyftv3bt8

* docs(review): take both anchor values from the side file on the re-run

The marker-recovery re-run said to pass "exactly as the command above
carries it", but on this path that command carries no --since-model (no
cache) or another model's (cache anchor withheld) - so a literal reader
was refused as cross-model-anchor and reviewed the full diff, the very
failure the recovery path exists to remove.

Both values now come from the side file's sha/model pair, and the
replacement rule names both flags. The adjacent pin moves with the
sentence in the same commit.

Co-authored-by: Qwen-Coder <[email protected]>

Patrol-Run: qwen-pr-closeout/jmvyioqrq7v

---------

Co-authored-by: yiliang114 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 15:29:15 +08:00
易良andQwen-Coder 91bce47a14 fix(vscode): bucket session history by calendar week (#16097)
The session history dropdown grouped entries with a rolling 168-hour
window while the buckets are labelled Today / Yesterday / This Week /
Older. On a Monday, a session from the previous Saturday was still within
168 hours, so it showed up under "This Week" even though the calendar week
had only just started.

This Week now begins at the Monday-first start of the current calendar
week, derived from the same local day boundary as the Today/Yesterday
buckets. The boundary uses setDate rather than millisecond arithmetic so a
DST transition cannot shift it. Sessions before that boundary fall through
to Older, and Today/Yesterday are untouched.

Regression coverage pins a Monday with a previous-Saturday session (Older),
plus a Friday/Wednesday positive control proving This Week still exists
mid-week so the Monday assertion cannot pass on a missing group.


Patrol-Run: qwen-issue-patrol/jmvyk46lrtc

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 15:28:27 +08:00
ytahdnand钉萁 8de81eb209 fix(web-shell): tighten mobile composer layout (#16103)
Co-authored-by: 钉萁 <[email protected]>
2026-11-02 14:59:09 +08:00
qqqys 2a5d2ea6c6 feat(web-shell): show request success rate and distinct model colors in token usage (#16108)
The token usage panel tracked failed API calls but never showed them, and
models were hard to tell apart: the first two series colors were both blue,
every share bar started from the same blue, and the card had no color mark.

- Add a success rate to the overview and to each model card, with a status
  icon (ok / warning below 100% / error below 90%) and the failed count.
  Each retry counts as its own call, as in `api.totalErrors`.
- Add session output speed to the overview, weighted over all timed models.
- Replace the model palette with six categorical colors checked for
  color-vision-deficiency separation, with dark-theme steps. Colors follow
  the order models were first used, so a model keeps its color when the
  token ranking changes; models past the sixth share a neutral color.
- Add a stacked per-model share bar with a legend, and a color strip on
  each model card. Share text keeps the foreground color.
2026-11-02 14:03:36 +08:00
Shaojin WenandQwen-Coder 64651cde11 docs(managed-agent): reconcile the structure-review ledger with the broker extraction (#16109)
* docs(managed-agent): reconcile the structure-review ledger with the broker extraction

The binding registry and execution workflow slices (#16029, #16084)
extracted the reconciliation/recovery/cancellation cluster, but the parent
structure-review ledger still described a 6,329-line broker with only the
cooldown and hold inventory out, and still called the cluster entangled and
waiting for a registry. Update the D1 god-class rows, the surrounding prose,
the mirrored test size and the P1 extraction narrative to the measured
current state (4,270-line facade, nine collaborators), in both languages.

Every stated figure was re-measured against the source; prettier and the
bilingual heading/link checks pass. Documentation-only.

Co-authored-by: Qwen-Coder <[email protected]>

* docs(managed-agent): put the met Registry prerequisite in past tense

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 13:41:55 +08:00
Shaojin Wen 0a7f260704 fix(managed-agent): reach the listeners the server resolves in upgrade start and verify (#16051)
* fix(managed-agent): reach the listeners the server resolves in upgrade start and verify

start and verify derived the control-plane URL from server.env's
QWEN_MANAGED_AGENT_SERVER_ADDRESS and SERVER_PORT, and the Harness URL from
its QWEN_MANAGED_AGENT_HARNESS_BASE_URL. A port or address set anywhere else
(config/application.yml, an argument, SPRING_APPLICATION_JSON, a relaxed
SERVER_ADDRESS) or a management.server.port of its own made start wait out
--timeout on a port nothing served.

ResolvedConfiguration now also prints serverUrl, healthUrl and
harnessBaseUrl as Spring binds them: server.port, server.address and
server.ssl, with a wildcard reached on loopback, and the health endpoint by
Spring Boot's management-port rules. start resolves them before it changes
any unit, verify before its rows. --server-url still names the control
plane; a port of 0 or below refuses until it does; a configuration that
does not resolve falls back to the server.env URLs with a notice.

* fix(managed-agent): keep the health name for a blank path mapping

A blank management.endpoints.web.path-mapping.health, such as an unset
${HEALTH_PATH:}, is ignored by Spring Boot's MappingWebEndpointPathMapper,
so the endpoint stays at /actuator/health. The resolver turned it into an
empty segment and sent start and verify to /actuator, where start waited
out its timeout on a healthy server. Fall back to the name when the
mapping is blank, as Spring does.

Also from review:
- the fallback notice says only the Harness URL comes from server.env
  when --server-url names the control plane;
- start's success line names the API URL next to the health endpoint;
- verify with --server-url resolves the configuration only for --tenant,
  as it did before, since it does not reach the Harness.

* fix(managed-agent): refuse listener URLs fetch cannot dial in upgrade start and verify

A link-local server or management address keeps its IPv6 zone in the
resolved URL (http://[fe80:0:0:0:0:0:0:1%1]:8080), and the WHATWG URL
parser behind fetch rejects every zone spelling. The health probe caught
the TypeError as "not UP", so start waited out --timeout on a URL it could
never send. The same held for an out-of-range port, a server.env address
with a zone on the fallback path, and a --server-url that does not parse.

listeners() now checks both URLs with URL.canParse after the no-fixed-port
refusal, before any unit changes, and names the zone when that is why. The
resolver test pins that the zone is kept, so the script can say so. The
verify test lists the health probe with the session reads instead of
slicing it off.
2026-11-02 12:57:52 +08:00
易良andyiliang114 4700dd2368 fix(web-shell): put the list-failure notice under the tabs, not above them (#16104)
* fix(web-shell): put the list-failure notice under the tabs, not above them

#16082 merged with the notice as the nav's first child, so a failed session load
rendered above the Sessions/Archived tab bar and pushed the list's own controls
down - an error about the list appearing before the list's navigation. It now
renders after the tabs and the title filter, in the region the sessions occupy.

Also stops break-all from cutting the request id mid-token ('req' /
'uest-list-500'); break-words keeps it whole and wraps at a boundary.

Observed in a real browser at 1280x800 against a 500 from /sessions/query:
  before: nav children = [alert y=52, tabs y=180, search y=212]
  after:  nav children = [tabs y=52, search y=84, alert y=128]

The file also carried two prettier violations from the squash merge; prettier
--write normalises them, which is why the diff touches two unrelated indents.

* style(web-shell): keep the diff to the move and the word break

The previous commit ran a newer prettier than this repo pins, which reindented two
unrelated call sites and does not satisfy the repo's own --check. Redone from the
pristine file with the pinned prettier (3.6.1): the net diff against main is now
only the block move and break-all -> break-words.

---------

Co-authored-by: yiliang114 <[email protected]>
2026-11-02 12:50:12 +08:00
jinyeandQwen-Coder cd1e5feda5 test(managed-agent): Reconnect Hosted fault transport probes (#16101)
Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 12:43:17 +08:00
Shaojin Wen d36fdbdfe5 test(managed-agent): gate detach while a tool result is unknown (#16098)
The MCP detach must refuse before it releases anything when an
operation is recovery_blocked/outcome_unknown. Start an ext://hold
read, kill the stdio fixture, and call the Harness detach route; the
case requires the documented refusal, no release control, and the
operation still unknown.

The Hosted fork moves by the case's measured 27.4 s, booked at 55 s.
2026-11-02 11:44:00 +08:00
jinye 95e647754f fix(managed-agent): keep javadoc valid after the module split (#16099)
The Maven module split promoted the ECMAScript number formatter to public
API and moved the external runtime kinds into the contracts module. Both
changes left javadoc that doclint now rejects:

- The formatter's comment compares against 1e21 with a bare "<", which
  doclint reads as the start of an HTML tag. While the method was
  package-private the comment was never documented, so it went unnoticed.
- The external provisioner links to the kinds class without importing it.
  That resolved when both lived in the same package and no longer does.

The release profile is the only place that runs javadoc, so the failure
surfaced while publishing rather than in PR CI, and the managed runtime JAR
for the latest release was never built.
2026-11-02 11:42:31 +08:00
9dde2f657d fix(web-shell): surface Managed session list failures (#16082)
* fix(web-shell): surface Managed session list failures

Co-authored-by: Qwen-Coder <[email protected]>

* test(web-shell): match the list-failure sentences, not the whole alert

The list-failure alert wraps its message and its Retry button in the same
role=alert div, so textContent reads 'Managed sessions could not be loaded.Retry
loading sessions'. Two assertions compared that concatenation against the
sentence alone, which made four cases fail with a message that looked identical
on both sides.

Assert the sentence is present, the way the sibling HTTP-500 case already does,
and keep the alert count so the 'once' property is still pinned.

Not verified locally: this checkout's core dist predates
managed-runtime/managed-permission-rules.js, so the suite cannot load here. CI
is the first real run.

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: yiliang114 <[email protected]>
2026-11-02 10:41:58 +08:00
dd020ddfcc fix(web-shell): sanitise goal approval text and edit paths (#16080)
* fix(web-shell): sanitise goal approval text and edit paths

Co-authored-by: Qwen-Coder <[email protected]>

* style(web-shell): let Prettier place the diff-path chain

The Lint & Static gate runs prettier --check and this test file failed it on a
member chain that wraps differently. Formatting only.

Not fixed here: neutralises controls in the edit diff path still fails on this
branch (expected '+1-1' to be the sanitised path), on the unformatted original as
well, so it predates this commit.

* test(web-shell): reach the diff path past DiffView's own wrapper

The case asserts that a crafted edit path has its zero-width joiner, ANSI escape
and BEL rendered as visible escapes. It walked from the File diff region up one
level and took the first child, which is DiffView's stats element - hence the
'+1-1' the assertion reported. The path sits one level higher, as the sibling of
DiffView's root, so the traversal needs one more parent.

Selector only; the assertion and the sanitisation it exercises are unchanged.

* fix(web-shell): tolerate a diff block without a path

DiffBlock.path is optional, but the new sanitise call passed it straight to
sanitizeControlChars, which takes a string. tsc -p tsconfig.lib.json rejected
it as TS2345 at ToolApproval.tsx:727, which is what took down Lint & Static,
Test (ubuntu), Integration Tests, web-shell E2E and Capture visuals on the same
head. Use the file's existing optional-string shape ("?? ''") so a path-less
block still renders an empty label, as before.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy7yxvq7f

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: yiliang114 <[email protected]>
2026-11-02 06:16:31 +08:00
Shaojin Wen 7b4b353750 test(managed-agent): gate the two-server first release refusal (#16089)
Extend HostedMcpRestartFaultGatesIT with the FG12d cell #15750 still
carried: a Session configured with two MCP servers, the first release
refused before forwarding, and the same-id retry releasing both.

The case adds fixture2 only for its own boot, waits for both
configurations to settle, and requires three release controls: 503,
the same operation id answered 200, then the other configuration's
release answered 200. Both fixture processes must exit.

The Hosted fork moves by the case's measured 16.2 s, booked at 32 s.
2026-11-02 02:46:13 +08:00
jinyeandQwen-Coder 87c1918a6a refactor(managed-agent): Extract runtime execution dispatcher (#16084)
Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 01:21:15 +08:00
e3c7a6d7ef fix(core): don't treat fd duplication (2>&1) as a file write in shell permission checks (#15681)
* fix(core): don't treat fd duplication (2>&1) as a file write in shell permission checks

A redirection written without a space whose target is `&` plus a
descriptor number or `-` (`2>&1`, `1>&2`, `>&2`, `2>&-`, `<&0`) duplicates
or closes a descriptor; bash opens no file. The check recorded it as a
write to a file named `&1` in the working directory, so in non-interactive
mode, where edit and write_file are denied unless allowed, read-only
commands that use `2>&1` were refused.

`>&file` and `1>&file` were also checked against a path literally named
`&file`, so a deny rule for `file` did not match: with `Bash(*)` allowed
and writes to `protected.txt` denied, `printf TXT >&protected.txt` was
allowed. They are now checked against `file`. `2>&file`, an ambiguous
redirect in bash, is still treated as a write.

Fixes #15600

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(core): keep a quoted or escaped & in shell redirect targets

The tokenizer removes quotes and backslashes before redirects are parsed,
so `>"&1"`, `>'&1'` and `>\&1` reached the descriptor branch as `>&1` and
lost their write, although bash writes a file named `&1` for them. With
`Bash(*)` allowed and `Edit` denied they changed from deny to allow, and
`>"&protected.txt"` was checked against `protected.txt`.

The tokenizer now records which characters were typed unquoted. A token
counts as `>&`, `N>&` or `<&` only when its `>`/`<` and `&` were both
unquoted; quoting the word after the `&` doesn't matter to bash
(`>&"1"` still duplicates fd 1). A `$` or backtick word after the `&`
keeps the literal `&word` write, since it may expand to a descriptor or
a file name.

The same trim also dropped a quoted trailing `&` (`>"x&"` was checked as
`x`, and `>"&"` produced no write at all). Only an unquoted trailing `&`
is trimmed now.

Reported in review of #15681.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(core): read an unquoted - after >& as close, and a quoted one as a file

After an unquoted `>&`, `N>&` or `<&`, bash reads an unquoted `-` as
"close the descriptor" and starts a new word after it, so
`rm >&-notes.txt` closes stdout and removes notes.txt, and
`cat <&-notes.txt` reads it. The parser took `-notes.txt` as the
redirect word, and since it starts with `-` the flag check in
looksLikePath dropped it: no operation at all, so with `Bash(*)` allowed
and `Edit` (or `Read`) denied, the command was allowed. `main` reported
a write to `&-notes.txt` instead, which a blanket deny still caught but
a deny rule for notes.txt did not.

A quoted or escaped `-` (`>&"-x"`, `>&'-x'`, `>&\-x`) is part of a file
name: bash writes `-x`. The same flag check dropped that write too.

The tokenizer now splits an unquoted `-` right after an unquoted `>&`,
`N>&` or `<&` into its own close token, and the rest of the word goes
back into the argument list. Descriptor redirects are handled in one
place; a `-` that is still at the start of the word there was quoted, so
the write is reported without the flag check.

Spaced forms (`>& -notes.txt`, `>& "-x"`) are unchanged: the tokenizer
trims the unquoted trailing `&` of `>&`, as on `main`.

Reported in review of #15681 (R1-1, R1-2).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(core): keep the whole >&-word as a file operation

4ef50aae47 split an unquoted `-` after `>&`, `N>&` or `<&` off as a
close token and passed the rest of the word to the command, as bash
does. But only the command decides what that word is, and the parser
doesn't know every command; for the ones it knows it drops numbers,
`$` words and `{}` words as arguments. So `tee >&-2` (bash writes `2`),
`tee >&-$HOME/.bashrc`, `cat <&-2`, `my-tool >&-notes.txt` and
`rm -- >&--x` reported no operation at all, and with `Bash(*)` allowed
and a bare `Edit`/`Read` deny they were allowed. `main` reported the
whole word (`&-2`) and denied them.

The redirect token now keeps the whole word and reports it as `main`
does (a write of `&-word` for `>&`/`N>&`, a read for `<&`), and the rest
of the word still goes to the command as an argument, so
`rm >&-notes.txt` is still checked against a notes.txt deny rule.

Also adds tests for `>&`/`<&` aimed at `/dev/null` and `/dev/tcp`
(R2-2), for `printf X >&->&"-x"` (N8) and for `2">"&1` (M6).

Reported in review of #15681 (R2-1, R2-2).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(core): walk >&- chains in a loop, keep quoted ) in names

The `>&-word` split in pushToken called itself once per `>&-`/`<&-`
group in a word, so a single long token (`rm ` + `>&-` x 6000 + `x`,
18 KB) threw "Maximum call stack size exceeded" out of the permission
check; `main` returns normally. Each level also reported the whole rest
of the word as a path, so the check grew quadratically (389 ms for 5000
groups vs 9 ms on `main`). The split is now a loop, and only the first
redirect in a chain reports the whole word, as on `main`: the closes
after it open no file. 20000 groups now take about 60 ms.

A quoted or escaped trailing `)` was still trimmed from a word, so
`printf TXT >"notes)"` was checked against `notes` while bash writes
`notes)`. The trim now treats `)` like `&`: only a bare one is shell
syntax. `(echo hi >out.txt)` still reports `out.txt`.

hasAbsolutePathTokenForOperation didn't know `>&`/`<&`, so an absolute
`>&/tmp/out.txt` after `cd $DIR` stayed cwd-dependent (an `ask` where
`>/tmp/out.txt` gets none). It now matches `[12]?>&` and `<&` first.

Reported in review of #15681 (R3-1, R3-2, R1-3).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-11-02 00:51:14 +08:00
ae7901552e fix(core): settle the loop reminder delivery at the model-acceptance boundary (#15559)
* fix(core): settle the loop reminder delivery at the model-acceptance boundary

takeLoopReminder() cleared the pending map and logged the 'reminded'
telemetry while the ToolResult request was only being assembled. Between
the take and turn.run(), the send still awaits microcompaction and
memory recall and yields pending goal events, so a consumer that stops
the generator there loses the streak's warning (dropPendingReminders
only unmarks still-pending keys) while telemetry credits a delivery the
model never saw.

The delivery is now only prepared at the take: the client commits it
when the first model-accepted event arrives and abandons it — restoring
the pending entries — in the send's finally when the request never
reached the model. A full reset drops a prepared delivery so a late
abandon cannot leak a stale warning into a new prompt. The agent-core
round loop keeps its take-time accounting with an explicit commit since
it has no consumer-visible suspension between the take and the model
call.

Fixes #15537

* fix(core): defer the agent-core loop reminder commit to model acceptance

* fix(core): drop the ineffective pre-round reminder abandons

The three abandonLoopReminderDelivery() calls at the abort/maxTurns/time
checks could not change anything: each is followed by a break out of the
round loop, the detector is method-local, and abandon only moves entries
between two private maps without logging. They made the take-site comment
and the maxTurns control read as if they were what keeps `reminded`
truthful, so drop them and say what the control actually pins — no round
crosses a model-acceptance boundary, so nothing commits.

Also, from the same review round:

- abandon's doc block named the wrong function. A retry drops the streak
  through resetToolCallCount(), which deletes the three hard-coded keys;
  dropPendingReminders() cannot reach entries already taken out of the
  pending map.
- client.test.ts asserted not.toHaveBeenCalled() immediately after
  mockClear(), which cannot fail. Compare the call count with the value
  captured before the send instead, so the "suspended before turn.run"
  premise is actually pinned.
- cover the multi-entry path: commit credits every entry of a delivery and
  abandon restores all of them, not just the first.
- telemetry.md and both languages of the loop-protection design doc now
  say `reminded` is logged at model acceptance, and the "sent, never
  accepted, credited on a later send" case is added to the enumeration of
  not-counted cases.

---------

Co-authored-by: suzhiguo7 <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-11-02 00:43:23 +08:00
易良andQwen-Coder dfeae12de0 fix(core): use the amend delta for loosely spelled amend commits (#16073)
Co-authored-by: Qwen-Coder <[email protected]>
2026-11-02 00:43:20 +08:00
Shaojin Wenandverify 4a5c487681 test(managed-agent): gate the expired-cursor resync answer (#16076)
* test(managed-agent): gate the expired-cursor resync answer

FG9e now advances the Session stream's replay floor past a lagging
client and requires one `agent.session.resync_required` frame on both
the public and WebShell streams, with no event id and the floor and
snapshot cursors it must reload through. The same cursor on the plain
events route answers `409 cursor_expired`, and the floor itself stays
readable.

This closes the expired-cursor cell the FG8 tracker (#14163) deferred
and #15750 carried. The case measures 21.3 s locally; the Hosted fork
budget and Verify step move with it.

* test(managed-agent): wait for projection coverage before advancing the floor

Awaitility treats a lambda that returns without failing as satisfied,
so the coverage guard must assert rather than return. Without this a
poll that lands before the lead's Turn is projected would skip the
advance and dereference a null window.

* docs(managed-agent): correct the resync case's tracker references

FG9e now carries the third case, so the relay-hold sentence must name
only the two resume cases. The FG8 record no longer predicts a slice-2
landing and points at the FG9e delivery instead.

* ci(managed-agent): record the resync case's measured range

The 60 s fork-budget entry was written from one 21.3 s run; the final
audit runs measured 21–23 s, so state the range the budget covers.

* docs(managed-agent): fix the stale step-ceiling header and pin note

The Hosted job's opening parenthetical still summed 291 while the tail,
the Verify step and the job ceiling had moved to 292/202/325. Also say
the replay-floor flag is a default pin, not something currently off.

---------

Co-authored-by: verify <verify@local>
2026-11-02 00:28:04 +08:00
jinyeandQwen-Coder 1bdad7d4b7 refactor(managed-agent): Inject store dependencies through constructors (#16061)
Make dependency references final while preserving optional collaborators, lazy cycle resolution and standalone construction. Migrate verification fixtures and add a synchronized bilingual design.

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-01 16:21:00 +00:00
Shaojin Wen cea333133c fix(managed-agent): correct the child result mirror on consumption (#16074)
* fix(managed-agent): correct the child result mirror on consumption

classify() mirrors the child_run delivery into ma_delivery at
classification time. A parent's consuming revision can land after that
snapshot, leaving a CHILD_RESULT row at `delivered/accepted/pending`
forever while the record itself reads `consumed`; FG10b's
awaitConsumed then times out even though the walk completed.

The parent's own consumption commit now corrects that row in the same
transaction, and classification reads the record row with FOR UPDATE
so a consumption cannot slip between its read and the mirror write. A
row not written yet is left to classification, which reads the
record's current state.

Adds focused H2 coverage for the mirror correction and the commit-path
wiring. Issue: #15744.

* fix(managed-agent): lock only the child run's record row

The classification's locking read matched the session scope without the
record's primary key. MySQL therefore scanned the Session's records and
took next-key locks over the rows that scan touched, so an unrelated
child run's commit in the same Session could wait behind a
classification that had nothing to do with it.

Name the record key in the lookup. A real MySQL IT holds the
classification open and proves the unrelated record still commits; the
same IT fails with the session-scope lookup under a lock wait timeout.
Issue: #15744.
2026-11-01 22:39:10 +08:00
2aa908dc70 fix(managed-agent): block ledger-less agent gap fills and pin the A6 refusal arms (#16015)
* docs(managed-agent): mark the B2 identities and live SSE arm packaged

The G3 recovery-matrix status paragraph still listed B2's attempt/stream
identities and live SSE view as remaining focused/integration evidence after
PR #15993 packaged them into the model-round arm, and its admission-count
clause predated the arm's existing admission audit. Both languages now name
the packaged B2 observations and link the B2 design doc; the remaining list
keeps only A3's rewritten-input/Hook half, A6 and R2.

* fix(managed-agent): block ledger-less agent gap fills and pin the A6 refusal arms

A6's first clause is an evidence-semantics rule: unknown producer evidence
must block, never be laundered into proven absence. Two gaps violated its
evidence surface. The Hook/MCP saved-batch ownership refusal — the only
backstop for the extensions-enabled continuation entry — had zero tests:
deleting its clause kept every existing suite green (demonstrated before
pinning). And the parked-round gap fill answered a stranded agent call with
"the call never ran" when the child ledger was unavailable, although only a
present ledger's missing record is positive never-started proof (admission
commits before dispatch); child-lane config/version drift between generations
makes that arm reachable with a live child.

The fill now throws the family's retriable recovery error for an owed agent
fold with no ledger; journaled answers keep their silent replay and non-agent
calls keep their honest fold. Three ownership cases (hooks, mcp,
hooks+extensions) and four gap-fill cases pin the surface; each deletion
control reds exactly its named case. Focused regression 894/894, typecheck,
ESLint and Prettier clean. Bilingual design doc records the A6 evidence map
and scope boundaries; the parent matrix status notes the landed focused
evidence.

* fix(managed-agent): widen the A6 ledger guard and exempt the terminating routes

R1 review fixes, each verified against the code before fixing:

- The guard covers the whole child-run ledger family (agent || workflow),
  matching the module's own family readers; a stranded workflow call with
  an unavailable ledger no longer receives the fabricated never-ran fold.
- An abort exemption keeps the pre-aborted cancel and interrupted-settle
  fills terminating with their pre-A6 abandonment folds; only the
  live-signal continue route blocks. Without it the guard wedged cancel in
  a deterministic failure loop (regression witness demonstrated at route
  level).
- Two route-level cases build the child-lane drift into the replacement
  load (suppressChildAgents on the version-0 orphan parked shape):
  continue blocks retriably (409) with no fabricated fold, cancel still
  settles.
- The owner-gate matrix drops the vacuous hooks+extensions row (the
  harness entry's extensions clause is recorded as a follow-up) and gains
  both gate directions: a stale-revision commandStep refusal and a
  matching-revision acceptance that keeps the A3 commandStep follow-up
  working.

Nine delete-controls red exactly their named cases on a quiet tree with
known-good baselines; focused regression 8 suites 1583/1583, typecheck,
ESLint and Prettier green. Bilingual design docs name all three fill
callers, the residual trade-off, and the narrowed G2 scope.

* fix(managed-agent): extend the A6 ledger rule to every never-ran writer

R2 review fixes, each verified against the code before fixing:

- The resumed-turn answer (hosted mode) and the command-wait gap fill now
  refuse a ledger-less family call on a live signal, mirroring the agent
  fill; their terminating callers are pre-aborted so cancel and settle
  keep terminating.
- Every ledger-less family fold on the terminating paths — the pre-aborted
  fills and the 'all' settlement — writes a dedicated unknown-outcome
  answer (executionStatus unknown, record retained) that claims nothing,
  never "never ran" and never "cancelled".
- CHILD_LEDGER_TOOL_NAMES becomes the single family-membership fact; the
  six inline two-name literals across recovery and the tool turn now read
  it, so a third ledger-backed tool joins in one place.
- The owner-gate matrix gains the extensions row its comment claimed: a
  Hook owner with extensionConfig and no commandStep is refused, and the
  extensions-exemption mutant reds exactly that row.

Five delete-controls red exactly their named cases on a quiet tree with
failure-list adjudication; focused regression 8 suites 1587/1587, ESLint,
Prettier and typecheck green. Bilingual design doc records the round.

* fix(cli): preserve cancel escape for A6 recovery blocks

Co-authored-by: Qwen-Coder <[email protected]>

* test(cli): pin journal-only workflow ledger refusal

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: 易良 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-11-01 22:21:47 +08:00
jinyeandQwen-Coder 456a49c14f refactor(managed-agent): Separate runtime broker execution workflows (#16029)
* refactor(managed-agent): Separate runtime broker execution workflows

Give process-local Binding and Session state a registry and separate execution reconciliation, takeover scans and cancellation while preserving the existing fenced state transitions and dispatch boundary. Add a deterministic cooldown completion-order regression and synchronized bilingual designs.

Co-authored-by: Qwen-Coder <[email protected]>

* style(managed-agent): Remove extra blank lines from runtime workflows

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-01 13:08:45 +00:00
qqqysandqqqys 91a6274075 fix(web-shell): move Workflow controls into the composer menu (#16047)
* fix(web-shell): move Workflow controls into the composer menu

* fix(web-shell): keep the Workflow menu entry on one line

* fix(web-shell): preserve Workflow access in the VS Code composer

---------

Co-authored-by: qqqys <[email protected]>
2026-11-01 11:07:13 +00:00
1ac4f6cd1b fix(sandbox): install fonts-dejavu-core in the runtime image (#16032)
The capture card renderer in scripts/verify-capture.mjs requests DejaVu Sans Mono, but the runtime image installs no font packages at all.


Patrol-Run: qwen-issue-patrol/jmvx4obxsrz

Co-authored-by: yiliang114 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-11-01 10:53:37 +00:00
易良andyiliang114 7f774219ae test(opentui): run the Windows preview installer suite through cmd.exe (#16028)
* test(opentui): launch the Windows preview entry through cmd.exe

The PowerShell preview installer preservation suite failed on every
nightly Windows run: the seeded qwen.cmd launcher printed nothing, and
the leftover batch child kept the file locked so teardown hit EBUSY.

PowerShell was only a way to run a .cmd from Node. Its `&` call plus
`exit $LASTEXITCODE` returned before the batch child wrote anything, so
run the entry through cmd.exe instead (`shell: true`).

PowerShell also writes its own startup cache under
%LOCALAPPDATA%\Microsoft\Windows\PowerShell. The fixture pointed
USERPROFILE at the home tree the assertions treat as installer-owned, so
every successful test failed on that cache. Point APPDATA and
LOCALAPPDATA outside the compared home.

* test(opentui): compare home files, not shell-created folders

With the launcher fixed, every case in the suite now reaches the
preservation assertion and fails on Windows because PowerShell creates
its own empty %USERPROFILE%\AppData folder shells on first run. Those
are the shell's account directories, not installer output.

Redirect APPDATA/LOCALAPPDATA outside the fixture home (so the startup
cache file stays out too) and compare only the home's file contents.

---------

Co-authored-by: yiliang114 <[email protected]>
2026-11-01 10:53:33 +00:00
Shaojin Wenandverify 6e49388c4a docs(serve): refresh daemon usage guidance (#16056)
Replace obsolete alpha-era deployment and persistence limits with current behavior.
Document authenticated background startup, token sidecar lifecycle, and service-manager choices.

Co-authored-by: verify <verify@local>
2026-11-01 17:01:13 +08:00
6bef5a01c6 feat(web-shell): add "Add to Split View" to session menus (R1 fixes for #14878) (#15956)
* feat(web-shell): add sessions to split view from the sidebar

* fix(web-shell): validate split targets and retain pending additions

* fix(web-shell): report transient split-classification failures per session (#14775)

* fix(web-shell): harden the first split open: race, per-id memo, pending mirror, failure notice (#14775)

* fix(web-shell): disable no-op split adds, clarify the capacity hint, document split (#14775)

* fix(web-shell): fix split classification type errors (#14775)

* test(web-shell): fix split pending-navigation and archived-offer test refs (#14775)

* style(web-shell): prettier-format the split classification memo type (#14775)

* fix(web-shell): adapt controlled pane reveal to prop-derived pane set

The main merge (#14577) derives paneIds from the sessionIds prop in
controlled mode, so the reveal effect could no longer sync local state
(setPaneIds does not exist) and its render-time ref check was dead: the
ref is current by the time the effect runs. Track the previous commit in
an effect-time ref instead. Also restore the test-block closer the merge
dropped from SplitView.test.tsx.

* Fix split-persist e2e spec: assert disabled state for already-open session (bot R2 blocker)

The first smoke test clicked 'Add to Split View' on Session A after it
was already a split pane. The R1-10 gate disables that menu item with
the title 'This session is already open in Split View' (sidebar
splitAlreadyOpen), so Playwright retried a click on a disabled item.
Replace the click with toBeDisabled() + title assertions, reusing the
shape of the six-pane spec in the same file. The no-op guarantees
(pane count, draft, DOM persistence) are kept as-is.

---------

Co-authored-by: luozongmin <[email protected]>
Co-authored-by: Сергей Панченко <[email protected]>
Co-authored-by: Shaojin Wen <[email protected]>
2026-11-01 16:52:54 +08:00
Shaojin WenandQwen-Coder 417e81ec4d fix(review): keep prose re-review requests aimed at the PR (#15998)
* fix(review): keep prose re-review requests aimed at the PR

A re-review request typed as prose ("有更新了,重新评审") carried no
target the parser could classify, and the parser did not refuse it:
every non-flag, non-number, non-URL token classifies as a file target,
so the sentence became a file path, and prose typed beside a PR number
stole the target while the number was demoted to extraTokens.

- parse-args: PR-shaped tokens now outrank file-shaped ones regardless
  of arrival order; an explicit ./<number> spelling stays a file target.
- review skill: a Step 1 intent-routing clause resolves a prose request
  to the PR the conversation names (non-empty inputs only, flags kept,
  the CLI args file preserved), and the description plus the resume
  verdict bullet exclude the updated-PR case from --resume.
- fetch-pr: the head-moved resume refusal now says the fresh run IS the
  re-review and names the plain invocation for next time; the
  restart-bound semantics are unchanged.
- docs: both --resume surfaces in the user guide carry the exclusion.

Pinned by 12 new parse-args cases (the key one red before the fix), a
SKILL.md pin test, and a fetch-pr stderr assertion; DESIGN.md records
the incident as "The prose re-review request".

* fix(review): fence the prose routing's local and posting boundaries

The review round on #15998 caught two defects in the new routing clause
and one regression behind its non-blocking nit:

- Option-only invocations (--fix, --effort low) matched the routing
  condition and would have been retargeted into the conversation's last
  PR, disarming --fix. The clause now names the bare and option-only
  shapes as non-intent and leaves them to the parser verbatim; two new
  parser cases pin that contract.
- The clause promised posting authority for a --comment carried through
  the resolution, but the write gate re-parses the CLI's verbatim record
  on purpose, so a prose record ends at submit with `unbound`. Posting
  is now the resolution's hard boundary: present the resolved invocation
  and ask for an explicit re-run instead of completing a review whose
  submission is already refused. A new submit-suite test pins the
  refusal beside the explicit re-invocation's acceptance.
- The PR-shape reorder demoted a malformed URL behind the assigned
  target and lost its specific diagnostic to the generic extra-argument
  line. The invalid-url refusal is hoisted ahead of the target-assigned
  branch, position-independent, pinned by a dedicated test.

DESIGN.md records the review round; the PR body carries the same
boundaries in both languages.

* fix(review): guard prose routing against local-write intent, qualify scoping

The deep-verification round on ef86b03a99 measured one more hole in the
option-only class and one overclaim:

- F1: "review my local changes --fix" is prose with no classifiable
  target, so it still matched the routing trigger and resolution would
  disarm --fix. The clause now parses the verbatim input first and never
  resolves a PR into a verdict with fix.effective true — a mechanical
  guard, not a wording guess; prose asking for a local review is
  excluded even without --fix. The hash form's copy ambiguity is closed
  too: a # prefix is reference decoration, not part of the target. Four
  new parser cells pin the guard input, the PR-target contrast, the
  hash form, and the ./ escape-hatch boundary.
- F2: delta scoping on a plain re-review is high-only; the docs (both
  surfaces), the resume bullet, the routing clause, the --resume help,
  and the head-moved stderr now all carry the high-effort qualification.
- The ./ escape-hatch wording gains the measured precision: it holds
  when no PR-shaped token is present, not unconditionally.

DESIGN.md records the verification round; the PR body carries the same
boundaries in both languages.

* fix(review): honest resume guidance, promoted-number warning, pre-fix framing

The /review lane's round on 6a5372d6e4 filed five suggestions; all five
verified against the code and addressed:

- R1-1: the routing rationale described the prose-steals-target behavior
  in the present tense — behavior this PR's own parser preference closes.
  It now marks that half as pre-fix and keeps the live half (prose alone
  dies at capture) as the rationale; pinned.
- R1-2/R1-3: the head-moved guidance was gated on a refusal a re-review
  after a completed round never reaches (assessResume checks no-report
  before comparing heads, and cleanup deleted the report), and "gained
  commits" misdescribed a rebase. The guidance now covers the re-review
  family (head-moved, no-report, worktree-gone) with cause-appropriate
  wording; the restart-cost sentence stays with head-moved alone, the
  only reason recordRestart runs for. Docs and the resume bullet carry
  the same completed-round reality.
- R1-4: positive AND negative pins — guidance present on head-moved and
  no-report (without the restart sentence), absent on diff-hash-mismatch
  and worktree-dirty, so dropping the reason gate goes red. R1-3 and
  R1-4 contradicted each other on no-report; the family split resolves
  both (recorded in DESIGN.md).
- R1-5: a bare count inside a sentence is indistinguishable from a PR id
  and the promotion flips --comment to effective. The parser now warns
  when a bare number is promoted with non-PR tokens after it in the
  original order (the canonical number-at-end rescue stays silent), and
  the clause treats the warning as ask-before-running, never posting
  silently. Four-shape test pins presence and absence.

* fix(review): withdraw posting authority from promoted numbers, honest guidance

The sandboxed CI round and the second verification round proved the
promoted-number warning was disclosure, not a fix, and measured four
more gaps:

- R2-1 (Critical): a promoted count carried --comment posting authority
  to a PR the sentence never named ("--comment the 2 follow-ups to
  15998" authorised posting to PR #2). comment.effective is now false
  whenever the PR target is a promoted bare number (promotedNumberTarget
  marks the verdict); the write gate refuses with a promotion-specific
  cause. An explicitly typed number or a PR URL keeps its authority.
- R2-2: the warning moved after the URL preference, decided from the
  actual target, fired exactly when a requested posting authority was
  withheld — demoted numbers and courtesy particles stay silent.
- R2-3: the routing guard keys on what the user spelled, not the
  verdict's target type (prose parses to a file target — the old guard
  excluded the only input it existed for); local-intent moves are
  executable; the hash form has an operational resolution.
- R2-4: trailing sentence punctuation no longer demotes a PR number;
  classification normalises the mark for the integer test only.
- R1-2/R1-3 (fix-induced): worktree-gone gets its own cause-neutral
  sentence (it requires a surviving report); no-report asserts no
  cause; every arm carries the anchor condition; the restart-cost
  sentence prints where recordRestart runs, in the past tense; the
  ruling promise is scoped to high/medium.

New pins: the Critical's exact witness at the gate, both word orders,
the setting-driven arm, worktree-gone, and the reverse-order
malformed-URL cell that reds under a full revert.

* fix(review): name the source of withheld posting authority

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-11-01 16:50:22 +08:00
Shaojin Wen 859fd0b39c fix(core): keep the whole Kimi K3 generation at a 1M context window (#16048)
* fix(core): recognize the whole Kimi K3 generation at a 1M window

The K3 generation is served as `kimi-k3`, `kimi-k3.1-pro` and, from Kimi
Code, as the bare `k3.1-pro` / `k3.1-pro-highspeed` aliases. Two gaps left
the K3.1 tiers with the wrong window:

- the curated table only matched ids starting with `kimi-k3`, so the bare
  aliases fell through to the 256K Kimi fallback;
- the models.dev catalog is consulted before that table, rounds the 1M
  window up to 1,048,576, and lists each K3.1 sub-version under both its
  dotted and dashed spellings, so the catalog value overrode the declared
  window and compaction thresholds fired too late.

Match the generation once and share that pattern between the curated table
and the catalog correction, replacing the single-id catalog entry that the
next sub-version would have slipped past.

* fix(core): keep Kimi's k3-256k out of the K3 1M window rule

Kimi Code serves `k3-256k` at 262,144 tokens alongside the 1M `k3` and
`k3.1` tiers, and the generation pattern swept it up: with no catalog entry
it resolved to 1M instead of the 256K family fallback, and the
generation-wide catalog correction overwrote a correct cached 262,144.

Name the variant ahead of the generation row, exclude it from the shared
pattern so the catalog correction cannot raise it, and pin its window in the
id-keyed corrections for a catalog that omits or overstates the entry.

Fixes the P1 on #16048.

* fix(core): give Kimi's bare K3 aliases the same modalities as the prefixed ids

Kimi Code serves the K3 generation without the `kimi-` prefix, but the
modality table still matched only `kimi-k3`, so `k3.1-pro`,
`k3.1-pro-highspeed` and a bare `k3` fell through to the text-only Kimi
fallback while their `kimi-` spellings advertised image and video input.

Match the prefix optionally, and name the 256K variant ahead of that row:
Kimi Code reports image input only for `k3-256k`, no video.

Addresses the R1-1 suggestion on #16048.
2026-11-01 08:47:39 +00:00
Shaojin Wenandverify 7ab044d608 docs(readme): add daemon mode quickstart (#16053)
Explain how qwen serve supports shared sessions and browser/API clients.
Document lifecycle commands and loopback authentication risks.

Co-authored-by: verify <verify@local>
2026-11-01 08:22:29 +00:00