mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-24 16:16:13 +00:00
451 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1fffa5108d
|
fix(acp-bridge): Disable permission timeout by default (#9933)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* fix(acp-bridge): disable permission timeout by default Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * chore: regenerate settings schema Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(acp-bridge): fix stale timeout comment 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> |
||
|
|
24db7f6ef2
|
feat(review): say when the approach, not the patch, is the open question (#9340)
* feat(review): say when the approach, not the patch, is the open question Every finding /review emits is anchored to a `file:line` in the current diff. That is what a finding is — and it means a review can report where an approach leaks, but never that a different approach would retire all of the leaks at once. Measured: one change to `extractAndStripMeta` took three attempts across two PRs. #9097 (3 rounds, 18 findings) added a timeout to the vm call; #9136 (6 rounds, 56 findings) moved the walk inside the vm and ended up spawning a child process per call, growing 228 -> 920 source diff lines. #9325 landed it in one commit by not evaluating the literal at all. All 74 findings were individually correct, and every one of them went away with the mechanism. The signal was already there and filed as the wrong kind of thing: `did not converge within the reverse-audit round cap` appeared four times across the two PRs, as a coverage gap — "we did not finish looking" — rather than as a conclusion about the change. Nothing was responsible for reading it as "stop patching". Add one advisory paragraph, and one clause on the terminal verdict line, when a non-Approve round is past the round threshold AND its source diff has grown at least 3x since the review first measured it. This round's round-cap stop rides along as corroborating text when present; it is never a trigger on its own. It is deliberately not a finding. Findings are what the autofix loop consumes, and that loop patching each finding in turn is the pattern being interrupted — a finding here would be fixed rather than read. It addresses the human deciding what happens next, so it is a body paragraph and a verdict-line clause, it adds no cap, and it never moves the event. The baseline is a baseline, not the previous round's size: 228 -> 920 across six rounds is ~1.3x per round, which no per-round delta would notice, but 4.0x cumulatively. `Ledger.src0` records the first measurement and is carried forward unchanged, so a diff that later shrinks cannot rewrite its own baseline. It is the one marker field that survives truncation — the ruling that withholds an anchor from a partial finding list does not extend to a measurement of the diff. Known limits, documented rather than papered over: it cannot see across pull requests, so the three-attempt shape that motivated it would have fired only on a second forgeable persisted counter; and it is retroactively blank, staying silent until a PR has posted two rounds after this ships. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(review): suppress approach signal for downgraded approvals * fix(review): measure approach growth over full diff * fix(review): validate approach signal evidence * fix(review): pin approach-signal boundaries and validator coverage Round-5 review findings: boundary tests for the round threshold, growth factor, and source-diff floor; the round-cap corroborating clause and its zh rendering; src0 survival through the pr-context persist seam and the incremental marker carry-forward; artifact validator refusal/absence tests for approachSignal; design doc firing list names the pre-cap verdict. * fix(review): clamp the approach signal's round at the ledger cap (R9-1) The signal computed its displayed round with an unclamped `prevRound + 1` while the ledger marker stamp and the deferred-suggestions clause both clamp with `Math.min(prevRound + 1, LEDGER_MAX_ROUND)`. `parseLedger` accepts `round == LEDGER_MAX_ROUND`, so a side file at the cap is representable and carries forward: one composed body announced "⚠️ Round 10001" beside a marker stamping `"round":10000`, and the terminal verdict line printed 10001 too — the doc comment in this same diff claims all three consumers cannot disagree "at the cap included". The new test pins the cap for the third consumer, mirroring the existing deferred-clause cap test; mutation-verified that reverting the clamp turns it red with `round: 10001`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
d1cfd87683
|
feat(review): promote language-pitfall and wrapper/proxy checks out of Agent 1a (#9805)
* feat(review): promote language-pitfall and wrapper/proxy checks out of Agent 1a (#9788) Split the two checks folded into Agent 1a's line-by-line brief into dedicated Step 3A roles at high effort: Agent 1d (language-pitfall scan, always) and Agent 1e (wrapper/proxy routing, rostered when the plan's wrapperSignal is true — a capture-time vocabulary heuristic that fails safe: only an explicit false keeps it out, so version-skewed plans still owe the check). The roster, check-coverage and agent-prompt all read the gate from the plan, so a run that skips either agent is named. Briefs, SKILL.md, and the user-facing code-review doc updated; 1a keeps its walk minus the two clauses. * fix(review): address round-1 feedback on the 1d/1e split (#9805) * fix(review): address round-2 feedback on the 1d/1e split (#9805) * fix(review): address round-3 feedback on the 1d/1e split (#9805) * fix(review): address round-4 feedback on the 1d/1e split (#9805) * fix(review): address round-5 feedback on the 1d/1e split (#9805) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
37cedea5b2
|
feat(computer-use): replace built-in tools with bundled skill (#9856) | ||
|
|
43d46be912
|
refactor(core): shrink the content generator interface (#9676)
* refactor(core): shrink content generator interface * refactor(core): remove orphaned request-tokenizer estimator cluster Removing countTokens from both providers deleted the last production consumers of RequestTokenEstimator. Delete the orphaned cluster: requestTokenizer.ts (330), imageTokenizer.ts (534), types.ts (36), the directory barrel (11), and both test files (608 lines). Also drop the inert vi.mock of requestTokenizer.js left in client.test.ts and the stale dimension-extractor cross-reference in review/lib/assets.ts. textTokenizer.ts and supportedImageFormats.ts stay: converter.ts, pdf.ts, and fileUtils.ts still consume them and the core barrel re-exports them. * docs(design): sync lazy-google-genai-loading record with shrunk interface countTokens and useSummarizedThinking no longer exist on ContentGenerator, so the design record for the lazy-wrapper architecture must not keep advertising them: list the three remaining shared async operations, drop the useSummarizedThinking sentence and the summarized-thinking item from the consumer audit and Verification section, and add a dated note recording the interface shrink from PR #9676. * ci: record cd-cua-driver.yml size growth in .size-baseline Same latent main-side violation as fixed in #9682: #9587 grew the workflow without a baseline update; record the new size as the check message directs (precedent #9747). * docs: finish scrubbing tokenizer references after estimator-cluster removal Follow-up to 0ee17632c7/1871bb5b81 (review round 2): - supportedImageFormats.ts header and getSupportedImageFormatsString doc no longer describe a tokenizer decode/metadata-extraction stage; the list is now documented as the vision-input acceptance list, with token accounting noted as the flat DEFAULT_IMAGE_TOKEN_ESTIMATE. - web-shell-image-drag-and-drop.md's BMP rationale no longer claims ImageTokenizer parses BMP dimensions; dated sync note added stating BMP support rests on SUPPORTED_IMAGE_MIME_TYPES plus converter passthrough since PR #9676. * docs: drop tokenizer from the BMP test-plan line Follow-up to 18f08c0924: the test plan still required converter/tokenizer focused tests for image paths; the image-tokenizer estimator cluster was removed in PR #9676 (text tokenizer is unaffected and out of scope here). |
||
|
|
e0d933b23e
|
refactor(core): make derived Config ownership explicit (#8100)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
SDK Python / Classify PR (push) Waiting to run
SDK Python / SDK Python (3.10) (push) Blocked by required conditions
SDK Python / SDK Python (3.11) (push) Blocked by required conditions
SDK Python / SDK Python (3.12) (push) Blocked by required conditions
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* refactor(core): define derived config ownership * docs(core): align derived config ownership scope |
||
|
|
27285a5243
|
refactor: centralize approval mode contracts (#9796)
* refactor: centralize approval mode contracts * fix: align Python SDK import grouping * test: restore approval mode exports in CLI mocks * fix: close approval mode drift gaps * test(cli): preserve core exports in serve mocks * fix: close approval mode review gaps * test(cli): complete permission request fixture * test(sdk): match approval mode route * test(approval): close review coverage gaps * test(sdk): cover approval mode global scope path |
||
|
|
a60cbbc54a
|
refactor(core): make utils/ a leaf layer (#9778)
* refactor(core): make utils/ a leaf layer Eliminate every runtime (value) upward import from packages/core/src/utils production modules so utils/ can become a leaf layer with no runtime dependency on the rest of core. Two mechanisms, no behavior change: - Relocate domain-coupled modules out of utils/ into their owning module (agents, config, core, memory, services, tools), and move generic constants/types that live elsewhere into utils/. All `git mv` moves keep history; every import that pointed at a moved file is rewritten. - Extract the remaining value imports as small leaf modules inside utils/ (AuthType, isTool, ToolErrorType, DEFAULT_QWEN_MODEL) and re-export them from their original owners so cross-package consumers are unaffected. doesToolInvocationMatch moves into shell-utils, its only production consumer. Only type-only imports now cross the utils/ boundary. The two deferred inversions in debugLogger (Storage, getTraceContext) are stateful and left for a follow-up. * chore(core): enforce utils/ leaf layer with lint rule Add architecture/no-core-utils-upward-import, which flags runtime (value) imports that leave packages/core/src/utils. Type-only imports, sibling utils imports, and external package specifiers stay allowed; the two deferred debugLogger inversions (config/storage, telemetry/trace-context) are carried on an explicit allowlist. Enable the rule as an error on core sources and cover it with Linter-based tests. * fix(core): restore iconv-lite tree-shaking for sync-file-encoding The utils leaf-layer refactor moved sync-file-encoding from utils/ to services/, but the esbuild tree-shake plugin still matched the old ./utils/ specifier, so its sideEffects:false marker no longer applied and the ACP startup closure regained a static iconv-lite import. Point the onResolve filter at the new ./services/ path. * fix(ci): catch stale integration imports earlier * fix(core): close utils boundary review gaps * fix(core): close self-reference boundary gaps * ci: re-trigger after self-hosted runner checkout EACCES |
||
|
|
b2d0687213
|
feat(serve): add --open-with-auth (#9738)
* docs(serve): propose ephemeral auth for --open Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): address ephemeral auth review Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): clarify asset pre-check boundary Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): centralize token selection plan Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): make ephemeral auth opt in Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): align ineligible-browser handling with manual-URL fallback Browser-launch eligibility is a heuristic with common false negatives, so it is no longer a hard pre-listen gate: an ineligible environment warns (naming the tripped signal), starts the daemon, and prints the fragment-bearing manual URL, matching the launch-failure recovery. Also pin the generation breadcrumb with planned test assertions. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * feat(serve): add opt-in ephemeral auth for --open Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * feat(serve): replace ephemeral auth with --open-with-auth Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): clarify temporary token storage Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs(serve): clarify ephemeral token persistence Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#9738) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#9738) 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> |
||
|
|
dbf7382c8f
|
fix(memory): scan uncapped when selecting forget candidates (#9530)
* fix(memory): scan uncapped when selecting forget candidates Recall moved to the uncapped scanner in #8716; forget did not. A document ranked past the 200-document cap could be recalled and injected into the prompt but never forgotten. Forget now scans uncapped, so its candidate universe matches recall's. The model-selection prompt renders every candidate, so it gets its own bound of 400: literal query matches first, then the most recently modified remainder. The heuristic fallback keeps scanning the full uncapped list. Indexer, status, and extraction stay capped on purpose, and the two design docs that recorded forget as capped now say otherwise. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(memory): give each scope its own share of the forget prompt Review round 1. The 400-candidate bound ranked both scopes into one recency budget, so a store whose project entries are all newer than its user entries seated no user memory at all. The capped scanners this replaced ran per scope, so each scope always had seats. That made an old user entry unselectable by the model while recall could still inject it, which is the same asymmetry the PR set out to close. Each scope now keeps a 200-candidate quota and whatever a smaller scope leaves is handed to the other. Within a scope, literal query matches rank first and both groups are ordered newest first, so truncation is deterministic instead of scan-order, and the bound logs when it drops candidates. Also from review: the query normalisation and match predicate are now shared with selectByHeuristic so the two cannot drift; the user scan gets the best-effort guard recall.ts and extractionAgentPlanner.ts already carry; and the docstring and design docs no longer claim an unconditional guarantee the bound does not provide. Three tests, each verified against the mutation it is meant to catch: global ranking drops the user ids, an ascending sort drops the newest filler, and handing the fallback the bounded list returns 400 of 450 matches. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(memory): bound the unconfirmed forget path and drop the silent scan guard Review round 2, all suggestions. MemoryManager.forget passed limit: MAX_SAFE_INTEGER and deletes without confirmation. With an uncapped scan and a heuristic fallback that substring matches the whole store, a one-character query matched nearly every entry in both scopes, where the capped scanners had held that same failure to one scan's worth of candidates. The limit is now the prompt bound, restoring the old ceiling. Round 1 added a best-effort catch on the user scan. That was wrong on two counts: scan.ts caps after reading and ordering the whole tree, so uncapping adds no read exposure to justify it, and swallowing the failure made forget report "no entries matched" for a scope it never read, then act on that answer by deleting. Reverted, with a comment saying why forget differs from recall here: a missed injection is recoverable, a missed deletion is not. normalizeForgetQuery now delegates to normalizeSummary so query matching and the post-selection re-match cannot drift apart, and one design-doc sentence no longer implies only semantic matches fall off the bound. Two tests, each verified against its mutation: the quota split is now exercised with both scopes over quota, where dropping it to 150 seats 250 project entries instead of 200; and the delete ceiling fails at 401 removals if the unbounded limit comes back. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(memory): split forget's deletion seats per scope, and decouple the ceiling Review round 3. The deletion ceiling added last round truncated the heuristic fallback in candidate order, and listIndexedForgetCandidates pushes every user entry ahead of every project entry. With 450 matching user entries and 50 matching project ones and the side query down, forget deleted 400 user entries, zero project ones, and reported success. That is the reachability asymmetry this PR exists to remove, moved into the delete path. The per-scope allocation the model prompt already used is now shared with the heuristic, so each scope keeps its share of the limit and a smaller scope's unused seats go to the other. The ceiling is also its own constant now rather than an alias of the prompt bound. Resizing the model prompt is a cost decision and resizing this is a blast-radius decision; sharing one constant let the first silently widen the second. Two tests, each checked against its mutation: the 450-user/50-project shape returns zero project matches under a plain slice, and oldest-first ranking inside a scope drops that scope's newest entry from the prompt. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(memory): pin the forget split at a small limit and the heuristic's own order Cross-review found both new tests mutation-survivable. Every case used a 400 limit, so hard-coding a 200 per-scope quota instead of deriving it from the budget still passed, and the recency case let the side query succeed, so it pinned the model prompt's ranking rather than selectByHeuristic's own comparator. One case at limit 5 with the side query failing covers both: it asserts the 3/2 split, which only holds if the quota comes from the budget, and that each scope contributes its newest entry, which fails if the comparator is reversed. Both mutants verified failing. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(memory): share the forget recency comparator and log a bound deletion Review round 4, both suggestions. The mtime comparator was the last thing the model path and the heuristic path each typed for themselves, after this branch had already hoisted the query normaliser, the match predicate and the per-scope allocator so the two could not drift. Each site has its own test, so a one-sided ordering change would have updated its own test, passed CI, and left the sibling stale. Now one definition. The deletion cap also bound silently. The prompt bound warns when it truncates; the path that actually deletes did not, so a forget that removed 400 of 500 matches reported success and left no record of why recall kept injecting the rest. It now says so. No test for the new warning: it is a debug log line, and asserting on it would pin the wording rather than the behaviour. Refs: #9378 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
014b903bf5
|
fix(daemon): Bound conditional-close refusal holds (#9820)
* fix(daemon): Bound active-work close refusal holds Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#9820) 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> |
||
|
|
a8b822f5d2
|
feat(web-shell): expose agent task changes (#9637)
* feat(web-shell): expose agent task changes * fix(web-shell): deduplicate agent task callbacks * fix(web-shell): ignore agent task telemetry churn * fix(web-shell): skip immutable prompts in task fingerprint * fix(web-shell): type agent task fingerprint |
||
|
|
eea98f3b04
|
refactor(cli): extract ACP skill management (#8865)
* refactor(cli): extract ACP skill management * test(cli): cover ACP skill safety guards * fix(cli): harden ACP skill mutation guards * fix(cli): handle ACP skill frontmatter variants * test(cli): deduplicate ACP skill fixtures * fix(cli): handle multiline Skill enablement fields * fix(cli): recognize escaped Skill enablement keys * refactor(cli): restore ACP skill extraction scope Restore the three post-review files to the initial extraction commit. The removed changes addressed pre-existing Skill behavior and test coverage rather than regressions caused by the module split. Latest origin/main changes only unrelated ACP agent sections, so no extracted Skill logic needs to be carried forward. --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
c2d63fbe58
|
fix(web-shell): show reasoning effort before session creation (#9599)
* fix(web-shell): show reasoning effort before session creation * fix(web-shell): harden reasoning preview lifecycle * chore(desktop): refresh frozen bun lockfile * fix(web-shell): restore reasoning preview after session clear * test(webui): pin session-clear model restoration on all reset paths (#9599) Witness the four back-to-welcome reset handlers' models re-projection (session_closed, stream auth failure, terminal stream error, heartbeat clear) with mutation-visible assertions: each test attaches a session whose live context displaces the provider models, then verifies the workspace reasoning preview returns after the reset. Also pin the providers-absent fallback in getConnectionAfterSessionClear so older daemons keep the pre-clear model list. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
4ddbf227e8
|
feat(mcp): add MCP 2026 core and WebShell Apps host (#8992)
* feat(mcp): add 2026 protocol negotiation * feat(mcp): render MCP Apps in WebShell * fix(mcp): keep legacy tool discovery lenient * fix(mcp): keep Apps HTML out of TUI and honor tool visibility TUI and history compaction dumped mcp_app HTML as JSON, and discoverTools registered app-only tools for the model. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): stabilize AppBridge lifetime and close sandbox CSP gaps Theme toggles and transcript reseeds were tearing down MCP Apps; the host CSP also allowed any loopback port and form posts bypassed connect-src. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): list under-declared modern MCP capabilities over the wire v2 typed helpers return [] without a request when a capability is omitted. Use them only when the server declared the capability, and keep Apps unmounted in collapsed tool rows. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): keep Apps sandbox reachable and list past 64 pages Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): reject empty compacted html and keep MCP Apps expanded in multi-tool groups Fixes R3-1 and R3-2 review comments: R3-1: getMcpAppDisplay now rejects empty html strings (from compaction) so session replay shows fallbackText instead of mounting an empty iframe. R3-2: ToolGroup now checks for MCP apps across all tools (not just singleTool), auto-expands when any tool has an MCP app, and keeps MCP app rows expanded (summaryOnly=false, forceExpanded=true) even when adjacent tool calls are merged into the group. * feat(web-shell): fold thinking into the compact-mode tool summary (#9148) Compact mode used to drop thinking messages entirely, so a running turn gave no indication of the thinking step. Keep the thoughts and aggregate them with the adjacent tools into one summary: a streaming thought reads "Thinking…" with the running shimmer, and a completed thought settles into a click-to-expand row in its original interleaved position. The translate action is preserved on both the thinking block and the folded thought rows, and the merged group gets a synthetic id so its expanded state never leaks into non-compact mode. Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com> * fix(mcp): address app discovery and sandbox regressions * fix(web-shell): keep MCP apps expanded in compact summaries * Revert "feat(web-shell): fold thinking into the compact-mode tool summary (#9148)" This reverts commit ab2eebc5d36f17a51ce94e423db5745dcbb273fe. * fix(web-shell): render compacted MCP App fallback and teardown before unload Compacted history keeps type:mcp_app with empty html; show fallbackText instead of a blank sandbox, and wait for ui/resource-teardown before unloading the iframe. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): bound the discover probe and raise the daemon bundle cap Silent legacy stdio servers inherited the 10-minute request timeout for server/discover. Cap the probe at 5s so fallback fits the discovery window, and raise the browser bundle budget after the main merge overflowed CI by 47 bytes. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): skip version-negotiation probe on remote transports SDK v2 rejects HTTP server/discover timeouts without falling back to initialize, and the 5s probe consumed the entire remote discovery window. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): keep MCP App iframe src across deferred teardown Deferred unload() was clearing src on the live iframe after a remount, so the new AppBridge never saw sandbox-proxy-ready. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): honor listing-level MCP App CSP and permissions registerAppResource puts ui.csp/permissions on resources/list, and resources/read does not merge that metadata into content entries. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): reuse session client for list and emit app fallback text Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): keep mcp list and IPv6 sandbox CSP valid Give qwen mcp list leftover handshake budget after the 5s discover probe, and stop emitting invalid [::1] CSP origins. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): keep modern list and short discovery budgets working Drop the era-illegal ping after mcp list connect, shrink the stdio discover probe to the discovery window, and document that remotes stay on legacy initialize until the SDK can fall back. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): keep the 2026 slice free of review-only extras Drop the global tools/list page cap, generated companion notices, and the review screenshot so this PR stays on stdio 2026 plus the WebShell Apps host. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): restore generated companion notices after the SDK v2 bump CI regenerates NOTICES.txt from the lockfile; the file has to ship with the new MCP client dependencies. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): isolate the Apps proxy from WebShell storage Drop allow-same-origin on the outer sandbox iframe so a default localhost daemon cannot read the WebShell session token. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(mcp): harden fallback and app sandbox * fix(core): preserve large and app-only MCP catalogs * fix(mcp): preserve legacy negotiation compatibility * fix(mcp): default stdio negotiation to legacy --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: ytahdn <1294726970@qq.com> Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com> Co-authored-by: YungSen Hsin <yungsenhsin@U-G0HXNQM1-2052.local> |
||
|
|
56db17bd4c
|
refactor(cli): enforce utils leaf-layer dependency direction (#9146) (#9737)
* refactor(cli): enforce utils leaf-layer dependency direction (#9146) Move domain-coupled modules out of packages/cli/src/utils into the directories that own them: config/ (dialogScopeUtils, settingsUtils), i18n/ (languageUtils), ui/ (handleAutoUpdate, standalone-update, systemInfo, systemInfoFields, update-relaunch, commands, doctorChecks), nonInteractive/ (nonInteractiveHelpers, chat-recording-failure, tool-result-boundary-diagnostics, permission-suggestions), serve/ (sandbox), services/housekeeping/ (scheduler, non-interactive-scheduler), and commands/review/ (findings). Extract the generic normalizePartList helper into utils/normalize-part-list.ts so utils consumers keep importing downward, and move the MergeStrategy enum into utils/deepMerge.ts (its owner). Add an eslint architecture rule (no-utils-upward-import) that forbids value imports from utils/ back up into a domain directory. Type-only imports stay exempt: they are erased at compile time and cannot create a runtime cycle (Settings in modelConfigUtils, CommandContext in sessionPaths). No behavior change: typecheck, build, and the affected unit tests pass. * fix: use Qwen Team 2026 license header on new files (#9146) * chore: refresh stale utils/ path references after leaf-layer move (#9146) * docs: reconcile no-utils-upward-import header with the allowed type-only set (#9146) * fix(cli): allowlist sandbox process.env accesses after leaf-layer move (#9146) * chore(ci): re-record qwen-autofix.yml size baseline after #9677 (#9146) #9677 recorded qwen-autofix.yml at 392111 bytes while the file it committed was already 397656, so every PR that merged main after it tripped the growth ratchet. Re-record the actual size; the file itself is unchanged by this PR. * fix(review): drop the stale utils/findings.ts digest root after the leaf-layer move (#9146) The #9146 move returned findings.ts to commands/review/, but the digest root lists merged from main still pinned it under utils/, where the file no longer exists — the absent root darkened every review's staleness check and failed review-source-digest.test.ts. Drop the stale file-shaped root from both digest copies and their pins; the commands/review/ directory root covers the validator at its new home, and the two utils helpers keep their file-shaped roots. * fix(review): colocate seatbelt profiles with the sandbox module (#9146) * fix(review): exempt inline type-only specifiers from the utils upward-import rule (#9146) * fix(review): report upward inline type-specifier imports under verbatimModuleSyntax (#9146) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(review): pin mixed-specifier and zero-specifier upward imports in the utils rule (#9146) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(review): anchor the nested-checkout utils rule fixture on the last marker (#9146) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(review): pin that the utils/findings.ts digest root stays removed (#9146) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): reword stale-bundle SCOPE header to the post-move helper shape (#9146) * test(review): drop the pre-move utils/findings.ts from the skill-parity fixture (#9146) * test(serve): derive the seatbelt colocation tripwire from BUILTIN_SEATBELT_PROFILES (#9146) * fix(architecture): fail closed on computed dynamic imports in the utils leaf rule (#9146) * fix(cli): point settings.test.ts at the post-move settingsUtils path (#9146) main updated settings.test.ts after this branch moved settingsUtils.ts from utils/ into config/, and the merge kept main's old import specifier, which vite fails to resolve. Repoint it at ./settingsUtils.js; every other consumer already uses the new path. * fix(cli): close utils boundary review gaps * test(cli): cover utils boundary allow paths --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
b5fbdb22d3
|
feat(cua-driver): add versioned Computer Use SDK and release pipeline (#9587)
* chore(cua-driver): sync upstream v0.20.0 * feat(cua-driver): add Computer Use SDK with versioned observation revisions Wrap the typed driver SDK in a standalone Node wrapper and add accessibility.observation_revision.v1: base-anchored validated diffs with opaque element tokens, explicit full-resync reasons, full-only answers on Windows/Linux. Fix portable include_screenshot schema type and classify stable kAXErrorFailure refusals as complete in the macOS capture tracker. * feat(cua-driver): complete typed Computer Use capabilities * fix(cua-driver): use Qwen-owned npm identity * feat(cua-driver): publish one Qwen CUA SDK package * fix(cua-driver): verify Windows Rust targets * fix(cua-driver): verify macOS Rust targets * fix(cua-driver): pin release Rust toolchain * fix(cua-sdk): fail closed on incomplete releases * test(cua-sdk): import workflow test globals * ci(cua-sdk): retry Debian package downloads * fix(cua-driver): harden lifecycle and release gates --------- Co-authored-by: tutu <tutu@U-RD4R9MQQ-2235.local> |
||
|
|
431a0bd9b0
|
fix(daemon): keep restored ask_user_question valid after load (#9763)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* fix(daemon): keep restored questions valid across load, send, and replay Post-merge review of the restore path found illegal provider history, phantom rewind snapshots, dropped resume notices, and replay that finalized a question the load was about to re-hang. Co-authored-by: Cursor <cursoragent@cursor.com> * test(cli): pin ask_user_question restore suppress wiring in acpAgent Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(daemon): skip persistence for a whole restored batch that ends unattended Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): pin restorable ask_user_question preservation on a real Config --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
f1b1305a76
|
feat(models): support dual-role image generation models (#9650)
* feat(models): support dual-role image generation models * fix(models): address dual-role image selector review * test(cli): cover image model resolver rejection * fix(models): preserve legacy vision image routes |
||
|
|
1e062a4d0f
|
perf(cli): raise VP scroll rendering to 60 FPS (#9681) | ||
|
|
509226260c
|
feat(review): back comment-status and presubmit for Aone Code targets (#9627)
* feat(review): back comment-status and presubmit for Aone Code targets
A second `--comment` round on an Aone MR re-posted every still-valid
finding as a new comment and never downgraded a self-MR review — both
flows were skipped for lack of a1 backing. Route Aone targets at the a1
reads (mr view / mr status / mr comment list / auth whoami) through the
same pure classification cores the GitHub path pins, so the report
schemas and the Step-7 downgrade semantics stay one contract:
parentNoteId threading, closed → resolved, outdated → stale (a
rewritten line stays re-postable), no commit anchors (code facts
degrade to unknown), and drift with no compare API fails safe. The
context-unavailable verdict cap stays until pr-context lands.
Closes #9613
* fix(review): harden Aone runners' pr_number guards and null gate payload
Address round-1 review findings on the Aone backing of comment-status
and presubmit:
- extractStatusChecks no longer throws a TypeError when a1 answers a
bare null to `mr status`; the payload now reads as the designed
unreadable gate state (undefined), capping the verdict like a
still-running check instead of crashing presubmit with no report.
- comment-status and presubmit validate pr_number with fetch-pr's
/^[1-9]\d*$/ grammar before Number() coercion, refusing '012'/'1e3'/
'0x1f'/' 12'/'12.0' tokens that would query a different MR than the
caller's label carries.
- Pin the two subject_type combinations no test covered (pathless
comment WITH outdated:true; the live path+line shape) with
mutation-probed assertions.
- Align the --host describes with the sibling commands' detection
wording (omission no longer promises github.com), name the real
bucket (`resolved`) in the review skill's Aone dedup note, and scope
the design doc's remaining-unbacked claim to its own section.
* test(review): pin the Aone dedup seams the round-2 review named (#9627)
Four mutation-verified pins on the existing Aone backing, each closing
a round-2 Suggestion:
- classifyAoneChecks: the continue-scan cell of aoneCheckState — an
unrecognized value in an earlier key beside a recognized verdict in a
later key reads the verdict, not pending (a first-present-key mutant
now fails)
- classifyAoneChecks: a context-keyed FAILED gate carries its name —
the passing context-keyed case pinned nothing because passing gates
never collect names
- both comment mappers: `note` beats `body` when BOTH keys are present
(`??` does not coalesce `body: ''`, so an inverted priority would
blank every recognition signal and re-post the whole review)
- aoneCommentToPresubmitComment: parentNoteId maps onto
in_reply_to_id, including the absent-stays-unset half
No source changes; each pin fails under its named mutant and passes on
the current code.
* test(review): pin the five Aone seams the round-3 review named (#9627)
* fix(review): align Aone comment reads with measured a1 facts (#9627)
* fix(review): read fully-dropped Aone checks array as pending, not all-clear (#9627)
* fix(review): match SKILL.md self-PR wording to the revert-guard test
The merge resolution reworded the self-PR note to "matched against the
'a1 auth whoami' account", but SKILL.test.ts's revert guard (#9616, #9627)
pins the exact phrase "the MR author is matched against 'a1 auth whoami'".
Restore the pinned wording (semantics unchanged) so the bundled-skill test
passes.
* fix(ci): record qwen-autofix.yml's actual size in the workflow ratchet
The workflow-size ratchet failed on this PR: qwen-autofix.yml is 397656
bytes but .size-baseline recorded 392111 (5545 over, allowance 4096).
The oversize was inherited from main, not introduced here: main's ratchet
commit (
|
||
|
|
b6901ee0fd
|
feat(web-shell): refresh composer skills incrementally after toggles (#9131)
* feat(daemon): attach skill-toggle mutation metadata to settings_changed Hosts can apply Skill toggles incrementally without a full task reload or suppressing skills.* events. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(review): fit skill-toggle mutation metadata in the SDK bundle budget The new normalizer parser pushed the browser daemon bundle over the 186KB cap. Raise it to 187KB and pin the review gaps that were cheap to close. Co-authored-by: Cursor <cursoragent@cursor.com> * test(daemon): pin skill-toggle mutation event count and parser edges Co-authored-by: Cursor <cursoragent@cursor.com> * feat(web-shell): refresh skills incrementally after toggles * fix(sdk): raise daemon browser bundle budget for skill-toggle metadata The 190KB cap overflowed by 491 bytes after merging main, so the SDK build fails before tests run. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): retry cancelled skill-toggle refresh per session Marking the mutation handled before the workspace reload settled dropped the fallback when the user switched sessions mid-flight. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): keep skill fallback until session snapshot reflects toggle Reference-identity snapshot checks dropped the workspace fallback on unrelated command updates, so a disabled Skill stayed in composer autocomplete. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): scope skill-toggle fallback to workspace and pending toggles A partial toggle from another workspace, a superseded later mutation, or an unknown session skill list could leave a stale Skill in composer autocomplete. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): pass initial value to skill-mutation origin ref React 19's useRef types require an argument; the missing initializer broke the web-shell build. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): restore skill fallback after workspace round-trip Dropping the fallback on a workspace switch left the handled mark in place, so returning to the origin session never reinstalled it. An unknown session skill list now also uses the ready workspace snapshot instead of an empty composer. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): keep skill mutations per workspace after toggles An intervening toggle in another workspace overwrote the only mutation slot, so a failed live refresh could not restore that workspace's composer snapshot. Also read nested _meta.availableSkills on live command updates so the session skill list is not wiped to empty. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): retain batched skill mutations instead of the latest only Two distinct toggles in one replay or live commit used to keep only the last mutation, so a later applied toggle could drop an earlier partial fallback and leave a disabled skill in composer autocomplete. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): keep Skill composer source correct after toggles Skip the applied fast path while session skills are unknown, and drop leaked pending toggles when the workspace fallback is cleared. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): keep skill-toggle refresh scoped to #9123 Drop multi-workspace mutation books, pending-toggle merging, and the nested skills mapper so Web Shell only consumes skill_toggle metadata and refreshes the composer. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): drop unused skill-refresh eslint disable The scoped effect already lists its deps, so the leftover exhaustive-deps suppression failed lint:ci. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web-shell): revalidate partial skill mutations * fix(web-shell): reconcile queued skill mutations * fix(web-shell): reconcile zero-session skill refreshes --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: YungSen Hsin <yungsenhsin@U-G0HXNQM1-2052.local> |
||
|
|
ec8a8a1a97
|
feat(review): back pr-context on Aone Code targets (#9621)
* feat(review): back pr-context on Aone Code targets pr-context was the one read subcommand still gh-direct, so every Aone run was forced context-unavailable: the verdict capped at COMMENT (the wired a1 approval could never fire), Agent 0 skipped, and the machine ledger never recovered from posted summaries. Route it through the platform reader with a normalized context bundle; Aone serves it from mr view + the flat comment list (thread comments carry the ledger), GitHub's implementation is an extraction of the existing calls — its output stays byte-identical. The forced cap leaves the Aone write path for parity with GitHub's state-claim handling, and the refetch commands a context file emits bake --pr on Aone, where comment bodies are addressed per-MR. * fix(review): keep Aone ledger carriers out of the blocker re-check (#9621) On Aone this pipeline's own round summaries are path-less comments, so they ride pr-context's issue channel, where their visible **[Critical]** lines self-promoted every prior Critical-bearing summary into "Blockers to re-check" — rendering each prior Critical three times (beside the ledger section and the inline roots that own the same findings) and spending the section budget on the pipeline's own prose until genuine human blockers degraded to snippets. Exclude bodies carrying the ledger marker from issue-channel promotion and the stdout count, strip the marker out of the settled snippet, and switch the pr_number guard to the canonical isPositivePrNumber so 0x10/5. spellings cannot fragment side-file continuity. Pin the witnesses the round's findings name: the guard, args.host forwarding, the issue-kind --pr refetch branch, the account-first author keying, and the GitHub test suites' independence from the cwd-origin probe. * fix(review): refuse pr_number spellings that do not round-trip (#9621) isPositivePrNumber alone admits two spellings whose Number() value does not round-trip to the raw string: leading zeros (007 fetches 7 but the raw string labels the heading and the prev-ledger side file, so a later 7 run reads a different side file and the round counter restarts) and digit strings above Number.MAX_SAFE_INTEGER (Number() silently rounds them, fetching a different PR than the labels announce). Add the safe-integer and no-leading-zero conjuncts — matching fetch-pr's [1-9]\d* rule — so every admitted input satisfies String(Number(x)) === x. Also pin the witnesses the round-2 review names: the commit_id round-trip through the GitHub reader and toRawReview into the persisted side file (both spreads were unwitnessed), the stale force-applies comment in submit-aone.test.ts the cap removal outdates, and the setup batch's Aone carve-out for the unbacked comment-status call. * docs(review): align Aone docs with the landed no-ancestry anchor rule and comment-status skips D6 described the AGit-Flow anchor as inert until the incremental rule landed, but that rule (#9630) merged while this branch was in flight — anchors now delta-scope Aone re-reviews. SKILL.md's comment-status section and Step 6's report-existence guard now name the Aone skip the setup batch already carries, so no path sends an Aone run at the unbacked command or at a report that was never written. * docs(review): annotate #9616 as landed and define the report-less re-check rule The out-of-scope list still read self-PR detection as open work although #9629 shipped it into this branch's merge base — annotate it like the sibling #9618 entry. Step 6's report-existence guard pointed report-less runs at a re-derivation the skill never defines; replace it with the explicit rule: no per-thread status routing, no hand-derived substitute, rule from the code at the reviewed commit, cannot-tell over a guess. * fix(review): route the context head through aoneHeadSha and close the round-5 findings getReviewContext read sourceBranch raw while every other head read trims — a padded server value diverged the context file from the rest of the run (phantom-drift shape). getCurrentUser now honors the seam contract on the anomalous whoami shapes instead of leaking untagged throws and non-string accounts. Step 6's report-less rule no longer contradicts the comment-status failure contract: runs where the command ran and failed keep the "re-derive if needed" fallback. The Aone paragraph names comment-body among the backed reads, and witness tests pin the identity gate's carriers key and the head normalization. * fix(review): shape-check the Aone comment listing in getReviewContext a1 can answer repo mr comment list with an exit-0 a1.error/v1 error object (backend auth failure or client timeout — measured by cleanup's a1CommentList on the identical payload). Without a guard the object survives the ?? [] coalesce and .filter throws an untagged TypeError, losing the envelope's actionable message at exactly the recoverable moment. Guard as the provider family already does and surface the cause; witness tests pin both envelope shapes (mutant-checked). * test(review): pin getCommentBody's body-field fallback (mutant-checked) * fix(review): union resolved comments into the Aone context bundle The default comment list excludes resolved comments (measured by the cleanup audit) while GitHub's REST fetches include them, so a resolved blocker/marker root never reached the re-check walk or the fail-closed identity gate. Union the default and --resolved listings as the audit does, dedupe by id, fail closed on either listing's error envelope, and disclose the residual that resolved replies stay invisible; witness tests mutant-checked. * fix(review): serve resolved comments and guard the envelope in getCommentBody getCommentBody queried only the default comment list while the context bundle it serves refetches for unions in resolved comments — a resolved id named by a truncation note threw "not found" every time, and an exit-0 a1.error/v1 envelope threw an untagged TypeError that lost the actionable message. Extract the shape-checked default+resolved union helper and read both sites through it; witness tests mutant-checked. * ci: correct qwen-autofix.yml size baseline to its actual post-migration size #9677 shrank qwen-autofix.yml from 431526 to 397656 bytes (prose moved to the design record) but recorded the baseline at 392111, 5545 below the file's own post-change size, so the first PR to run the ratchet tripped it. This branch introduces zero growth to the file (byte-identical to main); the bump aligns the baseline with reality. No workflow content changes. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
f829a02896
|
feat(review): validate Aone inline anchors against the captured diff before posting (#9634)
* feat(review): validate Aone inline anchors against the captured diff before posting Aone Code performs no server-side anchor validation — a controlled probe (scratch MR 29427547, a1 v0.2.51) proved any --line integer posts, and an old-side number silently lands on the same-numbered new-side line. The old side cannot be anchored at all, and file-level comments drop their path. Pin the removed-line semantics for the Aone write path: submit's Aone branch now validates every well-formed inline anchor against the review's captured diff before posting. An unanchorable Critical is relocated into the summary body, an unanchorable Suggestion discarded and counted — the GitHub 422-recovery dispose, performed in code — each disclosed in the terminal. A missing captured diff refuses the whole post; malformed shapes (missing path/line, reversed range, renders-as-nothing) keep their consistency-gate refusals, and a garbage state.bodyCriticals stands the gate down so compose's pinned refusal fires. The GitHub path is untouched — its server performs this validation. Issue #9615 * fix(review): reject unpostable anchors and unify the Aone gate's shape refusals - validateNewSideAnchors now rejects the input domain (fractional/zero/negative lines and reversed ranges) before the hunk scan, so its verdict can no longer certify an anchor the zero-validation Aone platform would post silently wrong. - Extract the consistency gate's per-comment shape checks into one shared predicate (commentShapeProblems) read by both the loud refusal and the Aone anchor gate, so a shape the gate disposes is never a refusal the operator misses (open fence, start_line-without-side). The path check becomes a type check, closing truthy non-string paths that reached the write seam unvouched. * fix(review): generalise the Aone gate's stand-down and harden its relocated entries Round-2 review fixes for the Aone anchor gate: - The stand-down now keys on ANY degrade that touches the payload and covers every compose-owned garbage shape: bodyCriticals that is not an array of strings, or a suggestionsDiscarded compose's counter refuses. The countability test reads compose's OWN acceptance table (toCount, exported as the total tryToCount), so the gate's merge and compose's counter can never drift — an integer-but-not-safe count now merges instead of silently dropping the gate's discards. - The relocated entry's claim extraction strips a leading marker RUN (fixpoint, like every other strip) and treats a fence-delimiter claim line as absent — both shapes used to leak raw markers or junk delimiters into the posted summary-body blocker line. - The gate keeps the model-authored comment indices through its removal (and floor enforcement keeps them through its own), so the consistency gate's refusal names the culprit in the model's payload JSON instead of a renumbered position the re-compose loop cannot act on. - A --dry-run with a missing capture no longer exits 3: it writes nothing, so it skips the gate with a disclosure and reports wouldPost: false (reason: aone-diff-missing); the exit-3 refusal stays reserved for the real write. - The MULTI_DIFF fixture's second hunk header becomes byte-exact git output (@@ -20,0 +22,2 @@, probed against git itself). - The design doc gains the gate-relocation doctrine (relocated entries deliberately inherit the model's own tag-exemption treatment), the dry-run carve-out in the failure-shape table, and the corrected fence/one-line-channel claim. * fix(review): close the anchor-gate witness gaps and a footer-leak in the relocated entry Gap-fill on top of the round-2 gate hardening: - The relocated entry's claim extraction strips the canonical footer FIRST: with an empty claim line (a marker-plus-separator-only body), the separator strip eats the newline+colon and the extraction falls THROUGH into the appended footer's first line, posting it as the claim. Witness added for the placeholder shape. - Pin the multi-line relocation entry CONTENT (it must cite the claimed end line, not the start — the start sits inside the hunk and looks fine) and the disclosure naming it. - Witnesses for the remaining mutant-tested gaps: a range whose start sits outside every hunk and end inside (the startLine mapping), the dry-run compose parity (preview composes from the gate-corrected payload), the suggestionsDiscarded 0 merge boundary, the empty-path shape (loud refusal, never a gate disposal), a declared LEFT start_side without a start_line, and the equal-boundary range (start_line === line, a shape GitHub itself produces). - The routing suites run from a per-test fixture cwd, so the captured-diff seeding and its cleanup can no longer overwrite or delete a same-numbered live capture in the real vitest cwd. Issue #9615 * fix(review): sanitise relocated-entry paths and stand down over any compose-refused bodyCriticals * fix(review): keep the anchor gate's captured diff when resetting the receipt state The Aone receipt suite's beforeEach wiped the whole .qwen tree to start from no receipt — deleting the captured diff the anchor gate needs along with it. Every post then died at the gate's missing-capture refusal and no receipt was ever written (ENOENT in the four receipt tests on CI). Remove only the receipt file; the seeded diff survives. * fix(review): close the anchor-gate entrances the review rounds demonstrated Round-3 remediation of the review comments on the Aone anchor gate: - R3-2 (structural): the BUILT relocated entry is now validated against compose's own ingestion (tryIngestBodyCriticals over the single entry) before the relocate is disclosed, and any refusal degrades the entry to the inert constant `finding — (no path):<line>` — the entrance space is unbounded model text and compose's acceptance is the authority, so a shape the enumerated guards never anticipated degrades the entry instead of refusing the whole post mid-degrade. The demonstrated entrance (a lone CR inside the claim: it passes the leading-fence guard, compose's CR normalisation then splits the entry and the second line leads with a fence delimiter) is covered by a witness. - Ledger collision: the relocated entry flips to `<claim> — <path>:<line>` — the claim leads, so a carried id keeps position 0 and the ^-anchored ledger readback matches instead of silently renumbering a carried finding as new. Witness asserts the id survives the readback regex. - R7-1: an explicit JSON null side/startSide reads as ABSENT (defaults to RIGHT), the model's idiom for an omitted optional field — never a declared old side. Unit and gate-level witnesses. - R3-3: witness for the non-identity authoredIndices branch — the gate renumbers the array, floor enforcement keys on the post-gate array, and the remap drops the comment floor enforcement names. - R4-2: the hostile-paths test gains the \r-bearing path (compose's ingestion normalises a bare CR to a line break — the same hostile shape as \n; the guard's \r half was unwitnessed). - R3-5: the design doc states the carve-out — the non-RIGHT degrade runs for single-line comments only; a multi-line non-RIGHT comment keeps the consistency gate's whole-post refusal; null side is absent, not a declaration. The failure-shapes table splits the row accordingly. Issue #9615 --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
2172721405
|
feat(cli): restore each daemon session onto its last selected model (#9687)
* feat(cli): restore each daemon session onto its last selected model Idle detach currently rebuilds Config from settings.model.name, so session A picks up whatever model session B last switched to. Fixes #9686 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): address session-model persistence review findings - reader: always select the last assistant record into the restore read set so the legacy lastAssistantModel fallback still fires when a trailing chat_compression candidate excludes it from the resume read - recorder: assign currentSessionModel before the awaited write so a rewind landing in the pending-write window re-anchors the new binding instead of the stale one - reader/recorder: reject non-string session_model payload fields instead of crashing the restore path on malformed transcripts - protocol doc: describe the session_model append as best-effort, not an unconditional consequence of a successful switch - cli: import RUNTIME_SNAPSHOT_PREFIX/stripRuntimeSnapshotPrefix from core instead of duplicating the prefix algorithm locally - tests: pin the isRuntime/baseUrl payload dimension, the prefix and route-mismatch false arms, the neither-field fallback, and regression coverage for the two fixes above * fix(cli): keep daemon session-model restore from failing load Pre-auth restore skipped the last-assistant fallback, and a recorded qwen-oauth binding could hard-fail load when cached credentials were gone. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): roll session-model auth retry back onto the settings route Same-id baseUrl restores and runtime-only settings models were skipping or breaking the fallback, which made load fail on the recorded credential set. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): keep empty daemon sessions from creating a transcript Recording the session model on newSession wrote a jsonl file before any user content, so close/delete/child-death left the id occupied and listing still showed the empty session. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): allowlist restored session-model routes against the registry JSONL baseUrl is only a registry selector, so unknown hosts are dropped before switchModel. Restore also keeps the last valid session_model payload instead of falling through a torn trailing record. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): retry session-model auth after same-id snapshot restore The retry gate ignored runtime-snapshot identity, so restoring an implicit registry route off a same-id snapshot looked unchanged and skipped rollback. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
39378ac0a4
|
feat(serve): restore ask_user_question HITL on session load/resume (#9665)
* feat(serve): restore ask_user_question HITL on session load/resume Keep a trailing unanswered question votable after daemon load/resume when --restore-ask-user-question is on, instead of closing it as a failed tool result. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(daemon): harden ask_user_question restore per review feedback - acpAgent: defensive restore hint (no `!` lookup; accepts undefined session) + normalized id lookup on both cold return paths; session test doubles carry shouldHintAskUserQuestionRestore - bridge: single maybeFireRestoreAskUserQuestionPrompt helper with the full admission-time busy predicate (pendingPromptCount + goalTurnActive), sync-throw try/catch, no-attached-client gate, fork suppression, and hasActivePrompt reflecting an admitted restore prompt; child-bound requests carry a suppress meta when the daemon already knows it will decline, keeping replay skip and re-hang aligned - Session: restore prompt gated on the config flag; early bail before per-turn bookkeeping when history is not restorable; system reminders ride the post-answer message; restore turns no longer burn the active-todo reminder; a permission timeout on a restored question no longer persists the fabricated decline (transcript stays dangling for a later re-hang); continueLastTurn declines a restorable question; restorable detection reads peekLastHistoryEntry instead of cloning the full history - history-replay-page: isInitialized() guard on the skip probe; dead paged-path skip wiring removed - transcript-replay: skip set matches raw ids after dedup renames - core: inline orphan-repair preserves the restored AUQ ids; the compression side query strips a trailing dangling functionCall; the CLI flag is honored only in ACP mode * fix(cli): skip restore hint helper when the switch is off Load/resume used to call shouldHintAskUserQuestionRestore on every Session, including test doubles that do not implement it. Short-circuit on argv first so the default-off path stays independent of the restore-only API. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
2a99e84169
|
fix(review): clear the deferred Round-5 findings from the Aone write path (#9604)
* fix(review): clear the deferred Round-5 findings from the Aone write path The full cleanup of #9579 — the 29 Suggestions deferred from round 5 of the /review bot on #9491 under the ~5-round rule (Criticals-only from that round on). One item (the GH_HOST setGhHost assertions) was already landed with the round-5 Critical fixes; the rest are implemented here. Write-path fixes: - A shaped-but-empty --host refuses with its own shape (host-flag-empty) instead of collapsing to the unbound refusal the flag was the remedy for — the agent re-run loop the refusal wording exists to break. - An invalid host (recorded verbatim or flag-typed) refuses in the exit-3 shape naming the offender and its origin, instead of setGhHost's TypeError escaping runSubmit as a failed command. - A flagless gh post whose nothing-bound routing would inherit an ambient GH_HOST pointing at canonical Aone refuses actionably (ambient-gh-host-aone) instead of failing opaquely after compose ran. - The shared authorisation gate no longer reads an absent host as a github.com claim for callers whose routing follows the recorded binding (submit): the ordinary flagless publish of a GHE-recorded review passes, while publish-assets keeps the strict comparison. - Mid-batch drift disclosure rides the partial-post shape too (headMovedDuringPost on AonePartialPostError, warned from submit's partial branch), and the post-batch re-read is tri-state: a failed re-read leaves headMovedDuringPost undefined and submit discloses "could not re-verify" instead of a false all-clear. - The Aone success JSON surfaces postedCommentIds/summaryCommentId — the audit the partial shape carries and the gh receipt records. Docs and contract fixes: - The context-unavailable cap wording now says what it does (keeps an Approve verdict at Comment; a Request-changes verdict still posts) in the user docs and both SKILL.md sites. - The head-drift bullet is qualified by the per-review restart bound — spent on Aone there is no submit-at-reviewed-SHA fallback; report and leave the rest to the user. - Step 9's Posted: contract admits the no-link note the Aone fallback prescribes. - The --host help text spells both canonical Aone hosts out. - The provider design doc's Phase-3 "refuses" sentence is marked superseded. Test hardening (unfalsifiable pins made falsifiable): - ensureAoneAuthenticated ordered before the writes; setGhHost ordered before the gh write; the a1 path never touches the gh host state. - Live-probe cells for the explicit-flag precedence, the unbound refusal, and the fast-path hostless refusal; the recorded-binding- outranks-probe fixture driven through submit's real gitOpt seam. - submit.test.ts mocks ./lib/git.js (no real git spawned in the vitest cwd), isolates the cross-session suite's recording store via chdir, and pins the newest-wins ordering when two recordings of one PR carry different hosts. - Producer-side 'refusing to post:' prefix pins, the RC-Note count source pin, the contextUnavailable:true gh-path pin, and the floor recovery's callerHost pin. * fix(review): address round-1 findings on the Aone write path (#9604) * fix(review): address round-2 findings on the Aone write path (#9604) Extract one refuse helper for submit's seven exit-3 refusal shapes (sibling publish-assets precedent), align the Aone pre-write refusal prefix with the other refusal paths, and pin the invalid-host remedy of the flag/origin arms positively — the recorded arm's absence pin alone let a ternary-collapse mutant ship green. * fix(review): address round-3 findings on the Aone write path (#9604) Make submit's exit-3 refusal terminal: refuse now throws a SubmitRefusal that runSubmit's single catch renders into the refusal shape (stderr line, posted:false JSON, exit 3), so a gate that says no cannot fall through toward the write — the helper previously returned and relied on every call site adding its own `return;`. Also extract the post-batch MR-head re-read, duplicated between submitAoneReview's partial-post and success paths, into one helper. * fix(review): address round-4 findings on the Aone write path (#9604) * fix(review): address round-5 findings on the Aone write path (#9604) * fix(review): address round-6 findings on the Aone write path (#9604) * fix(review): resolve merge-conflict residue in the review skill (#9604) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
0c36e5093a
|
feat(review): close Aone residual gaps — composeUrl, test-plan routing, a1 version floor (#9624)
* feat(review): close Aone residual gaps — composeUrl, test-plan routing, a1 version floor The three residuals #9619 tracks together, one pass: - composeUrl joins the platform reader: GitHub composes the PR-page URL from the routed host (deterministic grammar, no API call); Aone is reader-backed — the platform's own detailUrl, never assembled, since the nested-group owner/repo collapse can name a different repo. submit fills a receipt that carries no url through it on both platforms, so the skill's prose fallback shrinks to the coordinates relay for the one case the reader cannot serve. - test-plan's body fetch routes through the platform reader: the MR description on Aone (already in the reader's fetch metadata — no new API surface), so the Test Plan check runs on Aone targets instead of going unchecked on every run. - ensureAoneAuthenticated enforces the a1 version floor design-doc Q1 asked about — 0.1.90, the version the platform facts were probed against — in presence → floor → auth order, each with its own remedy message; unreadable versions are disclosed on stderr and fail open. Verified: ~590 targeted unit tests, tsc/eslint/prettier clean, build + bundle green, and a CLI smoke that refuses a fake stale a1 at the floor while a fake fresh one passes the gate. * fix(review): apply round-2 review on the Aone residuals All six round-2 suggestions on #9624, probe-verified and pinned: - R1-1: the version-probe fail-open now discloses the CAUSE — the extraction mirrors the whoami catch (first non-empty line past the execFileSync preamble), so segfault / unsupported flag / permission failures stay distinguishable instead of one constant preamble line. - R1-2: aoneReader.composeUrl discloses a failed lookup on stderr before degrading to '' — every other fail-open in the provider discloses, and the coordinates-relay case must stay distinguishable from an environment fault. - R1-3: one home for the PR-page host spelling — normalizeGhHostForUrl in lib/gh.ts, shared by compose-review's comment anchors and the reader's composeUrl, so a `--host GHE.Corp:443` run can no longer print two textual spellings of the same PR page; non-default ports survive. - R1-4: submit no longer re-queries the reader when the Aone receipt carries no webUrl — detailUrl is a stable MR attribute and the pre-write drift-gate read already carried it, so the second fetch could only block on the flaky state that lost the field. The empty receipt rides the coordinates relay; the reader keeps composeUrl as the canonical seam. - R1-5: the Aone body-fetch route runs the same ensureAuthenticated gate every other a1-backed flow runs first — a standalone test-plan on a missing/stale/logged-out a1 now fails exit 1 with the install/upgrade/login message instead of exit 0 with the generic note. The GitHub arm keeps its historical degrade. - R1-6: the handler wiring (the Aone fix's integration point) is pinned by handler-level tests — an Aone --host must route the body through the reader with the gate first, and a refused gate fails the command before any fetch. SKILL.md's Posted paragraph, its revert-guard pins, and the design-doc bullet follow the R1-4 semantics. Verified: tsc/eslint/prettier clean, 759 targeted cli tests + 23 SKILL guards green. * fix(review): apply round-3 review on the Aone residuals * fix(review): apply round-4 review on the Aone residuals * fix(review): fail closed on unknowable host in composed receipt link (#9624) * fix(review): route explicit GHE-family hosts to the GitHub reader Platform detection selected Aone on ANY *.alibaba-inc.com host for explicit --host/--remote signals, but a family host that is not the canonical pair (ghe.alibaba-inc.com is the live example) is a GitHub Enterprise instance — such a review authenticated against a1 and read an unrelated same-numbered Aone MR instead of the GitHub PR body (and test-plan additionally gated on a1 auth first). Explicit signals now select Aone only via the canonical pair (code./gitlab. alibaba-inc.com) — the same canonical-only rule the write gate has always applied — while the family predicate survives on the no-explicit-signal cwd-origin fallback. parse-args stops refusing /pull/ URLs on GHE-family hosts (the same predicate misapplied: those are real GHE PR URLs), and the five --host describe texts now name the canonical pair. Pins: registry.test.ts flips the GHE explicit-host expectation to github, adds the explicit-remote and canonical-port arms, and keeps the family cwd fallback; detection-side 592 + write-side 322 tests green. * fix(review): fail closed on family-only /codereview/ URLs AONE_CR_URL_RE captures the whole *.alibaba-inc.com family (shape-first grammar), but a family-only host is a GHE instance that serves no /codereview/ page: accepting its URL as a live target would let detection route the explicit GHE host to GitHub and aim fetch/submit at GHE PR #<id> — a target the supplied URL never named as a valid GHE resource. The classifier now gates the aoneMatch branch on isAoneCanonicalHost, so non-canonical /codereview/ inputs stay invalid-url, mirroring the /pull/-on-canonical-Aone refusal. The mirror arm is pinned too: a /pull/ URL on a family-only host parses as the real GHE PR target it is. --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
6bbb273a86
|
perf(web-shell): optimize streaming transcript rendering (#9672)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
* perf(web-shell): optimize streaming transcript rendering * test(web-shell): pin streaming fast paths --------- Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com> |
||
|
|
7703d1c310
|
docs: classify architecture invariants by enforcement mechanism (#9152) (#9689)
* docs: classify architecture invariants by enforcement mechanism (#9152) Record the policy decision asked by #9152: which architectural invariants are enforced mechanically, which are left to review, and which are not worth enforcing. Covers every invariant asserted in AGENTS.md and every open architecture issue (#8084, #9145, #9146, #9151, #4063). The drift-guard decision: do not extract a reusable framework from check-voice-guard-sync.js. The two new drift guards (cross-package-contracts.test.js and approval-mode-drift.test.ts) share a pattern but not enough structure to justify abstraction. The pattern is documented for copy-when-needed. * docs: correct guard references and complete the invariant inventory (#9152) * docs: fix #9145 attribution and drift-guard line counts (#9152) * docs: classify Web Shell UI conventions and reclassify Node engines (#9152) * docs: cover the full Web Shell convention set without a count claim (#9152) |
||
|
|
7bc0d80998
|
fix(review): audit Aone targets in cleanup's bypass tripwire (#9633)
* fix(review): audit Aone targets in cleanup's bypass tripwire Step 9's bypass audit already flags same-account writes on GitHub that bypassed `qwen review submit`, but Aone targets had no tripwire at all — cleanup audited them against GitHub (a hostless report hit github.com's same-named repo; a recorded Aone host pointed gh at a host it has no auth on). Route the audit by the fetch report's recorded host with the registry's cwd-origin fall-through, list the MR's comments through the a1 CLI (default + --resolved union — the default listing hides resolved comments), and flag any comment the authenticated account posted — or edited — inside the window that the submit receipt does not vouch for. Submit now records a commentIds receipt axis (Aone's sanctioned write posts comments, not a review) on success and on a partial post. Closes #9617 * fix(review): preserve both receipt axes on the submit receipt rewrite The submit receipt is keyed by PR number alone but carries an axis per platform — review ids on GitHub, comment ids on Aone — and each writer rebuilt the whole file from only its own axis. A submit on one platform silently erased the ids the other platform's submit vouched for a same-numbered target, and that platform's cleanup audit then flagged submit's own sanctioned writes as bypasses. Merge the whole prior receipt into the rewrite so both axes survive. Also flatten a1's message-less JSON error object in the audit's skip note instead of paging its opening brace, tag an unparseable `a1 auth whoami` answer with the failing command, name the audit's third disclosed residual (an edit of an unvouched pre-window comment is invisible once its discussion is resolved), and pin the previously unwitnessed audit contracts: the receipt vouch's edited-arm exclusion, the Aone auditSince window boundary, the --resolved union's dedupe, the header shape, and both footer platform nouns. * fix(review): tag null whoami answers and disclose audit residuals (#9633) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
e2de7d2884
|
feat(web-shell): Bind GitHub PRs to sessions with sidebar badge and search (#9543)
* feat(web-shell): Bind GitHub PRs to sessions with sidebar badge and search
When a PR is created from the Web Shell Git dialog, bind its number and
URL to the current session. The daemon accepts the binding through the
session metadata routes (validated at the route, bridge, SDK, and sidecar
layers, with the URL restricted to http(s) since it is rendered as a link
target), keeps it in live memory, and persists it as a per-session sidecar
file so the binding survives daemon restarts and follows the session
through archive/unarchive/delete.
The sidebar renders a #N badge next to the session title (opening the PR
via the desktop-aware external-link opener, shows the PR in the details
tooltip, and the session search now also matches PR number, branch name,
and worktree slug — so with many concurrent sessions, the one that
produced a given PR is one search away.
EOF
)
* feat(web-shell): Support multiple PR bindings per session
A session can produce several PRs (stacked or follow-up work), and
keeping only the latest binding would defeat the sidebar's
search-by-PR-number flow for every earlier one. The binding is now a
bounded list (10, oldest dropped) ordered by binding time: re-binding
the same number refreshes it and moves it to latest, the badge shows
the newest number with a +N overflow, the tooltip lists every bound
PR, and search matches any of them. The write API stays single-binding
per call; reads, SSE events, and responses carry the full list, with
the sidecar as the complete history merged over the live entry's
daemon-lifetime bindings.
* feat(web-shell): Show PR badges in the session overview and picker dialogs
The mission-control overview panel and the shared session picker row
(resume / delete / release dialogs) now show the same PR badge as the
sidebar — latest number with a +N overflow, opening the PR via the
desktop-aware opener — and the resume dialog's search matches bound PR
numbers, branch names, and worktree slugs through the shared
sessionMatchesGitQuery helper.
* fix(web-shell): Match the overview PR badge color to the sidebar accent
The overview card badge used the panel's neutral --primary tint while
the sidebar and picker badges use the accent violet; one element should
read the same on every surface.
* fix(web-shell): Address review findings on PR bindings
Read/display correctness (verified by ytahdn and the R1 review):
- mergeLiveSessionSummary merged {..existing, ..live} wholesale, so a
live entry's this-daemon-lifetime prs overwrote the sidecar-enriched
full history after a restart; prs is now merged by PR number (live
url wins, history kept), and the dead merge branch in
enrichPrSidecars is gone.
- The pr-only session_metadata_updated event carried no displayName,
which SDK folds treat as "cleared" — the title blanked on every PR
bind. The producer now echoes the current name.
- GitDialog synced sessionIdRef from the prop on every render, so the
fresh session id the dialog resolves for its own side queries was
clobbered before the binding read it; the sync now runs only when
the prop changes.
Robustness:
- upsertSessionPr's read-modify-write is serialized per sidecar path,
closing the concurrent-bind drop race under runSharedMany.
- The REST routes persist the sidecar before mutating the bridge, so a
failure on either side leaves the binding durable.
- The ACP dispatch only upserts when the call actually binds a PR (a
displayName-only rename no longer rewrites createdAt/order).
- pr.url is capped at 2048 chars across all four validation layers.
Structure & a11y:
- The three badge copies (sidebar / overview / picker) are now one
SessionPrBadge component: shared CSS, count-aware aria-label,
non-http(s) entries filtered defensively, and tabIndex=-1 inside
listbox options.
- The SDK's duplicated PR validator is a single session-pr module used
by both DaemonClient and events.
- Delete/Release dialogs' search matches bound PR numbers like Resume.
Tests: list-level live+sidecar prs merge, sidecar-vs-bridge echo
authority, route tests made order-independent, bridge
atomicity/cap/catalog-revision/displayName-echo cases, concurrent
upsert serialization, SDK fold keeps the name, GitDialog bind-failure
degradation, url-cap rejections at every layer.
* fix(core): harden session pr sidecar persistence and moves (#9543)
* fix(serve): align session pr echoes with the persisted sidecar (#9543)
Address round-4 review findings:
- R4-1 (Critical): bridge entries are re-created without prs on daemon
restart / close / archive-restore, so ACP and REST metadata updates
replied and broadcast only this daemon lifetime's bindings, silently
dropping persisted history. Hydrate the entry from the sidecar before
the mutation (new optional bridge seedSessionPrs) and make the ACP
handler reply with the authoritative persisted list like the REST
routes, fixing both the response and the session_metadata_updated
event on all three surfaces.
- R4-2 (Critical): the non-live metadata fallback persisted the rename
before the PR sidecar while bumping the catalog revision only after
both writes succeeded; a failed sidecar write stranded a durable,
unannounced rename behind a total-failure response. Persist the
sidecar first so a failed write leaves nothing durable behind.
- R4-3: map InvalidSessionMetadataError in toRpcError to the REST
invalid_metadata contract instead of an opaque -32603 Internal error.
- R1-5: add the stderr audit record for pr binding mutations, mirroring
the displayName branch (accepted in the round-2/3 thread).
- R2-4 (source part): make enrichPrSidecars' archiveState required so a
future archived-listing call site cannot silently enrich from the
active chats dir.
- R2-16: filter non-openable URL schemes in the session details tooltip
exactly like SessionPrBadge.
* chore(desktop): regenerate bun.lock to match workspace versions
Main's desktop lockfile drifted: @craft-agent/electron and
@craft-agent/shared are 0.0.5 in the workspaces but 0.0.1 in the
lockfile, and the @qwen-code/live-host workspace entry is missing.
bun install --frozen-lockfile (the Live Host CI gate) fails on any
PR touching packages/sdk-typescript/src/daemon/types.ts because of
this. Regenerated with bun 1.3.x.
* fix(web-shell): address R2 review findings on PR bindings
- SessionPrBadge: narrow onKeyDown to Enter only so the badge no longer
blocks roving-listbox navigation keys in picker dialogs (R2-15).
- SDK updateSessionMetadata: per-entry prs shape gate so a hostile or
buggy daemon response cannot surface javascript: urls or malformed
numbers downstream; valid entries survive (R1-17).
- Tests: bridge mirror atomicity (valid pr + invalid displayName),
GitDialog stale-id retry binding, list-level merge dedupe by number,
organized + archived listing paths keep PR sidecars, DaemonClient pr
request/parse + gate, Delete/Release dialog PR-number search (R2-6,
R2-13, R2-14, R1-15, R1-17, R2-20).
* fix(serve): address R5 review findings on PR bindings
Best-effort hydration (R5-1/R5-2/R5-3): the sidecar hydration read at
all three metadata-mutation sites (ACP dispatch, primary and workspace
REST routes) now absorbs non-ENOENT I/O errors as "no sidecar" instead
of failing the whole call — a squatted sidecar path no longer turns a
pr-less rename into a 500/-32603. This also makes the R4-2 fallback
ordering test reach the branch it names.
Validation hardening (R5-4): pr.url rejects control characters at all
four layers (bridge via hasControlCharacter, route, SDK guard, sidecar
reader) — the url is interpolated into the stderr audit line, so a
newline-bearing url could forge audit records.
Traversal parity (R5-5, R4-5): the ACP session/update_metadata handler
now gates on isValidSessionId before any sidecar I/O, and the primary
REST route's gate moved ahead of runtime resolution so traversal ids
get 400 invalid_session_id identically on single- and multi-workspace
daemons (previously 404 on multi-entry registries).
Tooltip (R5-6): PR rows key on index+number — a hand-edited sidecar
with duplicate numbers no longer risks cross-row reconciliation.
Tests: FakeBridge callLog pins seed-before-mutate order (R5-9);
cross-workspace pr sidecar lands in the owning workspace's chats dir
(R4-4); multi-workspace traversal test; metadata-filtered listing
keeps prs (R1-15); Resume dialog PR-number search (R2-20); dialog
fixtures annotated DaemonSessionSummary[] (R5-7/R5-8); control-char
rejection cases at bridge and sidecar layers.
---------
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
|
||
|
|
f1d05b79fc
|
feat(review): detect self-MR on Aone targets in presubmit (#9629)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* feat(review): detect self-MR on Aone targets in presubmit The self-PR verdict downgrade existed only for GitHub targets: the Aone read path skipped presubmit entirely, so a review of one's own MR silently carried the weight of an independent review (#9616) — exactly the wrong direction for the most common local Aone flow, re-reviewing one's own CR before the next amend. presubmit now routes by platform. On an Aone target it compares the authenticated account (a1 auth whoami) against the MR author from one mr view fetch — case-insensitive, fail-soft on a deleted author, fail-closed on an unreadable MR — and emits the same report shape with the unbacked slices neutral (CI classification and comment dedup have no Aone backing yet). The same fetch backs head drift via sourceBranch, and a malformed pr_number/owner_repo stays a usage error rather than a metadata blip. SKILL.md runs presubmit on Aone targets instead of skipping it, and the "self-PR detection has no Aone backing" caveat is gone from the skill and the user docs. * fix(review): unify Aone live-head reads and the presubmit whoami gate The round-1 review of the Aone presubmit found four seams the new path had hand-derived a second time; each is now stated once: - The self-PR comparison (including the load-bearing `author !== ''` guard) existed as two inline copies in presubmit.ts; isSelfReview states it once for both platform paths so a future normalization rule cannot diverge one platform silently. - "An Aone MR's live head is mr view's sourceBranch" was hand-derived in five places in aone.ts, two of them untrimmed: a padded server value then manufactured a phantom "PR head advanced during review" (and a submit-time refusal) against the trimmed reads, for an MR that never moved. aoneHeadSha states the fact once; all five sites route through it, repairing the two untrimmed copies. - The "same report shape as GitHub" invariant was convention only; both presubmit result literals are now typed against one PresubmitReport interface, so a field added to the envelope is a compile error on the path that forgets it instead of a silent toBool(false) at the consumer. - The Aone path spawned `a1 auth whoami` twice per run (plain gate + JSON account read). The gate now runs the JSON whoami once and returns the account: one spawn per run, and no account fetch remains after the MR fetch that could throw uncaught and orphan the graceful metaUnavailable report — the fail-closed path pays no a1 work after a thrown mr view. The padded-head regression cells for getPrMeta/getFetchMeta fail on the pre-round code; the empty-guard, single-spawn, and report-shape witnesses each fail under mutation probes. 4143 review tests green. * test(review): pin Aone presubmit auth-gate throw path (#9629) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
04886c4354
|
fix(review): make the incremental cache work for Aone AGit-Flow CRs (#9630)
* fix(review): make the incremental cache work for Aone AGit-Flow CRs * docs(review): qualify the Aone no-ancestry claims in comments and docs The D7 comments described the ancestry gate as unconditional and both ancestry tests as failing for every AGit-Flow update; the head test alone fails for every amend (the clamp fires only on amend-plus-rebase), and the narrowing join never lets a drift byte reach the published scope. Qualify the ledger.ts SHA_RE block, the resolveIncrementalAnchor docstring, the clamp-skip and call-site comments, the test-block comments, and the design/user docs accordingly. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
0dd518f950
|
feat(review): disclose that Aone posts join the discussion gate only (#9625)
* feat(review): disclose that Aone posts join the discussion gate only Aone has a dedicated ai_comment merge gate for AI-posted review comments. A controlled probe on a scratch CR (issue #9614) resolved the design doc's open question Q4: `a1 repo mr comment create` does NOT auto-set isAiComment for the posting identity (a general and an inline probe both read back false, re-checked against an async classifier), and a1 v0.1.90 exposes no flag to request it — so qwen-posted comments sit in the generic discussion gate only, and the ai_comment gate never tracks them. The same probe re-confirmed Q3: still no native reject/request-changes on the a1 mr surface. Until a1 ships a marking flag (feature request to the a1 CLI), the write path discloses the gate split instead of silently implying participation: the Aone REQUEST_CHANGES note names the posted comments as unflagged and the discussion gate as the only mechanical block, and SKILL.md / the user docs carry the same fact. createMrComment documents the constraint and is named as the seam where a future marking flag wires. * test(review): pin the directional ai_comment gate claim in the Aone disclosure note Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(review): bind the Aone gate-disclosure pins to content, call, and count source --------- Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
07405e669c
|
fix(artifacts): expand recorded directories into per-file artifacts (#9395)
* fix(artifacts): expand recorded directories into per-file artifacts A generated folder was stored as one file artifact, so Open and Download hit file APIs on a directory and returned 400. Record each file instead, and treat office documents as downloadable artifacts. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): address review on directory expansion Report depth truncation, keep later files on their own turn, and stop one bad filename or a full-content hash from breaking directory ingest. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): tighten directory expansion after review Use the locator's realpath workspace root, map unlistable directories to validation errors, skip junk roots, and keep walk caps/warnings aligned with what was actually recorded. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): drop unused recorded counter Directory expansion warnings are always surfaced, so the counter tripped no-unused-vars in CI. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): close remaining directory-expansion review gaps Reject non-object metadata and overlong titles before fan-out, wrap symlink-target lstat as validation, skip junk-only over-deep peeks, and stop offering Download for missing files. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): follow up on the latest directory-expansion suggestions Trim derived child titles, hash expanded files like direct records, only flag over-deep trees that actually contain recordable files, and show missing status on download-only cards. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): reject worktree-root expansion and close remaining review gaps Directory expansion treated a worktree cwd as a normal subdirectory, silently dropped unrecordable names, and could overflow the metadata budget when stamping expandedFromDirectory. Align the walker, tool, and store so those cases fail or disclose cleanly, and keep the pathless download test covering the collapsed 3-card cap. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): close junk-path, coalesce, and depth-disclosure review gaps Reject directory expansion when any path component is skip-listed, including symlink aliases into those trees. Treat unreadable over-depth directories as depth-limited, reject trim-unstable child names, and strip expandedFromDirectory when a curated same-batch record wins coalescing. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): use bracket access for expandedFromDirectory metadata acp-bridge tsc fails under noPropertyAccessFromIndexSignature when the test reads the marker via dot access. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(artifacts): keep worktree directory expansion and close round-7 gaps Strip the bound-root worktree prefix before the skip-directory gate so ordinary subdirectories still expand in worktree sessions. Realpath every directory walk root to catch intermediate symlink aliases into skipped trees, validate parent title/description before fan-out, and make the new fixtures Windows-safe. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ci): refresh desktop bun.lock for Live Host frozen install Qwen Live Host CI runs `bun install --frozen-lockfile` with Bun 1.3.9; the checked-in lockfile drifted and fails on main and any PR that touches Live Host path filters. Regenerate with the CI Bun version so install stays reproducible. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
94a8c40364
|
docs(autofix): pin the publish-side checks to the patch text (#9652)
Review feedback on #9525 arrived after that PR was merged, so the notes it raised never made it into the doc. Both are things an implementer could get wrong from the text as it stands. Say what makes the re-derived checks sound in their new position: they are computed from the patch text on the trusted side and may never re-read a value the execute job wrote — not a line count it reported, not a footprint list it declared, not a base SHA it echoed back. This is the check class that has repeatedly failed to be forgery-proof here, and an implementation that shortcuts to reading the count the gate already computed reintroduces exactly that. Record the sequencing constraint as a constraint: the issue-autofix follow-up has to land before the deletion pass removes machinery it still relies on, which is why that pass is per cluster. Cross-reference #9556, which asks the same question on the review side. Refs #9089, #9525. |
||
|
|
d0d68c0e8b
|
feat(cli): extend non-blocking slash commands to more builtins (#9495)
* feat(cli): extend non-blocking slash commands to more builtins #8130 opted /about, /help, and /settings in to run immediately while a response streams. Apply the same criteria to eleven more builtins so local UI controls no longer wait for the active turn: - UI-preference commands whose saved changes apply through the existing settings hooks: /theme, /editor, /vim, /voice, and /terminal-setup. - Read-only status commands: /tools, /lsp, /tasks, /hooks, /docs, and /bug. Commands that submit model turns, mutate conversation state, or read state the active turn is writing remain serialized, as documented in the non-blocking slash commands design doc. Each opt-in is pinned by a unit test. * docs(cli): scope extended-command claim to the eleven opted-in builtins |
||
|
|
7142a4fd93
|
docs(autofix): design runner-level isolation for PAT-bearing steps (#9525)
PR #9214 tried to close issue #9089 from inside the job by running the verification gate in an ephemeral container. Eleven review rounds took its diff from 329 to 2,700 lines while the per-round Critical count rose 8 -> 9 -> 15 -> 19 -> 29; rounds 10 and 11 changed no code and round 11 still produced 14 new Criticals on byte-identical files, six of them reporting that a site the diff added lacked the discipline the same diff installed elsewhere. That PR is frozen; its findings are inventoried in issue #9524. Two of its results shape this design. The container cannot help, because the gate's root of trust sits outside it: the digest chain is rooted in step outputs that the runner parses last-wins at step end, from a file under attacker-writable RUNNER_TEMP, with no sweep before the staging step. And the wrapper could never have run post-merge at all, which the pre-merge skip path hid from CI. The design moves the boundary from inside a step to between jobs: an execute job that runs branch code and holds no credentials, and a publish job that runs no branch code, re-derives its own decision from the patch instead of trusting the gate verdict, and holds the PAT. Agent process trees are killed by cgroup lineage rather than by an env marker, and the in-step enumeration machinery the split makes redundant is deleted rather than kept. Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
2c64ebe980
|
feat(autofix): audit the approach instead of stopping on growth-budget breach (#9262)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* feat(autofix): audit the approach instead of stopping on growth-budget breach A growth-budget breach no longer escalates to a maintainer handoff that stops the takeover. The breach now makes the round a growth-audit round: the agent audits the PR's approach on two axes — KISS (name a simpler alternative or prove each piece load-bearing) and minimal change (every hunk traces to the problem, an accepted finding, or a failing check) — and records a machine-readable verdict that the verification gate requires. sound re-arms the counting window at the current size and the loop keeps solving; drift simplifies first, then continues; conflict is the only growth path to a human, parked idempotently until a trusted human responds. The old divergence ladder (over budget for N rounds and not shrinking → stop) terminated takeovers whose remaining work could still fit: the growth it punished was protocol-mandated pinned tests (#9213 stalled at round 5 with two small Criticals left). A size signal now triggers a judgment, never a stop. Design: docs/design/autofix-growth-audit.md * fix(autofix): update the artifact-list pin for the growth-audit.json upload entry * fix(autofix): surface conflict verdicts past the failure.md exits and strip verdict forgery channels (#9262) * fix(autofix): harden the growth-audit verdict pipeline and park wake set (#9262) * fix(autofix): close the verdict-pipeline forgeries and loop-generated wake entrances (#9262) * fix(ci): drop the retired divergence rationale records (af-046/af-047) from qwen-autofix.md --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
5715782279
|
feat(review): post --comment reviews to Aone Code via the a1 CLI (#9491)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* feat(review): post --comment reviews to Aone Code via the a1 CLI The Aone chain of /review was read-only: a review of an Aone MR ran fully but `--comment` refused. This lands the Phase 3 submit slice — an authorised run now posts through `a1`: one comment per inline finding, then the summary comment, and `a1 repo mr approve` on an APPROVE. Aone has no native request-changes state, so that verdict posts a blocking summary header and leans on the discussion merge gate; the terminal names the difference. Writes ride a no-retry transport (a transient retry after an accepted write would double-post). The commit_id gate GitHub enforces server-side lives in the provider as a pre-write head-drift refusal, and a mid-batch failure reports exactly what landed with exit-3 do-not-re-run advice instead of a retryable throw. The recorded-but-hostless refusal stays fail-closed, now between two writable platforms. * fix(review): count an accepted-but-unreadable Aone answer as posted The triage review of #9491 flagged the gap: if `a1` ACCEPTED a comment but answered unparseable JSON, the read-back threw before the post was counted — the partial-post report undercounted by exactly that comment, and if it was the first, the do-not-re-run advisory did not fire at all, so a retry would double-post it. Split the read-back semantics: an exec failure still propagates (the write genuinely failed), but a succeeded exec whose answer fails to parse now degrades to "landed, result unreadable" — counted as posted, only the id dropped. The fail-open empty-sourceBranch drift gate is left as the deliberate, tested trade-off it was reviewed as; it is now named in the Phase 3 design-doc note beside the Q4 follow-ups. * docs(review): record the Aone write-path trade-offs in the Phase 3 note Names the two deliberate trade-offs the triage review of #9491 flagged for the Q4 era: the head-drift gate is fail-open on an empty sourceBranch, and the created-comment id read-back is best-effort — plus the tolerant read-back semantics the follow-up fix introduced. * fix(review): close five write-safety holes in the Aone submit path The maintainer review of #9491 found five ways the new Aone write path could post to the WRONG platform or double-post; all five closed: 1. The target-platform-unbound refusal never read the --host flag it names as the remedy, so the --user-authorized re-run refused again forever. An explicit flag on the re-run is platform proof and now lifts the refusal. 2. The write gate compared hosts with raw equality, but Aone is one platform under two names (the CR URL records the web host, the skill's --host rule carries the git host). Hosts now bind through hostsEquivalent, like every other host comparison in remote-match. 3. Platform routing keyed on the family wildcard and the ambient GH_HOST, so a ghe.alibaba-inc.com GHE export selected a1, and a recorded Aone host outranked an explicit --host github.com (the opposite of the registry's documented precedence). Write routing now keys on the canonical Aone pair (isAoneCanonicalHost), never consults the ambient GH_HOST (reads never detect from it), and lets an explicit --host outrank the recorded binding in both directions. 4. a1 takes a comment body as one argv element; Linux caps that at 131072 bytes while compose-review's cap counts characters, so a long bilingual CJK summary died with E2BIG only after every inline had landed. A size gate now refuses the whole batch before any write. 5. An accepted-then-died write (timeout after the POST committed, a reset mid-response) read back as not-landed, suppressing the do-not-re-run advisory and inviting the double-post a1Once exists to prevent. Exec failures now count as possibly-landed (ambiguous), so the advisory fires even when the count is zero. * fix(review): harden the Aone submit path per the verify-lane review The sandboxed-verification review of #9491 (8 Critical, 24 Suggestion) caught the next layer of the write path; every finding addressed: Platform selection: - The unbound refusal now also fires when NO recording exists at all: a --user-authorized publish from another directory finds nothing, and the cwd probe alone must not pick the platform of an irreversible write. Tests that modeled the old cwd-fallback now model evidence: args() seeds a recording, session-scoped recordings are seeded where a session id is set. - The gh write rebinds its routing host to the evidence that selected it (explicitHost ?? recordedHost) — a recorded GHE host no longer posts wherever the ambient env pointed. - Host comparison is normalised ONCE (case/port/trailing-dot) and shared by hostsEquivalent and isAoneCanonicalHost, so the write gate and the router cannot normalise differently; a port-bearing CR-URL host no longer dies at the gate after the whole review ran. - The fast-path repo axis binds case-insensitively (GitHub resolves owner/repo case-insensitively server-side). - The cross-session recording scan is last-writer-wins by mtime; the NEWEST same-PR recording decides (its host, or unbound) instead of an older session's stale host masking the newest recording's hostlessness. - recordedSeverityFloor binds through hostsEquivalent too — the floor recovery no longer silently discards the operator's floor on the web-host/git-host alias. Reporting: - a1Cause reads the captured stderr, not the execFileSync message: the message embeds the full argv — the entire multi-line comment body — so parsing it surfaced the operator's own review text, never a1's error. - The REQUEST_CHANGES terminal note is conditioned on the inline Criticals actually posted — a body-only Critical posts no discussion threads, so nothing mechanically blocks the merge and the note says so. - The summary skip-guard keys on the posted summaryMessage, not the raw body: an empty-body REQUEST_CHANGES still posts its blocking header, the verdict's sole carrier on Aone; the size gate measures the same message. SKILL.md: the mid-batch bullet no longer commands hand-posting the remainder (it contradicted the write-monopoly rule); it names the oversized-comment refusal as the third Aone-specific shape; the relay-the-link fallback stops assembling Aone links from meta's collapsed owner/repo (a nested-group hazard) and relays the target's coordinates. Tests: 24 mutation-driven hardening cells (ordering via invocationCallOrder, exact argv pins, boundary cells at 131071/131072, RC-header accounting, stderr-over-message, summary-create failure, accepted-then-unreadable counts, attribution-off passthrough, url-absence arm, positive read-retry). 3861 review tests green. * test(review): pin hostsEquivalent's alias equivalence across spelling variants * fix(review): close the third-layer holes in the Aone submit path The third review round of #9491 found the layer under the last one; every finding addressed: Platform selection: - The cwd arm of the write gate probes the origin through the CANONICAL predicate itself instead of delegating to the registry's family-wildcard detection: a ghe.alibaba-inc.com origin no longer takes the a1 path. - submit FORCES context-unavailable into the compose input on the Aone path: the cap no longer rides the model-written state, so an omitted contextUnavailable cannot compose an APPROVE that the a1 path turns into a real platform approval. The docs now say the native approve does not fire this phase. - The floor recovery's host axis binds to the host the write routes at (explicit ?? recorded ?? gh fallback): a flagless Aone post no longer drops the operator's recorded severity floor. Failure shapes: - A mid-batch failure emits "partial": true with the landed counts and comment ids: posted:false alone invited a wrapper retry that double-posts what landed. The summary's fate is stated when it was the write that died. - A deliberate pre-write refusal (drift, oversized) reads as aone-post-refused; an UNEXPECTED pre-write error rethrows, gh-parity — nothing landed, a re-run is safe, a recoverable blip no longer loses the authorised review. - The batch re-reads the head once after posting and discloses a mid-batch amend (headMovedDuringPost) instead of claiming the pins held. Text: - The approve-failure WARNING and the oversized refusal name the USER as the manual actor — "by hand" is never an agent action, and Step 7's ban now says so. - SKILL.md: restored the exact subcommand enumeration, qualified the cleanup tripwire as GitHub-only, taught the completion contract the partial/approved shapes, and documented the repeat-round caveats (no dedup backing yet, no self-PR detection on Aone). Tests: cwd canonical-arm cells (GHE family origin falls to gh), the forced-cap wiring, structured partial JSON, refusal-vs-failure reasons, the rethrow cell, missing-sourceBranch key, mid-batch drift disclosure, and the a1JsonOnce transient-no-retry invariant. 3870 review tests green. * fix(review): close the residual holes the sandboxed verify report found The follow-up-2 sandboxed verification of #9491 (155 scripted assertions, 153 pass / 2 fail) re-measured everything at the new head and surfaced three findings; all three closed: - F1R: a failed write exiting with EMPTY stderr (the 120 s deadline kill, SIGKILL/OOM, an a1 crash before writing) made a1Cause fall back to parsing the exec message — and Node embeds the FULL argv in it, so the "cause" quoted a line of the operator's own review body. The fallback now reports exit facts only ("a1 failed without stderr (exit N) / (signal X)"), never the argv-bearing message. The dominant shape (real a1 error on stderr) is untouched. - F3: the forced context-unavailable wiring on the Aone path survived 3870 tests unpinned — submit's compose now has a cell asserting the compose input carries contextUnavailable: true on the Aone path and false on the gh path. - F2: createdCommentId's result/data nestings were correct but unpinned (a key-drop mutation survived). A new cell pins ids read back from {result:{id}} and {data:{id}}. * fix(review): close the round-5 platform-binding holes in the Aone write path - Order the cross-session recorded-args scan by each recording FILE's mtime (writeSkillArgs rewrites in place, so the directory mtime never advances); fold the publishing session's own recording and the sessionless root recording into the same newest-wins ordering instead of pinning them ahead of (or behind) the sorted siblings. - Refuse an explicit --host that contradicts the recorded host (target-platform-conflict): the flag fills a gap in the recorded evidence, it does not retarget the recorded review at another platform's same-named repo. The Aone web/git alias still passes through hostsEquivalent. - Fail closed on a hostless recording read via the --skill-args override: the submission cwd's origin probe names submit's clone, not the review's, and must not stand in for the missing platform evidence; the --host remedy lifts the refusal. - Bind the gh routing host to the cwd origin when the cwd arm selected the platform (and mirror the chain in the floor recovery's host axis), so a cwd-selected post no longer restores ambient env inheritance and routes past the clone that chose the platform. - Hand the GitHub path's contextUnavailable claim through raw so compose-review's deliberate shape check still refuses a malformed non-boolean instead of silently coercing the cap away. - Serialize the partial-post ambiguous flag in the stdout JSON and give the partial shape its own completion-line disposition in the skill — never the `not posted` form a retry-on-not-posted wrapper acts on. |
||
|
|
4839935e55
|
feat: register toggle-only Qwen reasoning (#9574) | ||
|
|
bf0dbb1ae1
|
feat(web-shell): support mid-turn file attachments (#9570)
Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com> |
||
|
|
02d303f849
|
feat(serve): persist prompt terminal ledger for cold-load reconciliation (#9426)
* feat(serve): persist prompt terminal ledger for cold-load reconciliation Turn terminal events (turn_complete / turn_error) were synthesized by the ACP bridge and published over SSE only, so a prompt that was in flight when the daemon died could never be resolved after a restart: the cold load replay emits transcript chunks and carries no terminal evidence, leaving promptId-keyed orchestrators stuck on "unknown". Each session now owns an append-only sidecar ledger next to its transcript. The bridge appends one in_flight record at prompt admission and one terminal record at the single publishPromptTerminal exit (covering the close/kill/channel-crash/daemon-shutdown flushes) through an injected synchronous sink. Ledger writes are best-effort and never block prompt execution or teardown, and records carry only ids, states, and timestamps — no prompt text, user content, or paths. On a cold session load the serve layer reconciles prompts left dangling by a dead daemon: it classifies the transcript tail with the existing turn-interruption detector and appends a completed (stop reason reconstructed_from_transcript) or interrupted (code daemon_lost) verdict, guarded by an attribution check so an unattributable tail stays unknown (fail-closed). The load response gains an optional promptTerminals field with the trailing 64 terminal records, omitted entirely when the ledger holds no terminal evidence, and archive/unarchive move the sidecar alongside the transcript so evidence survives storage lifecycle. Design: docs/design/2026-08-19-prompt-terminal-ledger-design.md * fix(serve): tighten ledger reconciliation fail-closed semantics and complete sidecar lifecycle Address review findings on the prompt terminal ledger: - reconcile: fail closed on multiple dangling prompts (no synthesized terminal for the newest either); attribute the oldest dangling prompt only when the attribution guard skips settled admissions (fixes the [A if, B if, B cancelled] misattribution veto), the transcript's last write postdates the admission (temporal evidence), and a clean verdict is upgraded to interrupted when the model tail holds any functionCall part, id or not (id-less tool-call guard covering the detectTurnInterruption wire-pairing blind spot) - lifecycle: removeSessionFiles deletes the ledger in both archive states; archive/unarchive move it through a single getPromptLedgerPathForState helper with merge semantics when the destination already exists (append-and-unlink instead of a permanent split); move warnings carry full source and destination paths in both directions - scans: DataProcessor.scanChatFiles and usageHistoryService.rebuildFromSessionJsonl exclude .ledger.jsonl sidecars (the ledger is not a transcript) - writer: appendPromptLedgerRecord seals a torn tail before appending so a torn fragment cannot fuse with (and destroy) the next record - tests: pin the new behavior across multi-dangling fail-closed, settled-then-queued attribution, valid interleave migration, temporal veto, id-less tool-call guard, sidecar lifecycle (move/merge/warn-only delete), torn-tail sealing, queued-admission flush on shutdown, active-prompt and resume load contracts, and ledger exclusion from insight scans - docs: sync the design doc's reconciliation algorithm, lifecycle, and fail-closed invariants * perf(serve): read only the ledger tail for load-response promptTerminals readRecentPromptTerminals ran on every POST /session/:id/load (including attached hot loads) and synchronously read and JSON-parsed the entire ledger — a multi-megabyte event-loop stall for long sessions on the per-request hot path. Add a tailBytes option to readPromptLedgerRecords that reads a trailing byte window (the first window line is always dropped: the window start can tear a line in half). The load path now reads a 256 KiB window, which holds hundreds of ~150-byte records against the 64-terminal response cap; sessions whose ledger outgrows the window return a best-effort trailing subset, which the response contract already allows. * fix(serve): close wrong-terminal attribution classes in cold-load reconciliation Strengthen the reconcile attribution evidence per review round 2: measure the temporal evidence on the same api-history projection the verdict uses, fail closed on a compression checkpoint written after the target's admission, and require the visible tail to postdate every other prompt's settled terminal (FIFO evidence). Also fix a TS18048 narrowing gap in the window test, make the seal test assert the raw file layout, and restructure the window test so the call-site tailBytes wiring is actually observable. * fix(serve): keep ChatRecord import inline so lint-staged cannot merge it into a type-only import * fix(serve): fail-closed reconciliation on millisecond clock equality and deadline-overlapped turns * fix(serve): TOCTOU fence before ledger append and documented residual attribution risk * test(serve): pin the ledger race fixture on the transcript timeline * feat(serve): bind cold-load evidence to the admission via a dispatch marker * test(core): pin the ledger sidecar exclusion in usage rebuild * fix(serve): create ledger sidecar owner-only and fence marker-era compression by position Round-7 review Criticals: - appendPromptLedgerRecord created the sidecar with umask-default permissions (0o644) while the adjacent transcript is owner-only; the ledger now follows the 0o600 convention at creation time. - Marker-bearing admissions fence post-admission compression by marker position instead of wall clock, so a backward clock step cannot hide a compression reset that voids the evidence chain. - Design doc: the residual-risk claim is corrected — the dispatch marker binds ordering, not ownership; the two ownership classes that survive it (recordless predecessor with continued writes, ledger-less cross-client writer) are documented, pending writer identity on transcript records (#9483). --------- Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com> |
||
|
|
48b30647d0
|
refactor: centralize cross-package contracts (#9497)
* refactor: centralize cross-package contracts * fix(build): harden cross-package contract checks * docs(core): clarify sub-session prompt limit scope |
||
|
|
a41d5ec058
|
feat(web-shell): unify file uploads and references (#9477)
* feat(web-shell): unify file upload and reference flow * fix(web-shell): address attachment review feedback * fix(web-shell): address attachment review feedback * fix(web-shell): address attachment review feedback * fix(webui): restore optimistic text prompts --------- Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com> |
||
|
|
a659539bc7
|
feat(cli): Add standalone conversation isolation primitives (#9341)
* docs: finalize standalone PR2 core design Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * feat(cli): Add standalone conversation isolation primitives Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(serve): block mixed-case standalone restore Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(serve): fail closed on corrupt session metadata Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): repair PR2A CI doubles and identity replacement cases The ubuntu Test job failed on ten PR2A cases that pass on macOS: - Export SessionIdCaseConflictError from the worktree test's core mock and give its SessionService double findSessionIdIgnoringCase, since loadSession now resolves persisted spelling before reading metadata. - Add readCreationMetadataIfReadable to the Live task fake and mirror it onto the three server lifecycle spies so the fail-closed store interface sees the same fixture metadata as the legacy tolerant readCreationMetadata path it replaced. - Pin the original inode via rename in the two same-path replacement cases. ext4/overlayfs recycle a freed inode immediately, so rm+mkdir at the same path could satisfy the recorded device+inode identity on Linux runners and make a real replacement look valid. * fix(serve): key restore shared guard on persisted session id spelling The restore handlers resolved the persisted (possibly uppercase) spelling of a session id only inside the shared coordinator guard, while batch delete locks its exclusive guard on the raw caller ids. A restore of the normalized request id therefore raced a concurrent batch delete of the persisted-spelled id on case-sensitive volumes. Resolve the persisted spelling before acquiring the shared guard and key runSharedMany on the resolved id so both sides contend on the same key, in both the REST and ACP restore handlers. Regression tests assert the guard key at both transports. * fix(serve): unify persisted-session conflict contract across restore surfaces - SessionIdCaseConflictError now carries an optional candidateSessionId with a shape-aware message, so a same-spelling active+archived conflict names the persisted spelling instead of blaming the request-case id. - REST/ACP-HTTP conversion re-checks the candidate spelling before mapping to SessionConflictError; ACP child surfaces both shapes as INTERNAL_ERROR + errorKind 'session_conflict', and the reserved-source rejection carries errorKind 'reserved_session_source'. - Pin the previously ungated guards from review: lineage validity conjuncts and archived-state reads (session-source), persisted-spelling adoption in ACP load/resume, ensureStandaloneDirectory EEXIST raced re-inspection, and the trailing root revalidation inside inspectConversationDirectoryIdentity (fs-interception seam). * docs: sync standalone PR2 plan and design docs with R1 review - Plan doc: ship the readCreationMetadataIfReadable signature in the store snippet, record jsonl-utils/error-response/readCreationMetadata in the PR2A checklist with the second-core-file re-audit outcome, name the launcher as child-UUID generator in both sections, extend the PR2A vitest and prettier gates to every PR-touched file, and stop claiming junction/Windows coverage the matrix cannot run. - Design doc: reconcile the initial-prompt ordering clause with the strict create schema (prompt admitted by createWithInitialPrompt after the create transaction commits), replace the stale "before the UUID can be released" wording with the terminal-reservation model, and enumerate all seven deny categories in the acceptance matrix. * fix(cli): normalize session-map lookups in restore failure cleanup guards The sessions map is keyed by normalizeSessionIdForLookup-folded ids, but the three cleanupAfterRequestFailure guards read it with the raw config.getSessionId(). After a restore adopts a non-canonical persisted spelling (uppercase legacy transcript), those reads always miss and the guard would treat a still-stored session as unstored, shutting its config down in the double-cleanup-failure window. Normalize at the read sites; no behavior change for canonical or non-UUID ids. The adoption test now also pins that caller-case follow-up operations (cancel) still reach the adopted session through the normalized key. * fix(cli): snapshot standalone dir entries after final identity inspect prepareStandaloneDirectory read the child entries before the trailing identity re-inspection, so a same-uid entry appearing between the two steps would not flip the not_empty verdict. Read entries after the final inspect so the emptiness check runs on the freshest snapshot the identity machinery can guarantee. Addresses yiliang114's review on PR #9341. * fix(cli): lock restore guard on both request and persisted session id spellings Restore keyed its shared SessionArchiveCoordinator guard only on the resolved persisted spelling while batch delete/archive/unarchive lock on raw caller ids, so on a case-insensitive filesystem a delete carrying the request-case spelling never collided and could unlink the transcript mid-restore (R2-1). Lock both spellings on the REST and ACP-HTTP restore surfaces. Also from R2 review: - Pin the pre-guard/in-guard conflict-conversion stages in the both-states restore tests (R2-8: call-count + guard-not-entered assertions; mutation witness supplied by the reviewer). - Add the toRpcError SessionIdCaseConflictError producer case to dispatch-error.test.ts and list the suite in the PR2A verification block; extend the PR2B block with the collocated suites its checklist modifies (R2-5). - Correct the plan checklist label for the pre-existing shared jsonl-utils module from Create to Modify (R2-7). * fix(cli): canonicalize session-archive coordinator lock keys The two-spelling restore guard from the previous commit closed only the enumerated spellings: any third case variant of a caller id took an exclusive key that collided with no held guard, and on a case-insensitive filesystem could unlink the transcript mid-restore — including the common case where request and persisted spellings coincide and the lock set collapses to one key (R3 review, probe- verified on a case-insensitive mount). Canonicalize lock keys with normalizeSessionIdForLookup at the coordinator boundary (runSharedMany / runExclusiveMany / assertNotTransitioning) so every case variant of a session id contends on one key, and revert the restore-side spelling enumeration it makes redundant. Add a coordinator-level regression test for the case-fold collision, and fix a misleading comment above the in-guard call-count assertion (R3-1). * test(cli): pin workspace ordering/race propagation and align PR2 plan From the R4 review: - Pin that prepareStandaloneDirectory reads entries after the final identity re-inspection, via an interposed inspect that plants an entry mid-sequence (R4-3; mutant-verified to flip). - Pin that ensureStandaloneDirectory propagates a raced 'compromised' inspection verbatim instead of collapsing it to identity_changed (R4-7; mutant-verified to flip). - Plan: daemon bridge live-entry lookups (including getSessionEventEpoch) use the canonical ID — acp-bridge byId.get is exact-match with no id normalization — while the storage spelling is confined to SessionService filename/directory-hash/ACP-child storage operations (R4-1). - Plan: declare the session-archive coordinator lock-key canonicalization in the PR2A inventory and run session-archive.test.ts in the PR2A block (R4-2); add dispatch-error.test.ts to the PR2B block (R4-5). * fix(core): make case-insensitive resolver conflict decisions content-based The resolver threw SessionIdCaseConflictError on filename enumeration alone, before any content validation, and silently dropped a single candidate whose head recovered no records. Two probe-verified failure modes from the R5 review: - A present-but-unreadable case-variant transcript (torn/empty/ foreign-project head) resolved to undefined, so create admission admitted the canonical spelling and materialized a case-only twin; every later resolve then threw on the duplicate, permanently locking out the just-created session (R5-1). - A valid session with an unreadable same-spelling twin in the other state directory threw on enumeration while getSessionLocation cleanly reported one readable copy — listed as loadable, but every restore 409'd (R5-2). Conflict arms now consult getSessionLocation: exactly one readable spelling wins; conflict is thrown when two or more are genuinely readable (or a single candidate is conflicted across states); a candidate whose head fails validation still occupies the id when its file is on disk, while one that raced away mid-resolution resolves to undefined. Admission already maps the thrown conflict to persisted-true, so no admission change is needed. * fix(cli): make caller-supplied sessionId create admission case-aware The argv['sessionId'] branch (reached by raw stdio ACP session/new with a requestedSessionId, without any daemon reserveCreate) checked occupancy with exact-spelled sessionExistsInAnyState only, so a legacy mixed-case transcript did not block the create and the daemon persisted a case-only twin — which the resolver's conflict semantics then make permanently unrestorable on every surface (R5-2). Route the check through the case-insensitive resolver, treating its conflict throw as occupancy. Also pin that the ACP restore path hands the resolver-adopted storage spelling to assertSessionLoadable: archived uppercase transcript restored via the canonical lowercase id must surface errorKind 'session_archived' (R5-3; the request-spelling mutant skips the error on case-sensitive filesystems). * fix(cli): narrow reserved-source restore gate to internal runtimes Two items from the maintainer's round-2 live verification: - N1: the restore-side reserved-standalone-source gate fired for every runtime, so a transcript persisted on main with the client-supplied sourceType "standalone" became permanently unloadable while still listed. The create side already blocks new reserved-source transcripts, so any such file on an ordinary store predates the gate — keep it loadable there and hide only on the internal Conversations runtime (REST) / isolated ACP surface, where genuine standalone sessions will live. Generic-arm tests on both surfaces flipped to pin the compat restore; the internal-arm 404 pin is unchanged. - N2: the R5 occupancy throw reused the both-states message for a single unreadable transcript. SessionIdCaseConflictError gains a 'unreadable_transcript' reason with a truthful message, used by both occupancy arms; the case_conflict shape is unchanged. * test(cli): flip the remaining generic-surface reserved-source test The exact-spelling variant was missed in the N1 narrowing commit and failed CI on ubuntu (session/load + session/resume expected the old generic-surface hide). Flip it to pin the compat restore like its mixed-case sibling. * docs: sync PR2A plan with the shipped create-admission resolver consumer The round-6 triage deferred note flagged the plan as desynced: the R5-2 fix made loadCliConfig's caller-supplied sessionId branch a sixth findSessionIdIgnoringCase consumer. Declare it in the per-file checklist, count it in the consumer inventory, and add config.test.ts to the PR2A vitest block. * fix(core): narrow the session id case resolver's occupancy arms Three shapes were classified as permanent occupancy, regressing paths that loaded or created fine before the resolver replaced the exact-existence check: - Candidates were enumerated by case-insensitive filename match without the pattern gate that getSessionLocation applies, so an agent-suffixed id (which the CLI admits and writes under the raw session id) resolved to an unreadable-head conflict. Skip names the classifier would reject. - On a case-insensitive filesystem every spelling opens the same physical transcript, so a readable copy plus a torn case twin reported two readable candidates and raised a conflict for a session with one loadable copy. Collapse spellings that share a device/inode and resolve to the one whose own directory entry backs the file. - An unreadable head under the requested spelling itself is a case-only twin of nothing, yet it refused the id with no listing entry to delete or unarchive. Report it absent, matching getSessionLocation, so a first run that crashed before its first record can reuse its own 0-byte transcript. The twin-minting protection still applies when the persisted spelling differs from the requested one, and genuinely distinct readable spellings still conflict. Also pin the explicit-standalone child branch's sourceId guard and its documented lineage behaviour, which no case covered. * fix(cli): key the private conversation directory on the canonical id Restore derived the directory hash from the persisted spelling while the seven other materialize/discard call sites derived it from the lowercased live id, so restoring a legacy mixed-case transcript produced one directory and every later Live or task call produced a second, empty one — either orphaning what the first held or failing the call because the session sat outside its isolated directory. Rollback then inspected the other hash and leaked the first directory permanently. The directory belongs to the live entry, which the bridge registers under the canonical id alongside the lifecycle locks and in-flight maps, so both restore paths now derive it from that id too. This also keeps directories that pre-date the change reachable: restore used the lowercased request id before, so every one already on disk is canonical-keyed. Storage-facing operations — transcript filenames, metadata reads and the ACP child's own session storage — keep the authoritative spelling. The design doc and PR2 plan are corrected to scope the spelling rule accordingly. * test(cli): follow the canonical private-directory key in the Live restore case The internal-restore case pinned the directory hash to the persisted spelling, which the canonical-key change inverted. It now asserts the canonical id for the directory and bridge cwd, and keeps the original intent explicit by asserting that the creation-metadata read still uses the persisted spelling. * fix(cli): report proven parent lineage from the loadable-session reader The exported reader returned one verdict for two different situations: a child whose parent lineage it had verified, and an explicit standalone child whose parent it never read. PR2B is being built on that reader, so the ambiguity mattered even though no caller was affected yet. The verdict now carries the parent's own classification. An explicit standalone child whose parent is still readable must have a standalone top-level parent, which also rejects a grandchild or a lineage cycle because neither parent classifies as top-level. A parent that has been archived away or deleted keeps the child loadable — it is self-describing, and its own transcript is the evidence that a valid parent existed when it was created — but `parentSource` is then absent, so a caller that needs proven lineage rejects on that rather than guessing from `kind`. The compatibility adapter reads the new field instead of re-reading the store to re-derive the same classification, so its behaviour is unchanged while a duplicated location lookup and metadata read disappear from every legacy standalone child restore. Adapter output is identical for every input: explicit standalone was already filtered out before the parent check, so no current caller changes behaviour. * fix(core): stop the alias resolver from turning I/O and missing inodes into conflicts Two defects in the case-variant collapse added earlier in this branch. The stat guard was statically dead: `statSync` without `{ bigint: true }` always returns numbers, so the `typeof` test could never fire. The hazard it was meant to cover is a filesystem that exposes no inodes — FAT/exFAT and some SMB mounts report `ino === 0` for every file — where `dev:ino` collapses genuinely distinct transcripts onto one identity and a real two-transcript conflict silently resolves to one spelling. That is a fail-open on a correctness decision, so it now uses the existing `hasVerifiableInode()` helper, whose docblock describes exactly this case. The resolver also swallowed every `statSync` failure into `undefined`, which its caller reads as positive proof of a conflict. A transient EACCES or EMFILE therefore surfaced as `409 session_conflict` — a permanent-looking answer for a blip that succeeds on retry. Only ENOENT is now treated as meaningful: a transcript that raced away is no longer a competing spelling. Every other error propagates. * fix(core): let a crashed first run resume its transcript past a case twin The self-escape added for an unreadable transcript under the requested spelling only covered the single-candidate arm. Once any case twin was enumerated, resolution took the all-unreadable arm instead, where presence was computed over every candidate including the requested spelling's own file — so the documented crash recovery vanished the moment a stale twin existed, and neither file could be deleted because both classify as nonexistent. Reusing an id whose file is already on disk mints no case-only twin, so that arm now takes the same escape. A twin under a different spelling still occupies the id, because minting the requested spelling beside it is what would make both unrestorable. The disappearance test was vacuous: its candidate spelling equalled the request, so it returned at the self-escape and never reached the race loop it named — deleting that loop left it green. It now uses a differing spelling with `existsSync` false, and forcing the loop to throw unconditionally kills it. The all-unreadable rejection test likewise needed two spellings that are both distinct from the request to exercise twin-minting protection. * fix(cli): fail closed when a filesystem cannot prove directory identity The conversation-directory checks compared `dev`/`ino` directly, so on a filesystem that exposes no inodes — FAT/exFAT and some SMB mounts, where Node reports `ino === 0` for every entry — every directory compared equal. The root pin, the two anti-swap re-probes around `realpath`, and the expected identity check would all confirm a directory that had in fact been replaced, which is the swap those probes exist to catch. They now require a verifiable inode on both sides before treating a match as proof, reusing the `hasVerifiableInode()` helper already written for this in core and exporting it from the package surface. An unverifiable inode reads as a changed identity rather than as a match. The regression test pins a root whose inode is also 0, so a plain `===` comparison still matches and only the verifiability guard can fail it. * fix(cli): keep caller-supplied session-id admission fail-closed on I/O errors Swapping the existence check for the case-insensitive resolver narrowed the catch to `SessionIdCaseConflictError` and rethrew everything else, but the resolver deliberately propagates non-ENOENT `readdir` and transcript-read failures. An unreadable chats directory therefore killed startup with a raw EACCES or ENOTDIR instead of the guarded message, and bypassed the `throwOnSessionIdConflict` contract the ACP path depends on. The previous check answered "occupied" for any read failure. Restoring that keeps an unprovable id on the guarded path; distinguishing "cannot determine" from "occupied" would be a new response shape and is left alone here. * fix(cli): keep the directory identity module out of the core package barrel Importing `hasVerifiableInode()` from the core package barrel pulled core's whole module graph into the serve pre-listen bundle closure, so `check:serve-fast-path-bundle` reported glob, chokidar, fzf, @iarna/toml and the core shell tool runtime as statically reachable from `run-qwen-serve`. This module is deliberately dependency-free for that reason. The predicate is restated locally with a comment recording why it is not imported, since core has no subpath export for it. The barrel export added for that import is reverted so the package surface is unchanged. * fix: correct three defects introduced by the previous review round **The inode guard made a directory fail to equal itself.** `createConversationRootIdentity()` compares `before`/`after` of the same path, so requiring a verifiable inode threw `identity_changed` on the very first root establishment and, because the workspace clears its cached root on failure, Conversations never started on exFAT/FAT or an inode-less SMB mount. "Cannot prove unchanged" is not "changed": the root is now established with `inodeVerifiable: false` recorded, comparisons fall back to device, canonical path and stat shape, and the weaker guarantee is explicit on the identity for callers to surface. Where inodes exist they are still required to match. **The occupancy escape was placed to discard a real twin.** It returned early for the whole arm whenever the requested spelling was enumerated, so a present-but-unreadable twin stopped occupying the id — the case-only twin the surrounding comment exists to prevent. The escape belongs per candidate, not per arm: the requested spelling's own file never counts as occupancy, every other spelling still does. **The private directory was still a caller obligation.** The comment claimed the bridge registers live entries canonically and every materialize derives from that id, but the bridge echoes whatever the caller passed, and `LiveTaskService.ensureResident()` passes an id that originates in a tool argument. `ConversationWorkspace` now canonicalizes before hashing, so one session resolves to one directory by construction. Also unifies the resolver's two arms, which were the same algorithm written twice — that duplication is why the escape landed in only one copy. * fix(cli): Collapse case-variant session ids in batch lifecycle and CLI create Batch delete/archive/unarchive locked on canonical keys but still deduped raw spellings, so two case variants of one id deadlocked the batch. CLI --session-id now stores the lowercase spelling so new mixed-case transcripts stop accumulating. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
5003ab3c7f
|
feat(web-shell): add transcript contract prevalidation (#9388)
* test(web-shell): add transcript contract prevalidation Freeze reproducible evidence for current transcript paths before any VS Code or HTML export production migration. - Add versioned fixtures, closed export schema, and capability gates - Probe direct-daemon and ACP identity under partial history prepend - Preserve raw adapter semantics and full write_file Turn Output diffs - Document the two-MR architecture, security constraints, and blockers * fix(web-shell): harden transcript prevalidation gates Make the evidence-only contract suite enforce the review assumptions it documents while preserving the existing runtime transcript behavior. - Run the contract suite in the required no-AK integration job - Fail closed on ambiguous identity probes and deduplicate gate kinds - Enforce manifest, hash, export safety, and renderer version boundaries - Cover visible transcript text and stable Desktop packaging semantics - Record the complete PR comment evaluation and verification outcome * fix(web-shell): close transcript prevalidation gaps * fix(web-shell): remove brittle Desktop wiring probe Keep transcript contract prevalidation at the evidence level it can actually prove. The previous source-text assertion could both reject equivalent formatting and pass unreachable packaging code. - Remove the Desktop script parser and its false behavioral claim - Mark installed-artifact verification as deferred to Desktop smoke tests - Clarify MR1 matrix, CI wiring, and provenance evidence boundaries - Refresh the hash-locked capability matrix fixture Note: This does not change Web Shell or Desktop production behavior. --------- Co-authored-by: heyang.why <heyang.why@alibaba-inc.com> |
||
|
|
d96f264de7
|
feat(telemetry): link daemon HTTP request spans to inbound W3C traceparent (#9391)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* feat(telemetry): link daemon HTTP request spans to inbound W3C traceparent The daemon HTTP surface records a request span per request, but every span starts a new trace: a caller forwarding the standard W3C traceparent header (OTel-instrumented clients, proxies, gateways) gets no linkage back to its own trace. Extract traceparent/tracestate from inbound request headers in the daemon telemetry middleware and parent the request span to that remote context. Extraction reuses the same path as the existing JSON-RPC _meta extraction (global propagator first, strict manual fallback so behavior is identical without a registered SDK) and fails closed: requests without a valid header keep the exact current span shape. * fix(telemetry): guard inbound traceparent sampling and align W3C fallback - Force TraceFlags.SAMPLED on inbound HTTP parents via the existing shouldForceSampled() matrix: an unsampled remote parent under the default parentbased_always_on sampler silently dropped the request span, the whole next() subtree, and the session-subprocess spans forwarded via _meta (review C1). - Replace the hand-rolled manual fallback parser with a direct W3CTraceContextPropagator instance so acceptance rules (future versions, tracestate, all-zero ids, version-00 extension field) match the registered path with or without an initialized SDK. - Gate middleware extraction behind isTelemetrySdkInitialized() to skip the hot-path parse when telemetry is off, and emit a debug daemon log when a present-but-invalid traceparent header is rejected. - Re-export DaemonRequestSpanOptions from the core barrel and add a type-level guard so the parentContext field cannot silently disappear (vitest alone cannot catch its removal). * chore(vscode): regenerate companion NOTICES.txt for @opentelemetry/core * fix(telemetry): lazy-load OTel core fallback propagator behind SDK init Address review feedback on the inbound traceparent linkage: - Keep @opentelemetry/core out of the static graph. The module-level W3CTraceContextPropagator in daemon-tracing.ts pulled the CJS barrel (bot-measured +65,046 bytes) into every closure loading that module, including telemetry-off deployments. daemon-tracing.ts now keeps only a holder + setter (setDaemonFallbackPropagator, typed against @opentelemetry/api — type imports stay free at runtime); the lazy sdk-impl.ts chunk, whose closure already contains @opentelemetry/core via sdk-node/resources, constructs and injects the W3C instance on the successful SDK assembly path. Until injection, extraction returns no parent context: the HTTP edge is already gated on isTelemetrySdkInitialized (nothing changes when telemetry is off), and the _meta edge's consumers (withDaemonSpan / withInteractionSpan) short-circuit on the same flag, so an unresolved pre-init parent never had an observable effect. - Add the mutation-verified fail-closed test for the header-extraction try/catch in daemonTelemetryMiddleware: a throwing extractor leaves the request settling normally (recordDaemonHttpRequest still fires once) with no parentContext on the span options. - Record the rejected traceparent value (truncated to 128 chars) as http.request.header.traceparent on the invalid-header breadcrumb — traceparent only carries trace-id/span-id/flags, so this is privacy-safe and makes broken cross-service joins diagnosable. Also document why the _meta extraction path deliberately skips shouldForceSampled (trusted in-process bridge vs external HTTP input). * feat(telemetry): carry inbound trace id into daemon access log with telemetry off Telemetry off (the default) left daemon logs without any trace id: with no request span, the log trace prefix never fires, so a caller forwarding W3C traceparent could not be joined to its daemon log lines. The middleware now parses the header with a plain regex (extractInboundTraceId — same shape/all-zero/ff rejections as the W3C propagator, no OTel machinery) and stores the trace id on the per-response telemetry context. The access log emits it as the camelCase traceId field of "request completed", keeping the log-based join alive with no telemetry config and no trace backend. With telemetry on nothing changes: the request span already carries the caller's trace id into the log prefix. * fix(telemetry): unify _meta/HTTP sampling and repair build export - Export extractInboundTraceId from the core barrel: the previous commit exported it from daemon-tracing.ts only, so downstream package builds failed with TS2305. - extractDaemonTraceContext now applies the same shouldForceSampled() matrix as the HTTP edge: the _meta path is also reachable from direct ACP clients (acpAgent newSession/loadSession/unstable_resumeSession and Session.prompt pass caller-controlled _meta), so an external sampled=0 parent no longer silences daemon spans there either. The in-process bridge is unaffected (its injected values are already SAMPLED). - The rejected-header breadcrumb now goes through sanitizeLogText so a crafted traceparent cannot forge log line structure with control characters. - Add the sdk-impl wiring test: after initializeTelemetry the injected W3C fallback propagator resolves inbound HTTP parents. * fix(telemetry): align log-path traceparent parsing and emit traceId in both modes - extractInboundTraceId now mirrors the vendored W3C propagator's acceptance exactly: single optional leading/trailing whitespace and trailing extension fields above version 00 (version 00 must stay four fields). Previously the strict four-field anchor made the two paths disagree on the same forward-compatible header, silently dropping the access-log traceId for exactly the callers the propagator path supports. - The camelCase traceId access-log field is now captured whenever a valid header parses, regardless of telemetry mode, so one saved log query / alert shape works for every deployment; with telemetry on the snake_case span prefix carries the same id redundantly. * fix(telemetry): move inbound trace id getter out of the middleware module 52d572c0f2 made the access log statically import the telemetry middleware module to read the captured inbound trace id. The access log sits inside the serve fast-path pre-listen closure (run-qwen-serve imports it directly), so the middleware's core-barrel import graph came along for the ride and check-serve-fast-path-bundle started failing: the 5.6MB core chunk (shell tool, glob, chokidar, @iarna/toml, fzf) became statically reachable from run-qwen-serve. Move the response-context symbol, its type, and the getDaemonTelemetryInboundTraceId getter into a new import-light telemetry-context.ts; the middleware imports the symbol from there and re-exports the getter, so the access log no longer links against the telemetry module at all. * fix(telemetry): capture inbound trace id pre-auth under a dedicated symbol * test(telemetry): pin the trace id seam through the context module getter --------- Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com> |
||
|
|
e4f5504e9f
|
feat(extensions): support authenticated HTTPS Git installs (#9458)
* feat(extensions): support authenticated HTTPS Git installs * test(serve): update capability integration baseline |