mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-10-19 12:43:39 +00:00
33 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
008c0de746
|
docs(sdk): clarify empty core-tool allowlists across SDKs (#13949)
Co-authored-by: Zhangchenhao-111 <285911435+Zhangchenhao-111@users.noreply.github.com> |
||
|
|
81582ca19d
|
fix(core): align Code Mode text with lazy tool discovery (#12990)
* fix(core): align Code Mode text with lazy tool discovery Code Mode now discovers deferred schemas through top-level tool_search, but several texts still described the old surface: the system prompt's calling convention named tools by registered name instead of JavaScript name, and the SDK docs, the bridge design doc and a few code comments said Code Mode hides both bridge tools and inlines every deferred schema. Refs #12973 * docs(core): scope the subagent bridge note to direct mode The incomplete-bridge fallback reveals ordinary deferred tools, not every summarized one, and Code Mode subagents do get tool_search beside exec. * docs(core): drop the eager-demoted skill gap closed by #12898 prepareTools() no longer filters eager-demoted tools out of the Code Mode allowlist, so a subagent whose tools.eager list omits skill can still load one through exec. The docblock described that gap as open. Refs #12809 |
||
|
|
e39567eb56
|
fix(cli): honor --bare and --safe-mode in the cross-session messaging gate (#9845)
* feat(core): accept cross-session messages behind an inbound gate Step two of QwenLM/qwen-code#8724, rebuilt on current main now that the registry from step one has landed. A session can be reached by another session on the same machine, and every message that arrives is gated before the model can act on it. Off by default behind `agents.crossSessionMessaging`. Transport is one UNIX domain socket per session, NDJSON over the wire, one frame per line. The socket directory is 0700 and the socket 0600, and that is the whole access-control story: Node cannot read SO_PEERCRED without a native addon, so a frame's claimed `from` is not authenticated. Everything downstream assumes that. The gate is why the transport and the policy land together. With an explicit `agents.crossSessionInbound` the user decides; unset, the policy follows approval-mode parity — a message auto-delivers only when acting on it cannot do more than the sender could already have done itself. Anything unreadable holds. Held messages are settled rather than stranded: the buffer is bounded, shutdown expires the rest, and every terminal outcome goes back to the sender as a control frame. Content reaches the model inside a <cross_session_message> envelope with the delimiter defanged in the body, so a peer cannot close the envelope early and forge one attributed to the user. The envelope carries a fixed notice that a peer holds none of the user's authority, and the auto-mode classifier gains the matching rule. `/peers` lists and releases held messages; without it, holding would be indistinguishable from dropping. Landed only after four independent reviews, whose non-obvious findings are worth naming because they were all live defects: - A receiver in AUTO_EDIT auto-accepted peer messages and nothing reviewed what they caused. The old rationale — "every consequential action still faces its own gate" — is true of AUTO but false of AUTO_EDIT, where edit confirmations are approved outright and the classifier does not run. A peer could get a file written with no prompt, no classifier and no user. Receiver policy now turns on whether the mode reviews actions at all, not on whether it is YOLO. - `server.unref()` does not cover accepted connections, so any peer that connected and lingered pinned the session open forever. - `fs.mkdir(recursive)` and `fs.chmod` both follow a symlink, so another user could pre-create the world-writable `/tmp` fallback directory and redirect our 0700 chmod onto a directory of ours. - A full listen backlog surfaces as EAGAIN on Linux, not EBUSY; the busy case was being read as dead on the primary platform. - The envelope was escapable without markup: `escapeAttribute` handled `&<>"` but not newlines, so a crafted `fromName` could emit free-standing lines inside the opening tag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(i18n): translate the /peers description in strict-parity locales `Test (ubuntu-latest, Node 22.x)` has failed every run on this branch with FAIL src/i18n/mustTranslateKeys.test.ts > does not fall back to English for any built-in command description in strict-parity locale { code: 'zh-TW' | 'zh' } AssertionError: expected [ 'peers' ] to deeply equal [] `peersCommand` set `description` to a literal English string rather than routing it through `t()`, so both strict-parity Chinese locales fell back to English and the parity test — which exists to catch exactly this — reported the `peers` path. Four merges of current main were attempted against this check. None could fix it: the untranslated description is introduced by this branch, so no state of main contains the missing keys. Use the `get description() { return t(...) }` form every other built-in command uses, and add the key to `en.js`, `zh.js` and `zh-TW.js`. All three locale files remain in sync at 1817 keys. Verified both directions in a clean worktree install: with the change, `mustTranslateKeys.test.ts` is 20/20; stashing it reproduces the CI failure exactly, `expected [ 'peers' ] to deeply equal []`, twice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VgTjRF91xANQh6SY9YGyCf * fix(ipc): close Critical review findings in the cross-session inbox - Dedup held msgIds case-insensitively, matching /peers resolution - Print unique dash-stripped handles and resolve them in /peers - Bound outbound sends: in-flight cap plus an absolute deadline (socket.setTimeout is an idle timer a dribbling peer resets) - Defang whitespace-split and quote-glued envelope delimiter tokens - Seed onHeldChange subscribers with the current held snapshot - Skip POSIX-only socket-path tests on win32 like the sibling suites * fix(cli): gate held-message announcements on the held set growing Once the hold buffer is full, every further peer frame evicts the oldest entry while arriving under a fresh id; announcing each of those appends a history item and re-renders per frame — reintroducing one layer up the unbounded growth the hold buffer's ceiling exists to prevent. * fix(ipc): close round-6 Critical findings in the cross-session inbox * fix(ipc): close round-7 Critical findings in the cross-session inbox - defang envelope delimiters split by render-invisible separators the \s class misses, sharing flattenPeerLabel's strip class so the two cannot drift (R5-1) - restore admission-failed queue submissions deferred until idle, the same recovery the /btw path uses, so a restored peer envelope cannot leak raw into the racing turn's mid-turn steer drain (R7-1) - cap the accepted-message backlog symmetric to the held cap: the pre-wiring buffer and the post-wiring queue refuse once full, with an honest 'expired' receipt instead of unbounded socket-speed growth (R7-11) * fix(ipc): close round-8 Critical findings in the cross-session inbox - Refuse any msgId canonicalizing to `all` at the wire boundary and fold the /peers bulk keyword case, so a peer-chosen id can never alias the bulk action or trap a per-message decision (R8-1, R1-11, R1-27). - Re-hold approved messages whose delivery fails instead of silently dropping them: decide() returns 'failed' and parks the entry back at its position, reevaluate() re-holds failed releases, and /peers reports the failure honestly (R8-2, R1-18). - Drain accepted peer envelopes on a preprocessing-free path: the queue marks them peer, the drain submits them with the Teammate send type (which returns before slash/shell/@ handling), renders the one-line projection, and restores failed admissions as peer entries (R8-3). - Close the envelope-defang class structurally: escape every `<` in peer content so no forged delimiter survives regardless of wedged invisibles or homoglyph spellings; the open-enumeration regex is deleted (R5-1). Every guard is witnessed by new focused tests and verified with mutation probes (each probe fails its witnesses when removed, passes restored). * fix(ipc): close peer messaging lifecycle races * feat(core): address local peer sessions from agent tools * fix(core): make peer addresses unambiguous * fix(core): reserve peer routing namespace * fix(ipc): complete peer delivery feedback * fix(ipc): harden peer session routing * fix(cli): honor effective peer messaging gate * fix(cli): report effective peer messaging state * test(cli): teach the cross-session test doubles the new gate accessor The interactive UI gates the peer inbox on `config.isCrossSessionMessagingEnabled()` instead of the merged `agents.crossSessionMessaging` setting, so every config double that exercises `startInteractiveUI` must expose that accessor. The shared `sessionRegistryConfigStub` in `llm.test.tsx` builds most of those doubles, and the four `startInteractiveUI.test.tsx` cases that assert the inbox binds need the enabled config rather than the default. Fixes the 37 failing cli tests (`Tests 37 failed | 28548 passed`) in the `Test (ubuntu-latest, Node 22.x)` lane; both files now pass 101/101. * refactor(core): drop peer-receipts, superseded by peer-send on main The send-side receipt correlation this module implemented shipped on main as packages/core/src/ipc/peer-send.ts (#10158, step 3 of #8724), in a strictly richer form: it canonicalizes message ids, models seven delivery statuses with explicit legal-transition sets, bounds tracked sends at MAX_TRACKED_SENDS, and reports the settled receipt with its age. This copy had no production import site -- only its own test imported it -- so keeping it could only let it drift from the shipped version. Removing both files leaves the branch as just the cross-session messaging gate change it now describes. No behavior change. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): gate every peer-inbox reader on --bare and --safe-mode The only reader was isCrossSessionMessagingEnabled(settings.merged), which cannot see the two session-level suppressions. A session started with --bare or --safe-mode bound a UDS inbox and published its ipcPath into the live-session registry, advertising itself as addressable from the modes that exist to close cross-process surfaces. On the daemon path it was worse than visible. sendToPeer reports `disabled` only when the caller's own record lacks an ipcPath, so publishing one is also what let a suppressed hosted session send -- while inbound was already refused. An interactive TUI under the same flags bound nothing and sent nothing; a hosted session did both. Answer the question once, in the file whose doc comment already claims to be the one place it is answered: crossSessionMessagingSuppression(config) reads the two accessors Config already carries, and isCrossSessionMessagingActive(settings, config) narrows the setting with it. All four readers now ask that -- the interactive bind, the interactive failure banner, /peers, and registerHostedSession. /peers also stops hardcoding "--bare or --safe-mode" and names the cause that actually fired, with the environment channel that reaches each, so a session suppressed only by QWEN_CODE_SAFE_MODE recognises why. Drops the ConfigParameters field and accessor an earlier shape of this branch added. Config already stores both flags and loadCliConfig already passes them in, so the parameter duplicated two booleans that were on the object handed to every read site. Its fail-closed default was also what made the hosted reader look unsafe to wire, which is why that reader was deferred rather than fixed; reading the accessors removes both the parameter and the reason. Each new case was mutation-verified and each mutation reverted: ignoring config at the ACP gate, at the interactive bind, or at the banner branch turns the corresponding cases red; deleting the /peers message arm turns both of its cases red; dropping the getBareMode clause turns the bare-mode cases red in three suites. Refs #8724 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: record that --bare and --safe-mode suppress cross-session messaging Five places stated the settings-only rule as a contract and would be wrong after the gate change: the agents.crossSessionMessaging row, the two --safe-mode inventories, the hosted-session registration paragraph, and the SDK's inbox note. The row now carries the same "Ignored in --bare and --safe-mode" clause its sibling general.outputStyle already has, and both inventories list cross-session messaging alongside the other surfaces safe mode closes. The dated design record docs/design/2026-09-02-peer-inbox-reachability.md is left as written -- it describes the state at its own date, and rewriting it would falsify the record rather than update it. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): name both causes when a flag and the setting both turn messaging off `/peers` answered with the startup suppression whenever one was in force, so a session whose own `agents.crossSessionMessaging` was also off was told the setting cannot matter, and was sent to a restart that leaves messaging off with the surviving cause still unnamed. The scope remedy now leads and the flag follows as a second cause when both fire; the single-cause text is byte-identical to before. Also qualify the SDK's universal reachability claim for the two flags in the three copies that carry it (package README, the `endpoint.ts` file JSDoc and the hand-maintained `docs/developers/sdk-typescript.md` mirror, whose inbox bullet was still the pre-PR twin of the one this branch amended), and record at the gate that an *inherited* `QWEN_CODE_SAFE_MODE` — unlike `QWEN_CODE_SIMPLE`, which `spawnChannel.ts` scrubs from spawned `qwen --acp` children — suppresses every session a daemon or editor hosts. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Patrol-Run: qwen-pr-closeout/jmujyzhfka1 --------- Co-authored-by: qqqys <qys177@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: yiliang114 <effortyliang@gmail.com> |
||
|
|
70edbf475e
|
feat(core): preserve prompt cache for deferred tools (#10410)
* feat(core): add deferred tool call bridge * fix(core): update read-file image guidance regex to the two-step bridge wording (#10410) * fix: resolve remaining merge conflicts from main merge (#10410) * fix(core): address round-1 review for the deferred tool-call bridge (#10410) Security/correctness: - Re-check the execution allowlist against the resolved tool_call target in the scheduler (fork allowlist previously only saw the wrapper name). - Skip the pending bridge TOOL_CALL start on abort so a cancelled call never emits a start event after its synthetic TOOL_RESULT. - Route INVALID_TOOL_PARAMS bridge resolution errors through the validation-retry loop so repeated bad envelopes get the stop directive. - Reject a hidden deferred target when tool_search is unregistered, and make the TOOL_NOT_REGISTERED remedy conditional on tool_search existing. - Seed announcedMcpToolNames for eagerly revealed MCP tools so a later disconnect is still announced when the bridge is incomplete. - buildDeferredToolsReminder returns null when the bridge is incomplete. Docs (bridge-registered caveats, two-half reachability, DeepSeek removal, schema regeneration, table cells, wording): settings.md, settingsSchema.ts + regenerated settings.schema.json, sdk types.ts/README/sdk-typescript.md, design doc, skills/types.ts, web-shell i18n. Tests: allowlist re-check, abort ordering, retry directive, leader-only and tool_search-absent rejections, revealed-tool exclusion, bridge availability both directions, PM bridge gate, cancel-after-resolution rename, incomplete- bridge reminder, MCP disconnect announcement, zoom hint wording. * test(cli): model the complete bridge in the ACP tool_call routing test (#10410) * test(integration): adapt sdk-mcp-server E2E to the deferred-tool bridge contract (#10410) Under the ToolSearch + ToolCall bridge, tools fetched via tool_search select: stay hidden from the model-facing declaration list so the prompt-cache prefix remains stable (they are invoked through tool_call; direct invocation by name still executes, as the resume test already exercises). Flip the three fresh-session advertisement assertions from 'tool is advertised after select:' to 'tool remains hidden', matching the contract documented in tool-search.ts. The resume-restoration test is unchanged and already passes at the merge commit. * fix(core): normalize the legacy deny gate for tool_call like the loop fallback (#10410) The no-PermissionManager bridge deny gate matched deny entries exactly (canonicalToolName is alias-resolution only — no lowercase/trim), but the _schedule legacy-deny fallback it delegates to compares excludedTool.toLowerCase().trim() === normalizedToolName. Config stores permissions.deny entries verbatim, so an entry like 'Tool_Call' or ' tool_call ' slipped past the gate's exact compare: the envelope was unwrapped and the fallback then checked the deny list only against the resolved TARGET name — executing a call the same config denies when it arrives as a direct tool_call (pre-diff behavior). Mirror the loop's normalization on the gate so both paths deny identically (R2-1). Adds three parameterized cases ('Tool_Call', ' tool_call ', 'TOOL_CALL') extending the legacy-deny pin; removing the normalization turns all three red while the exact-entry case stays green (verified). Full coreToolScheduler.test.ts 395/395. * fix(core): complete the bridge remedy list, eager-reveal disconnect tracking, and prune pinning (#10410) Three fix-induced findings from the bridge rounds: R1-15: the unreachable-tools warning's remedy clause now names tools.disabled alongside deny rules and --exclude-tools, matching the cause enumeration the PR documents in settingsSchema.ts; the warning test pins the mention. R1-28: an eager-reveal of an MCP tool (incomplete bridge) now also records the name in announcedMcpToolNames. The seed alone only reaches that set via rememberAnnouncedDeferredTools, which runs exclusively in startChat — so a server registering after the initial startChat used to disconnect silently and the model kept calling a dead server's tools. New test covers the mid-session reveal, the disconnect announcement, and a reconnect/disconnect flap announcing twice; removing the add turns it red (verified). R1-18: new interleaved test pinning the batch-start prune as the only mechanism that clears a tool_call-keyed retry counter across a successful bridged execution (recording keys on the model-facing name, prune keys on post-resolution names); disabling the prune fires RETRY LOOP DETECTED prematurely and turns the test red (verified). client.test.ts + coreToolScheduler.test.ts: 770/770 green. * docs: qualify the bridge reachability claims to match the code (#10410) Four fix-induced doc findings, applied together across all copies: R1-12 (9 locations): 'the demoted tools are out of reach for that session' is now qualified with the two escape hatches the code has — tools also listed in tools.visible are declared upfront, and resumed sessions re-declare demoted tools referenced by direct calls in the transcript (consistent with client.ts's own 'would be false for this session' comment). R1-25 (settings.md tools.core/discoveryCommand/migration rows): 'stay reachable through tool_search + tool_call' now carries the 'while both bridge tools are registered' condition. R1-9 (threshold, 3 copies): 'Set 0 to always keep deferred tools behind the bridge' overstated — two reveal paths ignore the threshold; replaced with the qualified wording naming resumed-history replay, tools.visible, and the incomplete-bridge fallback. R2-2 (4 locations): the unaffected/bypass lists now name tool_call next to tool_search, matching isExemptFromEagerAllowList which exempts both identically. * test(integration): harden the bridge-contract E2E assertions (#10410) R2-9: the flipped not.toContain declaration assertions now carry positive controls on the same snapshot — the post-select list must contain both alwaysLoad bridge tools (tool_search, tool_call) — so an emptied or index-shifted declaration list can no longer pass them vacuously. R2-15: the first test now scripts the fake model to invoke the selected tools through tool_call envelopes (the documented post-select path), exercising resolveDeferredToolCall end-to-end through query(); results are paired by the model-facing name ('tool_call') with content checks, since the scheduler keeps modelFacingName on the wire. Reverting the script to direct-by-name calls would remove the suite's only end-to-end signal for a broken invocation half. typecheck -p integration-tests clean. * chore(core): address wenshao verification notes on the bridge (#10410) 1. tool-search.ts:326 — Prettier-format the schemaBlocks map (slipped through CI because the format step runs --write, not --check). 2. tool-call.test.ts — pin the recursive-bridge guard by its message ('cannot invoke bridge tool'), not just errorType: the downstream isDeferredAndHidden rejection returns the same INVALID_TOOL_PARAMS, so the old errorType-only assertion stayed green with the guard deleted. Mutation check confirms the strengthened test now kills that mutant (2 failed with the guard removed, 12/12 intact). * fix(core): enforce the subagent/teammate tool-exclusion set in the tool_call bridge (#10410) R4-1 (review round 4): the tool_call bridge bypassed the subagent/teammate tool-exclusion set. EXCLUDED_TOOLS_FOR_SUBAGENTS / EXCLUDED_TOOLS_FOR_TEAMMATES were enforced only at declaration level (prepareTools isExcluded), but the bridge makes invocation independent of declaration without carrying the exclusion set over — resolveDeferredToolCall enforced only the plan-lifecycle and leader-only policies, tool_search had no exclusion filter, and the scheduler's post-unwrap re-checks never consulted the set. A wildcard/ general-purpose subagent (or teammate) could therefore discover (tool_search) and execute (tool_call) control-plane tools it must not reach (team_delete, cron_*, workflow, send_message, ...); the probe executed team_delete end-to-end through the bridge from a subagent context. The merge base rejected the same trigger (undeclared names fail the declaredToolNames gate), so the bridge introduced the path. Fix: enforce the same context-aware exclusion set in resolveDeferredToolCall, after the plan-lifecycle/leader-only checks (so those keep their specific messages). To avoid a circular import (agent-core imports coreToolScheduler, which imports tool-call), the exclusion sets and getExcludedToolsForCurrentContext moved from agent-core.ts to subagent-plan-tool-policy.ts (already imported by tool-call.ts); agent-core.ts re-exports them for existing consumers. The check is guarded by isSubagentLikeExecutionContext so the leader context is not gated. Adds a mutation-checked test (rejects an exclusion-set target team_delete / workflow bridged from a subagent context): removing the exclusion check turns both red. tool-call.test.ts 14/14; tool-search + coreToolScheduler 447; subagent-plan-tool-policy 6; agent-core + background-agent-resume 99; core tsc --noEmit clean. * fix: remove unused SUBAGENT_PLAN_LIFECYCLE_TOOLS import from agent-core (TS6133) The exclusion sets that spread SUBAGENT_PLAN_LIFECYCLE_TOOLS moved to subagent-plan-tool-policy.ts so the tool_call bridge can enforce them without a circular import; the import in agent-core.ts was left behind and tsc --build flags it as unused (TS6133). * fix(core): address round-5 review on the tool_call bridge exclusion (#10410) R4-1 follow-up (Critical): the flat exclusion denied AgentTool through the bridge even while maxSubagentDepth permitted the spawn — prepareTools depth-gates AgentTool (re-admits it while spawnBlockReason === null) but the bridge consumed the raw set. Add the shared predicate isToolExcludedForCurrentContext (subagent-plan-tool-policy.ts) that carries the depth-gated re-admission, consumed by BOTH prepareTools and resolveDeferredToolCall so the layers cannot drift; the scheduler threads the real configured maxSubagentDepth through. Without the configured depth the bridge fails closed on AgentTool (the raw-set floor). The predicate is deliberately ungated so prepareTools keeps its frameless fail-closed contract; the bridge and tool_search apply their own isSubagentLikeExecution context gate, leaving the leader session untouched. R5-1: mirror the exclusion on the discovery side — collectCandidates and the select: blocked predicate now drop context-excluded tools, so subagents are no longer advertised the schemas of tools they are forbidden to invoke (schema leak + wasted turns). Leader re-inspection stays unrestricted. R5-2: move the plan-lifecycle / leader-only / exclusion checks ahead of the isDeferredAndHidden gate (relative order kept). Real excluded tools are frequently not deferred (workflow/team_delete/todo_write shouldDefer=false, enter_plan_mode constructed shouldDefer=false), so the old gate order misrouted them into the factually-wrong 'already visible — call it directly' INVALID_TOOL_PARAMS denial and the scheduler's malformed-envelope retry counting. R5-3..R5-6 test pins: SEND_MESSAGE subagent denial + teammate resolve/deny cases discriminate the context-aware selector; the plan-tool denial pins the dedicated message; allow-side cases pin the context gate (leader and in-frame non-excluded resolution); the legacy alias case pins canonical-name membership keying. Fixtures now use the real non-deferred shape. Mutation-verified: nine mutants (check removed, selector swapped both ways, plan check removed, envelope-name keying, flat AgentTool, bridge gate removed, blanket in-frame denial, tool-search filter removed) all turn the new tests red; suites green (tool-call 22, tool-search 56, policy 6, agent-core 49, coreToolScheduler 396). * fix(core): close the two round-5 deferred bridge items (#10410) Case-insensitive resolution: tool_search's select: resolves requested names case-insensitively, but the tool_call half resolved them exactly, so a schema reviewed as e.g. Read_File was not callable through the bridge. The invocation half now falls back to a case-insensitive match against the registered names (exact-case names keep the fast path), and the fallback feeds the recursive bridge guard, so casing cannot dodge it either. Remedy pin: 'rejects an unknown deferred target' now pins the present-side remedy ('Run tool_search again...'); the absent-side constant was already pinned by the sibling case, so both remedy branches are mutation-checked. Mutation-verified: removing the fallback turns 2 tests red; dropping the remedy suffix turns 1 red; suite green (24). * fix(core): address round-6 review — disallowedTools re-check and stale DeepSeek text (#10410) R6-8 (Critical): the bridge resolved around the declaration list, so a per-agent disallowedTools blocklist — enforced only at declaration level in prepareTools() — did not stop a bridged call: a subagent configured with disallowedTools: ['mcp__slack'] could tool_call mcp__slack__post_message to execution, where the merge base synthesized 'Tool not found'. Fold the blocklist into AgentCore.isToolExecutionAllowed with prepareTools' exact match semantics (MCP server-level patterns via matchesMcpPattern, exact match otherwise); the predicate already feeds both the direct-call gate and the scheduler's bridge target re-check, so one fold closes both. The scheduler's bridge denial message now covers allowlist and blocklist. Mutation check: removing the fold turns the new agent-core test red. Discovery-side mirror (tool_search candidates) is deliberately deferred: it needs the blocklist threaded into the agent context frame across all launch sites; invocation is fail-closed either way (ruled in the thread). R6-1..R6-7: drop the stale 'automatic opt-out for DeepSeek models' references from every string this PR writes (settingsSchema + generated vscode schema, SDK types/README, user/dev docs) — this PR's config change removes that opt-out, so the text must not keep citing it; also drop the DeepSeek disable advice from the tools.toolSearch.enabled doc row (round-6 deferred list item). Triage follow-up (wenshao): the ACP frontend now threads the configured maxSubagentDepth through resolveDeferredToolCall, matching the terminal scheduler and tool_search so the depth-gated AgentTool re-admission is consistent across all three frontends (omitting it failed closed, but the corner case now agrees everywhere). Verified: core typecheck clean; agent-core 50, tool-call 24, tool-search 56, coreToolScheduler 396 — all green; blocklist mutant red. * test(cli): give the Session test config mock getMaxSubagentDepth (#10410) The ACP bridge now threads the configured depth through resolveDeferredToolCall; the shared partial Config mock in Session.test.ts cast 'as unknown as Config' lacked the method, so 'routes tool_call through a hidden deferred tool in ACP' crashed with a TypeError. Mocks claiming the Config interface should carry the methods the code under test calls — verified the target test green locally. * fix(docs/core): address round-7 review — deny-vs-remove semantics, tie-break parity, precedence pin (#10410) R7-1 (Critical): the rewritten tools.core row claimed permissions.deny 'removes a tool outright', but deny never removes MCP tools from the registry — ToolRegistry.registerTool gates only on disabledTools and mcp-client registration never consults the PermissionManager. Restore the split semantic (deny blocks calls; tools.disabled / per-server excludeTools remove from the registry), matching the SDK README and the permissions.deny row's own wording. The pasted migration-table row at :492 gets the same wording fix AND its structure repaired (it was a 5-cell settings-table row inside the 3-column migration table; now a proper Legacy/Equivalent/Notes row). R7-14: the invocation half's case-insensitive fallback now uses last-match like the discovery half's lowerIndex (Map keyed by lowercase name, last registered variant wins), so both bridge halves resolve the same tool for a case-variant request. Mutation check: first-match turns the new tie-break test red. R7-13: new test composing executionAllowedTools with disallowedTools — the blocklist wins for an allowlisted-and-blocklisted bridged target. Mutation check: removing the blocklist fold turns both blocklist tests red. Verified: tool-call 25 + agent-core 51 green; core typecheck clean. * fix: align deferred tool case resolution * fix(core): preserve bridged tool identities * chore(docs): minimize formatting churn * fix(core): handle deferred tool edge cases * fix(core): close deferred bridge gaps * fix(core): fail closed on malformed bridged classifier entries, correct bridge docs (#10410) - toAutoClassifierInput coerces a missing/non-string envelope name and fails closed on the name alone, so a malformed bridged history entry can no longer throw into the caller's raw-args fallback and leak unredacted arguments into the AUTO classifier prompt (R16-3). - The prefix-stability guarantee no longer scopes mid-session declaration refreshes to resumed sessions: any tool-set refresh re-declares a still-hidden deferred tool that live history calls directly. Swept through settings.md, settingsSchema.ts (+ regenerated schema), i18n zh, SDK README/JSDoc, and the tool_search description (R16-2). - SDK docs now state that the incomplete-bridge warning goes to the CLI child's stderr, which the SDK discards unless debug is enabled (R16-4). - Correct the collectCandidates mutation comment: the keyword assertion pins the layered collectCandidates + returnSchemas filter pair, not the single filter (verify-report F2). * fix(core): redact orphaned tool call envelopes * docs(sub-agents): clarify deferred-bridge exception to the tools allowlist (#10410) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): preserve Goal provenance for bridged duplicates * fix(core): guard canonical tool-name lookup * fix(core): keep bridged envelope payload in loop-detection repeat key - getLoopDetectionToolCall substituted {} when a bridged envelope's `arguments` was not a plain object (e.g. a JSON string awaiting SchemaValidator repair), collapsing every such call to the same target onto one repeat key; the always-on consecutive-identical guard then halted turns of distinct calls. Keep the envelope itself as the key material so distinct payloads stay distinct. - Correct the deferred-tool refresh enumeration in all doc copies: subagent completion never triggers a declaration refresh; the real triggers are resume, MCP discovery, the first plan-mode entry in a session, and subagent definition changes. Regenerated settings.schema.json from the settings source. * fix(core): bound nested tool_call classifier projection, correct toolSearch cache text (#10410) * fix: address deferred bridge review findings * feat(core): default tools.toolSearch.threshold to 0 (always-defer) (#10410) The preload gate shipped on by default for one reason: revealing a deferred tool rewrote the function-declaration list at the front of the prompt prefix, so a single tool_search load busted the whole KV cache. With a small deferred set the token savings were negligible, which made deferral a net loss. The tool_search + tool_call bridge removes that premise — a bridge reveal never touches the declaration list — so always-defer is now affordable and matches Claude Code's ENABLE_TOOL_SEARCH default. What preloading still buys is the one tool_search round trip before a deferred tool's first use: a per-tool one-off, not the whole-session compounding cost a cache bust was. The gate itself is unchanged — still session-start only, still one budget over bundled and MCP deferred tools alike, still all-or-nothing — so raising the threshold opts back in exactly as before. Safe mode and bare mode already forced 0, so that path is untouched. Regenerated settings.schema.json from the settings source and corrected both design docs, whose cache-bust rationale the bridge supersedes. * fix(core): preserve deferred fork tools * fix(core): harden bridge history consumers against malformed envelopes - microcompact: guard non-string tool names from unvalidated history, and record an unparseable-call sentinel so a reused call id fails the identity unanimity check instead of compacting on partial evidence - goals: classify Goal bookkeeping provenance case-insensitively so a case-variant bridged get_goal/update_goal result cannot enter the evidence catalog as an external fact - client: seed recentCompletedToolNames from the resolved bridge target on resume, matching what the live path records - docs: record the missing presentation-mark/schema-fingerprint gate as a known limitation pending the #11321 scope decision * test(core): raise code-mode waitFor deadlines past host boot time The verification gate rejected the previous round on three failures that never reached an assertion: the code-mode host spawns a tsx child whose boot alone measures ~4.5s on the oversubscribed runners, while exec-context-tools waited the vi.waitFor default (1s) for the first nested dispatch and two scheduler tests waited 4s for nested dispatch / awaiting_approval. Raise the three inner deadlines to 10s and the two containing test timeouts to 20s. No assertion changes. * fix(core): align bridge wording and gates with the two-half tool bridge (#10410) - Reword the hidden-deferred-tool sentence (shared by the Workflow tool description, its failure hint, and the workflow-keyword reminder) to the bridge flow: review the schema with tool_search, then invoke with tool_call; the old 'reveal with ToolSearch first' wording instructed a mechanism tool_search no longer performs (R27-1). - Gate the workflow-authoring route and the keyword reminder on BOTH bridge halves, so a tool_search-only session inlines the reference and drops the steering instead of pointing at a route that cannot invoke (R27-2). - Enforce the registry's isToolDeclared capability gate in resolveDeferredToolCall, matching tool_search's select: refusal, so tool_call cannot execute a registered-but-undeclared target such as propose_goal outside a goal-proposal turn (R27-3). - Carry the parent agent's per-agent disallowedTools blocklist through the agent ALS frame into a fork's execution allowlist and toolConfig, so the union with the live registry cannot re-admit a tool the parent was configured never to reach (R24-1). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): enforce configured tool allowlists across bridge * fix(core): persist fork disallowedTools blocklist across background resume (#10410) * fix(core): admit exec under CodeModeOnly when configured tools omit it (#10410) In code mode the registry declares exec unconditionally, so folding a finite toolConfig.tools list that omits exec into the execution allowlist refused the only declared tool and degraded the agent to text-only. Carve exec out in the executionAllowedTools === undefined branch, mirroring the existing carve-out below; the blocklist check still runs first, so disallowedTools: ['exec'] keeps winning. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): align deferred bridge policy and redaction * fix(core): preserve deferred fork tool access * fix(core): restore deferred bridge in containers * fix(core): preserve deferred bridge policy and accounting * fix(core): distinguish deferred bridge outcomes on resume --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix[bot]@users.noreply.github.com> Co-authored-by: yiliang114 <jinjing.zzj@gmail.com> Co-authored-by: 易良 <1204183885@qq.com> |
||
|
|
f2f10e7b39
|
feat(settings): turn cross-session messaging on by default (#11840)
* feat(settings): turn cross-session messaging on by default The switch has been opt-in since the feature landed, which was the right posture while the pieces that make a reachable session safe were still arriving one pull request at a time: authenticated inboxes, receipts and held-message expiry, review-class parity with tighten-only repository settings, trusted controller tokens, admission control, the registry contract, daemon-managed sessions and the SDK peer endpoint. With all of that in place, off by default no longer protects anything. It only means a voice front-end joining through the SDK cannot see a session whose user never found the setting, and two sessions in one repository cannot tell each other a build finished. Flip the schema default to true. Merged settings carry only what some scope actually wrote, so the schema default never reached the readers of this key: every one of them saw `undefined` and compared it to `true`. The four readers now ask one helper, which answers on for `true` and for an unset key, and off for `false` and for anything it does not recognize — a value the reader cannot interpret must not open a socket. The workspace tighten-only ranking follows: unset now ranks with `true`, so a repository's `false` is a tightening and is kept, and a workspace `true` repeats the default and is dropped without a warning. The words change with the default. `/peers` on a session with no inbox distinguishes "turned off in settings" from "the inbox could not start" instead of telling the user to enable a setting that is already on. On a platform without an inbox transport, the startup failure line is shown only to a user who turned the switch on by hand; a bind that failed where it should have worked is still reported to everyone, because that session is unreachable and this line is the only place its user learns it. The controller-token hint, the send_message error, the settings reference, the SDK guides and the messaging section of the commands page now say what turning the feature off looks like. Suites that stage a session through partial mocks turn the switch off in their settings, or complete the config mock where registration now runs; the daemon-session and interactive-startup suites are where unset-means-on is exercised. Part of #10925 * test(cli): turn messaging off in suites that stage startup or ACP sessions through partial mocks Two suites the default flip reaches that the first commit missed. The entry-point startup test counts exit-cleanup registrations, and with messaging on the inbox arms a third. The ACP worktree-restore suite loads sessions through an exhaustive core mock, and with messaging on each loaded session registers itself through calls that mock does not stage. Neither suite is about messaging, so both say off in their settings; the on-by-default path is exercised in startInteractiveUI.test and the daemon-session suite of acpAgent.test. * refactor(cli): name the opted-in-by-hand check next to the enabled helper The startup failure notice asks two questions three lines apart: whether messaging is on, which the shared helper answers and an unset key answers yes, and whether the user wrote `true` themselves, which decides whether an unsupported platform is worth mentioning. The second read the raw setting inline, next to a helper documented as the one place the first question is answered, and looked like a missed migration. Bind it to a name so the two questions read as two. * fix(cli): address review on the default-on switch: scope-aware notices, no futile rebinds, honest docs Review round one on the default flip. Thirteen suggestions, all taken. Two readers now ask a question merged settings cannot answer, so both move next to `isCrossSessionMessagingEnabled` in peerMessaging/enabled.ts: - Whether a person opted in. The startup notice for a platform with no inbox transport is said only to someone who wrote `true`; it read the merged value, which also carries an operator's system-defaults file, so a fleet default counted as a hand opt-in. It now reads the user scope, and the workspace scope when it is trusted and active. - Which scope turned messaging off. `/peers` told everyone their settings said `false` and to set it to true. Anything but `true` or unset reads as off, so it no longer states the value, and it names the scope with a remedy that works there: a workspace `false` outranks a user `true` (a workspace may only tighten), System overrides everything, and a user `true` does override system defaults. `/peers` on a platform with no inbox transport now says so as information. It was error-styled and repeated the failure's advice to disable a setting most of those users never enabled. An ACP process no longer retries the bind for every hosted session after `unsupported_platform`. That refusal holds for every candidate path, so the settled null is kept; every other cause still retries. The design doc, in both languages, names the two senders delivered without review that the default now reaches: a process the session starts, which inherits the child token, and a same-user process that claims the receiving session's review class, which nothing authenticates. It records why they are documented rather than gated. The setting's description, the settings reference and the VS Code schema carry the same clause, and the Windows paragraph says what changes there. `commands.md` no longer promises a warning for a workspace `true` that merely repeats the default. Tests pin what was unpinned: a daemon-hosted session with the key unset registers; one bind attempt under `unsupported_platform` and a retry under any other cause; the warning when a workspace `true` would reopen a switch the user turned off; the operator-default startup case; the scoped off messages. Negative assertions that stopped being able to fail are repointed, and stale "off by default" test names and comments are gone. |
||
|
|
af4dece3a7
|
feat(sdk): add a peer endpoint so a program outside Qwen Code can join cross-session messaging (#11560)
* feat(sdk): add a peer endpoint so a program outside Qwen Code can join cross-session messaging
`@qwen-code/sdk/peer` is an implementation of the cross-session protocol
for a program that is not itself a Qwen Code session — a voice
front-end, a relay, a build watcher. `PeerEndpoint.start({ name })`
binds an inbox and publishes a session record, so the program is listed
by `qwen sessions ps` and by every session's `list_agents`; `list()` and
`send()` address sessions by name; `onMessage` receives what they send;
receipts are applied as state transitions and can be awaited.
It is written from the protocol page with Node's own modules alone and
shares no code with Qwen Code. That makes it the page's first
independent implementation, and a conformance suite runs it against
Qwen Code's own registry, inbox, directory and send path in both
directions: listing and reaching each other, frames parsed and answered
each way, a folded drop receipt settling a burst, a user-minted
controller token recognised and a forged one not, identical verdicts on
hostile lines, record files and names, and every published limit pinned
to Qwen Code's constants.
The endpoint takes the shared `<pid>.json` when it is free and a minted
`<pid>-<8 hex>.json` when it is held, never replacing a record it cannot
prove dead. Its inbox accepts only its own token, it never deletes
records or sockets other processes wrote, and trust comes only from a
controller token. On Linux it refuses to publish a record without a
start token and PID namespace.
Implementing the page found two rules it left implicit, now stated in
"Writing your own record": `procStart` and `pidNs` are required on
Linux, and a held `<pid>.json` means writing the minted name instead.
Separate Node-only subpath, kept out of the browser-bundled entries;
the build checks its size and that it carries no runtime dependency.
* fix(sdk): present the controller token only on sends marked controller
Review follow-ups on the peer endpoint.
The controller token was presented on every send, so any process that
published a record under a name the program then addressed read the
plaintext of a credential Qwen Code only ever stores hashed. A send now
presents it only when marked `controller: true`; unmarked sends — from an
endpoint holding a token too — present the recipient's own token. On a
marked send, two records for one session id and name are ambiguous rather
than collapsed to the newer, so a copied record cannot take the grant. A
malformed `controllerToken` (empty, no `qpc_` prefix) is rejected at start
with `invalid-controller-token`, as is a marked send without one.
Other fixes:
- Two `start()` calls in one process raced for `<pid>.json` and one
record was lost; registrations inside a process now run one at a time.
- `send()` checks `closed` again after the directory read, so a send
overlapping `close()` rejects instead of writing a dead reply address.
- `ambiguous.matches` holds only addresses that select one session.
- Receipt `reason` is flattened and bounded like every other label.
- The local concurrent-send ceiling is marked `local` and described as
this program's limit, not as a busy peer.
- An explicit `socketPath` no longer re-permissions a directory the call
did not create.
- `probePeerSocket` is renamed `probePeerSocketVerdict`, matching core;
the status list is the single source of the status type.
- `flattenPeerLabel` matches control and format characters by Unicode
category and caps in code points, in core as well, so the two
implementations still agree.
- The build checks every emitted peer declaration, from a module with
tests of its own; the surface test is compiled by the typecheck and
matches imports as path segments.
Docs: `list_agents` and `send_message` need `agents.crossSessionMessaging`
on the receiving session; delivery without review also follows a matching
`fromMode`, and `agents.crossSessionInbound` outranks a controller grant;
a controller send to another endpoint is dropped unread; the endpoint's
inbox applies none of the protocol page's §6; the `socketPath` sibling
fallback; and the protocol page gives separate reasons for `pidNs` and
`procStart`. The design doc no longer says the default entry ships to
browsers.
Tests pin each of these, plus the paths the first round left unexercised:
ledger and answered-id eviction past their bounds, start rollback, the
Linux identity guard, registry directory tightening and read errors, the
over-long line with the default deadline, the symlinked and over-long
socket paths, prompt close, Windows, and a live PID with a mismatched
start token.
|
||
|
|
f86098363d
|
test(core): pin tools.eager Object.prototype-key tolerance and correct related docs (#10479)
Some checks are pending
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / Integration Tests (no-AK, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (ubuntu-22.04) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (windows-2022) (push) Blocked by required conditions
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
Update ECS Runner Qwen / Resolve version (push) Waiting to run
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-1 (push) Blocked by required conditions
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-4 (push) Blocked by required conditions
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-5 (push) Blocked by required conditions
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-2 (push) Blocked by required conditions
Update ECS Runner Qwen / Update Qwen on ecs-update-hk-3 (push) Blocked by required conditions
* test(core): pin tools.eager Object.prototype-key tolerance and correct related docs Item 1 of #10400 (a tools.eager entry named after an Object.prototype key crashing PermissionManager.initialize) was already fixed before #10098 merged: resolveToolName only reads own properties of the alias table and the crashing unknown-entry loop was removed. Pin that contract with regression tests so a reintroduction fails CI: - initialize() tolerates constructor/toString/valueOf/... eager entries - resolveToolName returns Object.prototype-keyed names unchanged - config.test.ts: add the third LS x tools.eager cell (enabled and covered by the list -> registered eagerly) Also correct the doc inaccuracies tracked in the issue: - settings.md: permissions.deny blocks MCP tool calls at runtime but does not remove them from the registry (tools.disabled / the per-server excludeTools filter does) - ToolSearch-absent caveat (5 copies): note the resumed-session history re-reveal and tools.visible carve-outs - sdk-typescript docs + README: note the tools.eager exempt families Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): cover visible permission-deferred tools Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
36de6ebef9
|
fix(core): decouple permissions.allow from tool registration via tools.eager (#10075) (#10098)
* fix(core): decouple permissions.allow from tool registration via tools.eager (#10075) `permissions.allow` was overloaded in #9829 to double as a registry-level allowlist, so configuring it purely for auto-approval silently reshaped the model's toolset — the #10075 report (`edit` / `write_file` vanishing in 0.22.1). #10082 softened the symptom (uncovered tools became `deferred` rather than unregistered) but kept the conflation: one key still decided both "don't ask me about this" and "don't send this schema". Split the two jobs instead of trading them off: - `permissions.allow` (and `ask`) go back to PURE auto-approval. They never remove, demote, or hide a tool. This is the actual fix for #10075. - A dedicated `settings.tools.eager` allowlist now drives the eager/deferred split, preserving #9827: when set, only the named tools' schemas ride in the initial model request and everything else is deferred — still registered, still in `/tools`, still loadable via `tool_search`. #10082's three-state registration machinery is kept as-is; only its input changes, so no multi-layer deferred wiring is reverted. #9829's rule-parser alias map is likewise untouched — the rollback is scoped to the one part of that PR which caused the regression. Also fixed along the way: - Command-discovered tools (`tools.discoveryCommand`) were hard-DROPPED when the allowlist did not cover them, which reproduced #10075's silent disappearance for that registration path. They now defer like built-ins. - `isLsToolEnabled()` no longer treats permission rules as a registration signal (reverting the #9829 side-effect). - `/tools` marks deferred tools "(on demand)" with a footnote, so an active `tools.eager` list is visible instead of silently reshaping the toolset — the user-facing half of the original report. Migration: users who set `permissions.allow` for schema-shrinking (llama.cpp, Docker Model Runner) move that list to `tools.eager`; the shape and entries are unchanged. `tools.core` no longer migrates to `permissions.allow`. * fix(core): make an explicitly empty tools.eager list active, not ignored `tools.eager: []` collapsed into "unset", so an empty list silently meant "no restriction" — the opposite of the convention #10080 establishes for the sibling `tools.core` key, where an explicit `[]` is an active-but-empty allowlist and only `undefined` means unrestricted. Two knobs in the same `tools.*` namespace reading empty-array backwards from one another is a trap, and #10138 shows how expensive that divergence gets once transports start dropping the empty case. Activation is now `Array.isArray()`, matching #10080. An explicitly empty `tools.eager` defers every non-exempt tool, which is a gentler answer to the constrained-decoding failures behind #10065 than `tools.core: []`: the eager request carries almost no tool schemas, but nothing is disabled — every tool stays registered and reachable via `tool_search`. Two supporting changes were needed to make the empty case reachable at all, the same class of bug #10138 reports for coreTools: - `Config.getEagerTools()` returns `readonly string[] | undefined` instead of collapsing an absent list to `[]`. - The CLI guards with `Array.isArray(settings.tools?.eager)` before calling `normalizeDisabledToolList`, which maps `undefined` to `[]` and would otherwise erase the distinction on the way into core. Malformed-only lists (`['', 'Bash(unbalanced']`) now leave the allowlist active but matching nothing, so everything defers. Deferring more than intended is recoverable — ToolSearch still reaches every tool — whereas ignoring a configured list resends exactly the schemas the user asked to keep out. * docs: qualify schema-shrink advice and registration claims (#10075) Address review findings R1-2, R1-4, R1-7, R1-13: state that coreTools is the only allowlist-style registration gate (whole-tool deny/exclude rules and tools.disabled also deregister), qualify the schema-shrink advice to whole-tool rules with MCP tools exempt, and correct the deprecated tools.core migration guidance (permissions.allow is pure auto-approval and cannot reproduce the fail-closed allowlist restriction). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): cover the two behaviours this PR changed but left unpinned R1-17 (adapted) — the `#9827` test sweep removed the only guard that `Config.initialize()` tolerates non-string entries in a settings array. The path that guard protected is gone with `isLsToolEnabled`'s revert, so it is re-pointed at the equivalent new surface: settings load performs no element-type validation, so `tools.eager` holding `null` or a number must be filtered rather than crash registry construction. R1-18 — `isLsToolEnabled()`'s reverted behaviour had no test. The eight deleted tests only covered the positive allowlist path that no longer exists; nothing pinned the negative. Added a test asserting that `permissions.allow` / `permissions.ask` coverage no longer opts `list_directory` into registration, so only `tools.listDirectory.enabled` or `coreTools` can open that gate. The docs findings from the same review (R1-1/R1-2/R1-4/R1-5/R1-12) are already fixed by |
||
|
|
ad8e09c663
|
fix(core): keep allowlist-uncovered tools registered as deferred instead of removing them (#10075) (#10082)
* fix(core): demote permissions.allow-uncovered tools to deferred instead of removing them (#10075) * test(integration): add E2E regression guard for #10075 uncovered tools * fix(cli): preserve deferred daemon tools * fix(core): preserve deferred permission boundaries |
||
|
|
652cd76781
|
docs: consolidate assorted documentation corrections (#9855)
* docs(zed): use real debug log env var (#9853) * docs(headless): fix JSON jq examples (#9858) * docs(cli): align status line default preset prose (#9864) * docs(acp): update permission mediator status (#9866) * docs(sdk): align query option reference (#9855) --------- Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> Co-authored-by: 易良 <1204183885@qq.com> |
||
|
|
f2e593c26b
|
fix(core): make permissions.allow restrict the tool schemas sent to the model (#9829)
* fix(core): make permissions.allow restrict the tool schemas sent to the model (#9827) permissions.allow only auto-approved calls; it never gated tool registration, so the outgoing tools array kept every built-in schema even when an allowlist was configured — contradicting the settings docs migration table ("unlisted tools are disabled at registry level") and breaking backends like llama.cpp that compile all tool schemas into a single grammar. - Activate a registry-level allowlist when settings.permissions.allow has at least one valid rule: built-in tools not covered by any allow rule are no longer registered (absent from /tools and the API request). MCP tools and the structured_output contract stay exempt; session-granted rules ("always allow", skill allowedTools) extend membership but never activate the allowlist mid-session. --allowed-tools / SDK allowedTools / legacy tools.allowed keep their pure auto-approval semantics. - Complete the rule alias map so the display names shown by /tools (SendMessage, UpdateGoal, ...) match in allow/deny rules. * fix(core): honor permissions.allow in the list_directory opt-in gate (#9827) isLsToolEnabled() only read tools.listDirectory.enabled and the coreTools allowlist, so an explicitly allowlisted list_directory passed PermissionManager.isToolEnabled() but was never registered — absent from /tools and the model request, with calls failing TOOL_NOT_REGISTERED. This broke the documented tools.core -> permissions.allow migration equivalence for exactly this tool. Consult getRegistryAllowList() with the same coverage semantics the registry gate uses (toolMatchesRuleToolName, so Read / ListFiles / specifier forms all count). * fix(core): keep plan-mode lifecycle tools registered under the allowlist (#9827) The permissions.allow registry gate covered exit_plan_mode / enter_plan_mode / ask_user_question, so the exact reporter configuration unregistered them. The plan-mode system reminder still instructs the model to present its plan by calling exit_plan_mode, whose schema is then never sent, so the sanctioned plan flow cannot complete. Exempt the three plan-mode lifecycle tools alongside structured_output (same synthetic- system-tool class the CORE_TOOLS docstring names; deny rules still apply). * docs(sdk): correct allowedTools registry-allowlist contract (#9827) The JSDoc added for QueryOptions.allowedTools (and the coreTools block) claimed the SDK allowedTools param activates the registry allowlist and hides unlisted built-in schemas. It does not: ProcessTransport maps it to the CLI --allowed-tools flag, and this PR's CLI wiring builds registryAllowList only from settings.permissions.allow. Reword both JSDoc blocks and the two hand-maintained SDK doc pages (sdk-typescript.md, sdk-typescript/README.md) to the shipped contract: allowedTools stays a pure auto-approval grant; only permissions.allow in settings.json (requires restart) activates the registry allowlist. * docs(settings): note plan-mode lifecycle exemption in the allowlist (#9827) The permissions.allow registry-allowlist exemption list named only MCP tools and the structured_output contract. Add the plan-mode lifecycle tools (exit_plan_mode / enter_plan_mode / ask_user_question) exempted in b8ba258c40 so the documented exemption set matches the gate. * fix(core): exempt the computer_use__* family from the registry allowlist (#9827) * fix(core): gate command-discovered tools through the registry allowlist (#9827) * fix(core): make registry-allowlist membership monotonic within the session (#9827) * fix(core): narrow the skill allowedTools grant contract to restart-scoped registration (#9827) * fix(core): count ask rules toward registry-allowlist membership (#9827) A tool covered only by a permissions.ask rule was silently deregistered whenever the permissions.allow registry allowlist was active: allow ["ReadFile"] + ask ["Shell"] hid the whole shell family from the model, so the documented "always require user confirmation" silently became "tool unavailable" and the ask rule could never fire. Ask rules express "this tool must stay usable, with confirmation", so they now count toward registry membership (frozen at startup for the same restart-scoped monotonicity as allow rules). * docs(settings): note that ask rules keep tools registered under allowlist (#9827) * test(cli): pin registry-allowlist strip in bare mode (#9827) The wiring tests only covered the safe-mode half of registryAllowList: bareMode || safeMode ? undefined : ... — a mutant dropping the bareMode guard survived the suite and would activate the allowlist from settings while bare mode strips those same rules from the merged allow set, leaving the bare registry's minimal toolset ungated. Mirror the safe-mode test for --bare. * fix(core): attribute registry-allowlist misses to permissions.allow (#9827) An allowlist-miss rejection surfaced as "Qwen Code requires permission to use X, but that permission was declined" citing a deny rule that does not exist (findMatchingDenyRule finds nothing) and never mentioning permissions.allow. When no deny rule matched and the registry allowlist is active, emit a distinct message pointing at the real config knob. * test(core): pin resolveToolName coverage of every ToolNames entry (#9827) TOOL_NAME_ALIASES hand-maintains the canonical/display-name mappings that tool-names.ts declares; nothing enforced the sync, so a tool added to tool-names.ts without an alias entry would compile, pass every test, and silently never match a permission rule — the exact #9827 bug class, now with higher stakes since a missed entry also breaks allowlist coverage. Walk every ToolNames/ToolDisplayNames pair and assert it round-trips through resolveToolName. * fix(core): expose isPermissionsAllowListActive on scoped PM shims (#9827) * fix(core): honour ask-only list_directory coverage in the opt-in gate (#9827) * docs: align registry-allowlist contract wording across docs and JSDoc (#9827) * docs: scope settings.md removal and whole-tool-deny claims precisely (#9827) * fix(core): count merged allow coverage in the list_directory opt-in gate (#9827) isLsToolEnabled() scanned only the settings-sourced getRegistryAllowList() for allow coverage while PermissionManager.isToolEnabled() counts the merged allow set (settings + --allowed-tools + SDK allowedTools + legacy tools.allowed). Under an active allowlist, list_directory covered only by a merged rule passed isToolEnabled but was never offered to registerLazy — it vanished from /tools and the model request while calls failed TOOL_NOT_REGISTERED. Count the merged allow set for coverage (activation still requires a valid settings rule) and filter empty/whitespace-only entries from activation exactly like PermissionManager.initialize's parseRules does, so a degenerate [""] entry cannot activate the gate here while the permission system reports the allowlist inactive. * test(core): pin activation source and merged-allow coverage of the list_directory gate (#9827) Every existing isLsToolEnabled test fed the identical array as both allow and registryAllowList, so the settings-only vs merged-allow distinction was unpinned and the R4-1 divergence shipped uncovered. Add three cases shaped like the CLI wiring: coverage by a merged (non-settings) allow rule under an active allowlist registers the tool; merged-only coverage with no settings rule does not activate the allowlist; an empty settings entry ([""]) does not activate it either. * fix(core): attribute scheduler denials to permissions.allow only for uncovered tools (#9827) The allowlist-miss message fired for any disabled tool with no matching deny rule while the allowlist is active — including tools rejected by the legacy coreTools gate that ARE covered by an allow rule, where 'not covered by any permissions.allow rule' is wrong and the remediation a no-op. Expose isCoveredByAllowOrAskRule on PermissionManager and take the allowlist branch only when the tool is genuinely uncovered; covered tools fall back to the generic declined message. The optional call keeps scoped PermissionManager shims (installed via 'as unknown as PermissionManager') from throwing until they grow the delegation. * test(core): pin the covered-tool fallback for scheduler denial messages (#9827) Add a scheduler-level case where the allowlist is active, no deny rule matches, and the disabled tool IS covered by an allow rule (the legacy coreTools gate shape): the message must be the generic declined one, not the permissions.allow attribution. Also make the existing allowlist-miss stub explicit about coverage. * fix(core): register request_shutdown in the permission rule alias map (#9827) Merging origin/main brought ToolNames.REQUEST_SHUTDOWN (#9806) but no TOOL_NAME_ALIASES entry, which the resolveToolName exhaustiveness test added on this branch pins. Map request_shutdown / RequestShutdown so permission rules can address the tool. * fix(core): guard the list_directory allowlist gate against non-string rules (#9827) isLsToolEnabled()'s activation check and coverage scan called raw.trim() / parseRule(raw) directly while PermissionManager.initialize computes the same thing through parseRules, whose r && r.trim() filter skips falsy entries. Settings load performs no element-type validation (the schema declares only type: array), so a stray null in settings.permissions.allow/ask — or in the legacy tools.allowed key riding the merged coverage set — threw TypeError during createToolRegistry and crashed startup while PermissionManager.initialize tolerated the same settings file. Mirror the parseRules guard with a typeof check in both the activation check and the coverage predicate, and pin both arms (tolerated entries still activate/cover, a [null]-only list keeps the gate closed). * fix(core): exempt task_stop from the permissions.allow registry gate (#9827) task_stop satisfies the PR's own two written exemption criteria but was missing from the set: it is shouldDefer=true (task-stop.ts), the exact deferred-schema property the computer_use__* exemption cites, and it is advertised to the model by a registered tool's copy — run_shell_command's schema says to use task_stop to stop a background command (and not to use broad process-name kills), and the background-promotion result instructs task_stop({ task_id }) verbatim. Under the reporter configuration the suite pins, run_shell_command stays listed while task_stop was gated out, so the sanctioned stop flow failed. Add the exemption and pin it next to the plan-mode exemption tests, including that a whole-tool deny rule still wins via the existing evaluate pass. * fix(core): keep shim denials on the pre-#9827 message when coverage is unknown (#9827) The optional isCoveredByAllowOrAskRule call's : true fallback routed shim-mediated rejections of COVERED tools into the new allowlist-attribution message, contradicting the comment above it ('they keep the pre-#9827 message meanwhile'). Both production shims (memory-scoped-agent-config.ts, skillReviewAgentPlanner.ts) Pick a partial interface without isCoveredByAllowOrAskRule, so for them the ternary always took the allowlist arm — telling the user a covered tool 'is not covered by any permissions.allow rule' when a different gate (e.g. the legacy coreTools allowlist) rejected it. Flip the fallback to false so unknown coverage stays on the pre-#9827 declined message, and update the shim test to pin that message instead of the allowlist one. * test(core): pin that ask-only rules never activate the allowlist (#9827) The suite pins ask rules counting toward allowlist membership, but nothing pins the complementary activation boundary: no test constructed a PermissionManager with only permissionsAsk (no permissionsAllow) and asserted the allowlist stays inactive. Current behavior is correct; this guards against a future edit folding ask rules into activation, which would turn an ask-only posture (permissions.ask: ["Shell"], no allow rules — a natural 'always confirm shell' config) into an active allowlist that deregisters every unlisted built-in. The nearest existing test ('no allow rules → allowlist inactive') uses no rules at all and would still pass. * fix(core): exempt tool_search from the permissions.allow registry gate (#9827) Under a narrow active allowlist, tool_search itself was gated out of the registry. Without ToolSearch, client.ts resolveDeferredToolsForReminder eagerly force-reveals every registered deferred tool (all mcp__* and the deferred computer_use__* family) into the eager model request, and preloadDeferredToolsWithinBudget early-returns — inverting the schema-shrink goal into maximal schema bloat for exactly the deferred families the other exemptions preserve for ToolSearch discoverability. Pre-#9827 tool_search always bypassed the legacy coreTools gate as a non-core tool. * test(core): pin the deny-rule arm's precedence in the scheduler permission message (#9827) The three-way message branch in CoreToolScheduler covers the allowlist-miss arm and the generic fallback arm, but every findMatchingDenyRule mock returned undefined, so the deny-rule arm — whose position FIRST in the if/else-if chain is what makes a real denial cite the matching rule instead of the allowlist attribution — had no scheduler-level coverage. Add two tests where findMatchingDenyRule returns a matching rule: one with the allowlist arm armed (active allowlist + uncovered tool) pinning the if/else-if ordering, one without an active allowlist pinning the deny arm over the generic declined fallback. Mutation-checked: disabling the deny arm fails both tests. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(core): pin deny/ask sibling semantics at the discovery gate (#9827) The discovery-gate test built its PermissionManager with EMPTY ask/deny lists, so the gate's two documented sibling semantics were unpinned: settings.md says a whole-tool deny rule removes a discovered tool from the registry even under an active allowlist, and an ask rule keeps a discovered tool registered ("always require confirmation" must never silently become "tool unavailable"). Add two discovery-gate tests with deny-covered and ask-covered PermissionManager configurations: the denied tool is also allow-covered so only the deny branch of isToolEnabled can reject it, and the ask test carries an uncovered control tool proving the gate is active in the same run. Mutation-checked: ignoring deny decisions fails the deny test only; dropping ask coverage from isCoveredByAllowOrAskRule fails the ask test only. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: match the allowlist activation wording to the real predicate (#9827) Four surfaces said the permissions.allow registry allowlist activates "when at least one allow rule is configured", but PermissionManager.initialize computes activation as at least one VALID rule from settings.permissions.allow only (getRegistryAllowList): a malformed entry never activates it, and auto-approval-only sources such as the --allowed-tools CLI flag / the SDK allowedTools parameter never do either. Reword settings.md, the SDK docs, the sdk-typescript README and the coreTools JSDoc to the exact predicate, and complete their exemption lists with task_stop and tool_search, which isToolEnabled exempts but the docs did not name. Docs-only. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
2dbe806204
|
docs(sdk): fix query timeout example signature (#9867)
Co-authored-by: zhangyu.34 <zhangyu.34@bytedance.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
80863c0858
|
feat(web-shell): show subagent sessions in detail panel (#7380)
* feat(web-shell): show subagent sessions in detail panel * fix(web-shell): harden subagent session details * chore(sdk): account for subagent client APIs * fix(core): keep subagent resume history valid * fix(subagents): stabilize detail session streaming * test(cli): update telemetry route count * fix(subagents): address review feedback * fix(subagents): harden transcript refresh state * fix(subagents): address maintainer review * fix(subagents): preserve compact status details * fix(subagents): address review feedback round 3 (#7380) * fix(subagents): address remaining detail review feedback --------- Co-authored-by: ytahdn <ytahdn@gmail.com> Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com> |
||
|
|
cf6c51396c
|
chore(docs,test): batch three small docs and test fixes (#7373)
* docs(sdk): document auto permission mode * docs(sdk): clarify auto permission mode * docs(action): fix GitHub Action input names * test(weixin): cover exported AES key parser --------- Co-authored-by: 俊良 <zzj542558@alibaba-inc.com> |
||
|
|
9b6da8e604
|
feat(webshell): replay ChatRecord history in readonly WebShell (#6999)
* feat(web-shell): add readonly transcript renderer * feat(transcript): project chat records to daemon transcript * chore: remove unrelated merge changes * fix(cli): align transcript replay test mock * fix(transcript): preserve replay metadata and todo context Keep vision disclosures and assistant usage visible across tool replay boundaries. Propagate plan tool-call identity through daemon projection and provide Todo contexts in WebShell so snapshots remain independent. Accept session_source records and cover the cross-layer behavior with regression tests. * docs(transcript): translate designs and remove benchmark table --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
41157205c1
|
fix(config): reject fractional session and tool-call limits (#6920)
* fix(config): reject fractional session and tool-call limits * fix(config): validate persisted session turn limits |
||
|
|
075c3f03e5
|
feat(cli): forward ask_user_question answers from SDK can_use_tool (#6655)
* feat(cli): forward ask_user_question answers from SDK can_use_tool SDK-hosted agents could receive ask_user_question calls through the can_use_tool callback and approve them, but the user's answers never reached the tool: the CLI called onConfirm(ProceedOnce) with no payload, so the tool read an empty answers map and the model never got the decisions. Route updatedInput.answers from the SDK's allow response into the tool confirmation payload so the collected answers reach the tool. Reuses the existing updatedInput channel — no new SDK API or types. Document the pattern in the TypeScript and Python SDK READMEs. * fix(cli): forward ask_user_question answers on teammate approval path Address review feedback on #6655: - handleTeammateApproval now mirrors the leader path and promotes the user's answers from updatedInput into the confirmation payload, so ask_user_question calls approved through a teammate no longer drop the user's choices (wenshao). - Extract a shared buildAllowConfirmationPayload helper used by both the leader and teammate paths, and only promote `answers` for ask_user_question so a same-named field on any other tool's input can't leak into the payload. - Add tests for the teammate path and the defensive guards (array updatedInput, array/null/empty answers, foreign answers field). * test(web-shell): stub Range client-rect methods to fix flaky CI CodeMirror's async measure pass (scheduled via requestAnimationFrame) calls getClientRects()/getBoundingClientRect() on a text Range. jsdom implements these on Element but not on Range, so the call throws "textRange(...).getClientRects is not a function" from a rAF callback after the test completed. Vitest surfaces it as an unhandled error and fails the whole run with exit code 1 even though every assertion passed (seen intermittently in useComposerCore.dom.test.tsx). Polyfill both methods on Range.prototype in the shared test setup, mirroring the existing ResizeObserver/scrollIntoView stubs. * refactor(cli): use ToolNames constant and broaden permission tests Address review suggestions on #6655: - buildAllowConfirmationPayload now gates answers-promotion on the ToolNames.ASK_USER_QUESTION constant instead of a bare string literal, so a future rename of the tool name is a compile-time break rather than a silent regression. - Add an it.each case for a non-object primitive updatedInput (string) to cover the `typeof updatedInput !== 'object'` guard branch. - Assert the leader path overrides toolCall.request.args with the host's sanitized updatedInput before confirming. - Add a teammate-path test for an allow response with no updatedInput, asserting respond is called with (ProceedOnce, undefined). --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
a234860a4a
|
fix(core): Align MCP OAuth guidance and docs (#5589)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* docs: Align docs with current CLI behavior Update stale documentation and user-facing MCP OAuth guidance to match the current dialog-based flows, SDK permission semantics, current links, and Qwen OAuth status. Also replace Ink internal imports with public Ink APIs for the shared text input so the workspace builds against Ink 7. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: fix BaseTextInput Ink import (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5589) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): surface MCP OAuth credential read failures Fix SSE OAuth credential pre-check failures by reporting token storage read errors before connecting. Update SDK coreTools docs and extension release link text from the follow-up review. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): harden MCP OAuth error handling Handle stderr warning failures as best-effort and keep SSE 401 OAuth guidance when credential re-read fails. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): keep SSE OAuth pre-read best effort Avoid blocking SSE MCP connections when the diagnostic credential pre-read fails, and cover BaseTextInput absolute-position edge cases. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): handle SSE OAuth validation errors Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): surface MCP OAuth recovery guidance Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): cover MCP OAuth retry paths Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): address OAuth guidance review Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
643a7d4198
|
fix(sdk): correct npm package name in SDK install instructions (#4860)
The unscoped 'qwen-code' package on npm is a copycat/squatted package published by an unofficial maintainer (liangshuai, v0.0.5). Users following the SDK README would install the wrong package. Change 'npm install -g qwen-code@^0.4.0' to the correct scoped 'npm install -g @qwen-code/qwen-code@latest'. Co-authored-by: 俊良 <zzj542558@alibaba-inc.com> |
||
|
|
ed14a33064
|
feat(core): add NotebookEdit tool for Jupyter notebooks
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
Adds NotebookEdit as the structured write counterpart to existing notebook read support. Summary: - Add `notebook_edit` for safe cell-level `.ipynb` replace/insert/delete operations. - Integrate notebook editing with tool registration, permissions, Claude conversion, prior-read enforcement, IDE/inline modify flow, commit attribution, docs, and SDK permission docs. - Harden notebook read/edit behavior for truncated notebook renders, ambiguous fallback cell IDs, internal modify metadata, compact JSON, UTF-8 BOM notebooks, and cache behavior after structural edits. - Add unit and integration coverage for notebook read/edit behavior. Follow-up work remains for tab-indented notebook formatting preservation, a few low-risk unit-test additions, and non-blocking hardening suggestions from review. |
||
|
|
80f1e266ba
|
feat(protocol): add typed daemon event schema v1 (#4217)
Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com> |
||
|
|
57de269f45
|
feat(sdk): add DaemonSessionClient skeleton (#4201)
* feat(sdk): add daemon session client * fix(sdk): harden daemon session event replay * fix(sdk): replay attach-time daemon events --------- Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com> |
||
|
|
cadda23782
|
chore(deps): upgrade ink 6.2.3 → 7.0.2 + bump Node engine to 22 (#3860)
* chore(deps): upgrade ink 6.2.3 -> 7.0.2 + bump Node engine to 22
ink 7 requires Node >=22 and react-reconciler 0.33 with React >=19.2,
so this PR also bumps:
- Node engines (root + cli + core) 20 -> 22
- React/react-dom 19.1 -> 19.2.4 (pinned exact via overrides to keep
the transitive React graph deduped to a single instance)
- @types/node pinned to 20.19.1 via overrides to avoid an unrelated
Dirent NonSharedBuffer regression in sessionService tests
- @vitest/eslint-plugin pinned to 1.3.4 to avoid an unrelated lint
regression introduced by the 1.6.x rule additions
- react-devtools-core 4.28 -> 6.1 (ink 7 peerOptional requires >=6.1.2)
- ink hoisted to root devDeps so workspace-private peer-dep contention
doesn't push ink-link/spinner/gradient into nested workspace
installs (which would skip transitive resolution for terminal-link)
Workflow + image + installer alignment:
- .nvmrc 20 -> 22
- Dockerfile node:20-slim -> node:22-slim
- CI test matrix drops 20.x (keeps 22.x + 24.x)
- terminal-bench workflow Node 20 -> 22
- Linux/Windows install scripts upgrade their Node version targets
Documentation alignment:
- README.md badge + prerequisites
- AGENTS.md, CONTRIBUTING.md, docs/users/quickstart.md,
docs/users/configuration/settings.md, docs/developers/contributing.md,
docs/developers/sdk-typescript.md, docs/users/extension/extension-releasing.md,
packages/sdk-typescript/README.md, packages/zed-extension/README.md,
scripts/installation/INSTALLATION_GUIDE.md
Test gating:
- Two AuthDialog/AskUserQuestionDialog tests that drive <SelectInput>
through ink-testing-library now race ink 7's frame-throttled input
delivery and land on the wrong option. The maintainers had already
marked one of them unreliable (skip on Win32 + CI+Node20). Extend
that gate to cover all environments until upstream
ink-testing-library ships an ink-7-compatible release that flushes
input deterministically. The other test now uses it.skip with the
same comment. No business code changes.
Verified locally:
- npm run typecheck across all workspaces: clean
- npm run lint (root): clean
- npm run test --workspaces:
cli 312/312 files, 4918 passed, 9 skipped
core 266/266 files, 6836 passed, 3 skipped
webui 6/6, 201 passed
sdk 40/40, 283 passed, 1 skipped
- npm ls ink: single ink@7.0.2 instance across all peer deps
- single react@19.2.4 instance
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* chore: align Node 22 floor across all shipping artifacts
Reviewer (tanzhenxin) flagged five surfaces where the >=22 engine bump
leaked: SDK package metadata, web-templates engines, /doctor runtime
check, main bundler target, and SDK bundler target. Each was a separate
escape hatch letting Node 18/20 consumers install or run the artifact
on an unsupported runtime.
- packages/sdk-typescript/package.json: engines.node >=18.0.0 -> >=22.0.0
- packages/web-templates/package.json: engines.node >=20 -> >=22
- packages/cli/src/utils/doctorChecks.ts: MIN_NODE_MAJOR 20 -> 22
- esbuild.config.js: target node20 -> node22 (main CLI bundle)
- packages/sdk-typescript/scripts/build.js: target node18 -> node22 (esm + cjs)
- packages/cli/src/utils/doctorChecks.test.ts: rename test label to v22+
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* ci(e2e): bump E2E workflow Node matrix to 22.x
Reviewer (tanzhenxin) flagged that e2e.yml still pinned node-version
20.x while root engines is now >=22, so every E2E run on push would
either fail at npm ci with engine error or silently exercise the bundle
on a runtime that's no longer in ci.yml's test matrix.
The macOS job in the same workflow already reads .nvmrc (which is 22)
so this only updates the Linux matrix.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(deps): drop root wrap-ansi override so ink 7 gets its declared dep
Reviewer (tanzhenxin) flagged that the root overrides.wrap-ansi: 9.0.2
predates this upgrade and forces every consumer (including ink) to v9,
while ink 7 declares wrap-ansi: ^10.0.0. The lockfile had no nested
install under node_modules/ink/, so ink 7 was running with a transitive
dep one major below its declared minimum.
Dropping the global override lets ink resolve its own wrap-ansi 10
nested install (now visible in the lockfile under
node_modules/ink/node_modules/wrap-ansi), while the cli package's own
direct `wrap-ansi: 9.0.2` dependency keeps the cli code path
(TableRenderer.tsx) on the version it has been tested against. The
nested cliui override is preserved for yargs which still needs v7.
Verified via `npm ls wrap-ansi`:
- ink@7.0.2 -> wrap-ansi@10.0.0 (newly nested)
- @qwen-code/qwen-code -> wrap-ansi@9.0.2 (unchanged)
- yargs/cliui -> wrap-ansi@7.0.0 (unchanged)
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(InputPrompt): un-skip placeholder ID reuse after deletion
Reviewer (tanzhenxin) flagged that the new it.skip on the
'should reuse placeholder ID after deletion' test was undisclosed in
the PR description and removed coverage of real product behavior
(freePlaceholderId / bracketed-paste backspace path) without a
TODO(#NNNN) link.
Their argument was sound: the skip rationale pointed at ink 7's input
throttle, but this same file just bumped the wait helper from 50ms to
150ms specifically to give ink 7 frame time. Re-running the test under
the bumped wait shows it passes reliably (5/5 runs in the full-file
context, 9/10 alone), so the skip was masking the throttle-flake that
the wait bump already addresses, not a real product bug.
Drop the it.skip and the now-stale comment so coverage of the
freePlaceholderId reuse logic is restored.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(InputPrompt): bump first prompt-suggestion test wait to 350ms
The "accepts and submits the prompt suggestion on Enter when the buffer
is empty" test is the first in its describe block, so it pays the
renderer cold-start cost. On macOS-22.x CI runners that pushes the
Enter → onSubmit microtask past the default 150ms post-Enter wait. Match
the 350ms initial render wait used immediately above to absorb the cold
start.
* Revert "test(InputPrompt): bump first prompt-suggestion test wait to 350ms"
This reverts commit 6add83b62ea80c551c81f54af1fda3e6e7478f55.
* test(InputPrompt): wait for followup suggestion debounce before pressing Enter
Root cause of the failing prompt-suggestion tests on macOS and Windows
CI is not flaky timing of the test post-Enter wait — it's the 300ms
debounce inside createFollowupController.setSuggestion (shared core).
The Enter handler reads followup.state.isVisible synchronously, so if
the debounce timer has not fired before stdin.write('\\r'), the
suggestion path is skipped and onSubmit never runs. No amount of
post-Enter wait can recover from that — the keypress was already
processed against stale state.
The original wait(350) only left ~50ms margin over the 300ms debounce,
which ink 7 / React 19.2 mount overhead consumed on slow Windows
runners. Bump the initial wait to 700ms (named SUGGESTION_VISIBLE_WAIT_MS)
to give the debounce timer + cold-start render a generous buffer.
Apply to the two sibling tests too — without the wait their "does not
accept" assertions pass trivially when suggestion is never visible,
which is a false green that hides regressions in the actual reject path.
* fix(deps): align cli wrap-ansi with ink 7 (9.0.2 -> ^10.0.0)
Ink 7 ships its own wrap-ansi@10. CLI's direct dep was pinned to 9.0.2,
causing two copies of wrap-ansi in node_modules and a potential drift in
CJK width / ANSI handling between ink's internal text wrapping and our
TableRenderer.
Upgrading the CLI's direct dep to ^10.0.0 lets npm dedupe to a single
wrap-ansi@10 used by both ink and TableRenderer. API surface is
identical; the only documented behaviour change is that tabs are
expanded to 8-column tab stops before wrapping, which TableRenderer
doesn't feed in.
TableRenderer test suite (43 tests) passes against wrap-ansi@10.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* chore(deps): document @types/node 20.x pin in overrides
The override pinning @types/node to 20.19.1 (while engines require
Node >=22) is intentional: bumping to @types/node@22.x re-introduces
a Dirent<NonSharedBuffer> type regression that breaks
@qwen-code/qwen-code-core/sessionService tests.
Add a sibling "//@types/node" note inside `overrides` so future
maintainers see the rationale and know when to revisit the pin
without having to dig through PR #3860 history.
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(AskUserQuestionDialog): link skipped Submit-tab test to tracking issue
The 'shows unanswered questions as (not answered) in Submit tab' test
was switched to `it.skip` in the ink 7 upgrade because
`ink-testing-library@4.0.0` doesn't flush input deterministically
through ink 7's 30fps throttle.
Add a `// TODO(#4036):` marker so the skip is greppable and can be
re-enabled once upstream ships an ink-7-compatible release.
Refs #4036
Generated with AI
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(deps): move @types/node pin comment out of overrides block
npm's `overrides` field requires every key to be a real package name —
the `"//@types/node"` comment-key added in 205855875 trips Arborist with
"Override without name" and breaks `npm ci` across all CI jobs.
Move the explanation to a sibling top-level `"//overrides"` key, which
npm ignores at the document root. Same documentation value, no
override-parser collateral damage.
---------
Co-authored-by: 秦奇 <gary.gq@alibaba-inc.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
||
|
|
43bb14ddc9 |
docs(sdk): enhance coreTools/excludeTools/allowedTools documentation with permissions reference
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
ee33a3c35e | feat: add system prompt customization options in SDK and CLI | ||
|
|
5d939fdb83 |
feat: add --session-id support for CLI and SDK
- Add --session-id flag to CLI for specifying custom session ID - Add sessionId option to SDK QueryOptions - Implement UUID validation for session IDs - Pass session ID from SDK to CLI via --session-id argument - Add integration tests for session-id functionality - Update unit tests for ProcessTransport Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
3354b56a05 | docs(sdk): update sdk docs | ||
|
|
bf6abf7752 | fix: update timeout settings and default logging level in SDK | ||
|
|
96d458fa8c |
chore: rename @qwen-code/sdk-typescript to @qwen-code/sdk
|
||
|
|
c218048551 | fix: prevent sending control request when query is closed | ||
|
|
64de3520b3 | docs: update README to include SDK-embedded MCP server details and usage examples | ||
|
|
322ce80e2c |
feat: implement SDK MCP server support and enhance control request handling
- Added new `SdkMcpController` to manage communication between CLI MCP clients and SDK MCP servers. - Introduced `createSdkMcpServer` function for creating SDK-embedded MCP servers. - Updated configuration options to support both external and SDK MCP servers. - Enhanced timeout settings for various SDK operations, including MCP requests. - Refactored existing control request handling to accommodate new SDK MCP server functionality. - Updated tests to cover new SDK MCP server features and ensure proper integration. |
||
|
|
81c8b3eaec | feat: add GitHub Actions workflow for SDK release automation |