mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-26 09:04:37 +00:00
* feat(core): declare create_sub_session only under qwen serve create_sub_session needs the daemon bridge, which only exists under `qwen serve`, yet it was declared in every session. Interactive TUI and headless runs therefore carried a tool that can never succeed, polluting the model's action space and ToolSearch results. The tool is now registered by the ACP session at the same point it wires the sub-session spawner, so it exists exactly where it can work and nowhere else. * fix(core): keep create_sub_session on registries built with a wired spawner Dropping the unconditional registration also dropped the tool from every registry rebuilt after the daemon session starts: sub-agent and override registries are built through createToolRegistry with forSubAgent, and copyDiscoveredToolsFrom carries discovered tools only, never built-ins. Daemon sub-agents therefore lost the capability silently. Restore the lazy registration but gate it on a sub-session spawner being wired onto the Config, so interactive, headless and SDK runs still do not advertise a tool that cannot work there, while daemon sub-agent and override configs pick it up through prototype delegation. Going back through the lazy path also restores the PermissionManager.isToolEnabled gate for these registries. Harden the negative test to assert on both registration entry points; a regression that re-adds the tool eagerly never touches registerFactory, so the previous assertion would have stayed green. Add a positive test covering a subagent registry rebuilt after the spawner is wired. * docs(core): align setSubSessionSpawner doc with the new gate The setter's JSDoc still described the pre-PR behaviour — that leaving the spawner unset makes the tool report itself as daemon-only. With the spawner-gated registration the tool is never registered in interactive TUI or headless, so nothing reports anything there, and the comment contradicted the three sibling doc sites this PR already updated. * fix(cli): permission-gate the daemon create_sub_session registration The eager registration in the Session constructor called ToolRegistry.registerTool() directly, which honors only `tools.disabled` — so a daemon whose operator restricts `tools.core` or denies the whole tool still advertised create_sub_session and failed every call with EXECUTION_DENIED, exactly the "declared but unusable" pollution this change set out to remove. Registration now lives in an awaited helper that applies the same PermissionManager.isToolEnabled() check the core-side gate in createToolRegistry applies, and the daemon calls it once per session it creates, after the Session has wired the spawner and before the session is published. Also drops the unused CreateSubSessionParams public export. * fix(cli): declare create_sub_session only on daemon-backed sessions, revealed to the model Address the R4 review findings: - Wire the sub-session spawner only when the daemon's QWEN_CODE_SERVE=1 stamp is present. A standalone --acp session's peer is the editor, which answers the bridge's qwen/control/* ext methods with JSON-RPC -32601, so the tool was declared there but could never run. Gate registerCreateSubSessionTool on the spawner being wired so the tool exists exactly where it can execute. - Reveal the deferred tool and refresh the declaration snapshot after registering: the registration lands after startChat() froze the chat's declarations, so without the reveal the model was never offered the tool for the session's first lifetime. - Pin that newSession awaits the registration before the session is served, so the first prompt's declarations always include the tool. * fix(core): pin the create_sub_session reveal across /clear resets The reveal applied at daemon-session creation was permanently lost by the first /clear whenever the deferred-tool startup preload did not fit its all-or-nothing schema budget (or was disabled by a <= 0 / non-finite operator threshold): resetChat() clears the revealed set, the preload restores nothing, and registerCreateSubSessionTool never re-runs — the tool silently dropped out of the declaration list for the rest of the session. Add ToolRegistry.pinDeferredToolReveal(): pinned reveals are session-setup state (not ToolSearch discovery) and are re-applied by clearRevealedDeferredTools() while the tool stays registered and deferred, so the fresh session's startChat -> setTools() re-declares it. Pin create_sub_session at registration. * test(cli): pin create_sub_session registration on the permission-manager-enabled path * docs(core): correct DAEMON_ONLY_MESSAGE reachability in create-sub-session header Per wenshao's runtime verification (N2): the guard is reachable only for a daemon session whose spawner was cleared mid-flight; in non-daemon sessions the tool is absent from the registry, so a stale direct call hits the registry-miss error before execute() is reached. |
||
|---|---|---|
| .. | ||
| approval-mode.ts | ||
| clearContextDefaults.ts | ||
| config-session-env.test.ts | ||
| config.safe-mode.test.ts | ||
| config.test.ts | ||
| config.ts | ||
| config.workflow-registration.test.ts | ||
| config.workflows.test.ts | ||
| constants.ts | ||
| models.ts | ||
| storage.test.ts | ||
| storage.ts | ||