* feat(core): make list_directory opt-in (disabled by default)
glob covers directory listing in most cases, so list_directory is now disabled by default to keep the tool surface lean. Enable it with tools.listDirectory.enabled=true or by listing it in the coreTools allowlist (--core-tools / tools.core). The plan-mode reminder no longer steers the model toward the tool.
* fix(core): align list_directory opt-in gate with prompts and allowlist parsing
Round-1 review findings on the opt-in gate:
- Normalise coreTools entries with parseRule so specifier forms such as
list_directory(/src) still re-enable the tool. isLsToolEnabled used
resolveToolName, which does not strip specifiers, while
PermissionManager admits the same entry via parseRule — so the
allowlist accepted the tool and the registry never got it (R1-1).
- Drop list_directory from the plan-mode block error. That message is
returned to the model as the tool result of every blocked call, so it
kept steering the model at an unregistered tool even after the system
prompt stopped doing so (R1-2).
- Remove the now-inert ToolNames.LS entries and the prompt text that
advertised the tool to built-in agents. The gate also guards the
forked-registry rebuild path, so these agents were silently stripped
of a tool their own prompts told them to use (R1-3).
- Say why the tool is missing and how to enable it in the tool-not-found
message, instead of offering unrelated Levenshtein suggestions (R1-6).
- Cover the alias and specifier forms in the registration tests (R1-4),
and drop the stale examples from the sub-agents and Java SDK docs,
where the listed entry can no longer register the tool (R1-5, R1-7).
* fix(core): address list_directory opt-in round-2 review findings
- Reword the skill-review agent task prompt so the inspection guidance
only references read_file; the run's tool filter has no
list_directory, glob, or shell, so the old `ls` instruction was
unsatisfiable for the turn-budgeted background agent.
- Resolve tool-name aliases (ListFiles, ListFilesTool, ReadFolder) in
the list_directory not-found message so aliased calls get the
enablement explanation instead of a Levenshtein suggestion.
- Attribute a missing list_directory to the workspace tools toggle when
the workspace disabledTools set blocks it, since the opt-in setting
cannot lift that state.
- Drop the coreTools allowlist advice from the enablement message:
setting tools.core to ["list_directory"] alone would exclude every
other tool.
- Switch the two remaining sdk-java runTransportOptionsExample copies
from list_directory to glob; setAllowedTools only adds auto-approve
rules and never registers a tool.
- Extract the shared fake-server scaffolding in the list_directory
integration tests into a local helper, keeping the load-bearing
CLI-flag comment.
- Pin the skill-review agent tools array and assert the learn-skill
prompt steers to read_file / glob, matching the sibling planners.
* fix(core): only claim list_directory is opt-in-disabled when it is unregistered
The not-found explanation resolved aliases (ListFiles, ReadFolder) before
checking whether the tool was actually absent. The registry is keyed by
canonical names while the lookup that reaches this path resolves legacy
migrations only, so an alias call missed even when list_directory was
enabled — and the message then told the user to switch on a setting that was
already on, hiding the generic path's "Did you mean list_directory"
self-correction.
Gate the branch on the canonical name being absent from the registry, and pin
the two alias combinations that were unpinned: an alias against a non-empty
workspace disabledTools set, and an alias against a registered tool.
* fix: add structured error code to SessionNotFoundError responses
PR #8864 retried session switches while the target session is closing,
but relied on fragile string matching against the daemon's error message.
This commit:
1. Adds a `code` property to `SessionNotFoundError` — automatically set
to `'session_closing'` when the extra message mentions "closing",
otherwise `'session_not_found'`.
2. Includes `code` in the HTTP JSON response body so clients can
distinguish closing (transient) from genuinely missing sessions
without depending on error message text.
3. Updates the WebUI retry check in `DaemonSessionProvider` to use
`errorBody.code === 'session_closing'` instead of matching
`endsWith('The session is closing; retry after close completes')`.
4. Fixes an inconsistent error message in `rewindSession` that used the
short `'The session is closing'` without the retry suffix.
Closes: #8864 (follow-up)
* fix(daemon): expose session closing code
* docs(serve): document session closing codes
* fix(acp): preserve closing code after restore waits
* fix: restore class pin in bridge test and update error taxonomy
- Add toBeInstanceOf(SessionNotFoundError) alongside toMatchObject
to preserve the envelope type assertion
- Document session_closing code in 18-error-taxonomy.md
* chore: drop unrelated merge formatting
* fix(serve): coordinate caller-supplied session IDs
Complete daemon-wide admission across REST, ACP, workspace generations, SDKs, and MCP.
Closes#8411
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(serve): wire session bridges in hot-reload harness
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): address review round for caller-supplied session IDs (#8415)
Restore the observability and fail-loud guarantees flagged in review:
log every session-id admission routing failure, name the live foreign
owner workspace in restore conflicts, make the ACP dispatcher's
admission dependency required so load/resume cannot run on a mount
without one, and require mountAcpHttp hosts to inject the daemon-wide
admission instead of silently building a weak fallback. Harden the SDK
WS transport against environments without global fetch and against
non-capabilities 200 envelopes, and align the design doc with the
implemented restore-sharing and persistence-failure semantics.
* fix(sdk): harden session ID capability fallback
Preserve REST capability errors, fail closed on malformed envelopes, retain restore routing diagnostics, and align retry documentation.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(serve): normalize restored session IDs
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(session): preserve mixed-case legacy session access
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
The teardown test DELETEs the session while a prompt is pending on a
permission request. There is a race between the daemon delivering a
cancelled terminal and the session being destroyed. Under CI load the
session can be destroyed first, producing an ERROR terminal instead of
COMPLETE/cancelled.
Accept both outcomes, matching the pattern already used by
cancelledPromptReceivesReliableTerminal for the same race.
slowSseLineBytesKeepIdleWatchdogAlive drove the event out in 20-byte steps
50ms apart against a 150ms idle timeout, so any observed gap above 150ms
tripped the watchdog. Each idle close restarts the whole event from the
handler, and the 3s observation budget only covered about three attempts, so a
few slow steps exhausted the reconnects. The test failed this way on macOS
runners repeatedly, most recently on main.
Send the event in 8-byte steps and raise the idle timeout to 500ms, which
keeps the property under test intact: the event now needs about a second of
50ms steps to arrive, so a watchdog fed only by whole frames still expires
well inside the run. Raise the observation timeout so a single slow step is
absorbed by a reconnect instead of ending the prompt.
Locally the old test starts failing once steps reach 260ms; the new one
survives 400ms steps and still fails, as it should, when SseReader stops
reporting per-byte activity.
A prompt reserves both a prompt slot and a stream-cleanup slot in
DaemonClient.submit, but releases them at different times: the prompt slot is
released synchronously in FutureTask.done(), immediately before the terminal
publication gate opens, while the stream-cleanup slot is only released once the
SSE stream has finished closing on the stream-close executor. The terminal path
in observe() closes that stream asynchronously and does not wait for it.
Both semaphores were sized to maximumConcurrentPrompts, so a caller chaining
prompts off completionFuture() at full capacity races the previous prompt's
close: when the close had not finished by the time the terminal was published,
startPrompt failed with DaemonClientCapacityException("Stream cleanup capacity
is exhausted"). This is how the documented chaining pattern behaves under load,
and it made terminalContinuationCanStartNextPromptAtClientCapacity flaky in CI.
Size the stream-cleanup semaphore to allow one draining cleanup per prompt
slot, which is exactly the overlap the release ordering can produce. Admission
backpressure is unchanged in kind: cleanups that stay stalled beyond that
headroom still fail fast, now after two generations instead of one.
The stream-close executor queue is derived from the same capacity, so it still
absorbs every reservation without rejecting work.
The constant was defined as new Timeout(60L, TimeUnit.MINUTES) in both
the client and qwencode modules, i.e. 60 minutes, contradicting its name
and Javadoc ("A timeout of 30 minutes."). It is the fallback turn timeout
in ProcessTransport and the timeout used by QwenCodeCli.simpleQuery, so
callers waited twice the documented default. The 30-minute intent is
corroborated by TransportOptionsAdapter.DEFAULT_TURN_TIMEOUT (30 min).
Change the literal to 30L in both modules.
The two catch blocks in AcpClient's constructor threw a new
AgentInitializeException with only a message, discarding the caught
IOException/ExecutionException/InterruptedException/TimeoutException.
AgentInitializeException already has a (String, Throwable) constructor,
so pass the caught exception as the cause to keep the root failure and
its stack trace in the exception chain.
* feat(cli): implement non-interactive /context output and diagnostic
- Extract collectContextData() from contextCommand.ts for shared usage.
- Register /context in ALLOWED_BUILTIN_COMMANDS_NON_INTERACTIVE.
- Extend SDK control protocol with GET_CONTEXT_USAGE request.
- Implement handleGetContextUsage in SystemController for programmatic token queries.
- Expose getContextUsage() method in the TypeScript SDK Query interface.
* fix: address review feedback and fix critical bugs in context usage feature
- Add missing `get_context_usage` route in ControlDispatcher (SDK calls would throw)
- Fix `executionMode` defaulting: use `?? 'interactive'` to match other commands
- Validate dynamic import of `collectContextData` before invoking
- Preserve original error message in handleGetContextUsage catch block
- Add ControlDispatcher test for get_context_usage routing
- Add JSDoc comment for context command in non-interactive allowlist
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: re-check abort signal after async operations in handleGetContextUsage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add getContextUsage() to SDK TypeScript documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: clarify getContextUsage showDetails is a display hint, not a data filter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: make showDetails affect response shape, add getContextUsage test
- When showDetails is false, return empty detail arrays instead of full
data so /context and /context detail produce different payloads
- Add unit test for Query.getContextUsage() covering request payload
and response handling
* fix: strip UI type from SDK response, sync Java SDK protocol
- Remove leaked `type: 'context_usage'` from control response payload
- Add GET_CONTEXT_USAGE to Java SDK protocol mirror (enum, interface,
union type)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Refactor subagent model configuration from nested modelConfig object to a simple model string field for better UX and clarity.
Changes:
- Replace modelConfig object with model string in SubagentConfig interface
- Add model-selection.ts utility for parsing and validating model selectors
- Support 'inherit' keyword and bare model IDs (e.g., 'glm-5', 'claude-sonnet-4-6')
- Maintain backward compatibility by parsing legacy modelConfig frontmatter
- Update validation to reject cross-provider authType-prefixed selectors
- Update SDK types (TypeScript and Java) to reflect new schema
- Add comprehensive tests for model selection and validation
- Update documentation with model selection examples
Breaking changes:
- modelConfig.frontmatter field deprecated in favor of model field
- Cross-provider model selectors (e.g., 'openai:gpt-4') not supported for subagents
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>