mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-11 04:02:45 -10:00
feat/btw-qodercli-alignment
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3319e819b9 |
fix(sdk-python): resolve Windows command shims on the effective PATH (#13862)
* fix(sdk-python): resolve Windows command shims on the effective PATH * style(sdk-python): format Windows shim regression assertion * fix(sdk-python): secure Windows command lookup and cleanup * style(sdk-python): align test import grouping with CI --------- Co-authored-by: 易良 <[email protected]> |
||
|
|
09411aa0a9 |
fix(cli): Harden settings failures and sandbox command streams (#13140)
* fix(cli): Harden settings failures and sandbox command streams * ci: Quote the native stdin verification step * test(cli): Verify native sandbox and settings boundaries * test(cli): Check complete policy during Windows settings saves * fix(cli): Diagnose invalid verification temp roots before probing Check private stdin pipe ownership on Linux rather than requiring proc reopening to fail. Refs #12417. * fix(cli): address settings and sandbox review feedback (#13140) * fix(cli): recover read-only workspace settings across restarts (#13140) * fix(cli): preserve readonly recovery and SDK sandbox arguments * fix: protect settings recovery and sandbox streams Refs #12417 * test(cli): fix Windows workspace recovery fixtures Resolve the four recovery fixtures consistently and require the intended preservation failure instead of accepting an earlier canonical-path error. Refs #12417 |
||
|
|
85ae860cce |
feat(core): add OpenAI Responses API content generator (#8169)
* feat(core): add OpenAI Responses API content generator Adds an HTTP/SSE-only content generator for OpenAI's Responses API (/v1/responses), selected via AuthType.USE_OPENAI_RESPONSES. Reasoning continuity round-trips through part.thoughtSignature, matching the existing Anthropic thinking-signature mechanism, so compaction and history consolidation work unchanged. Closes #889 * fix(cli): add openai-responses to AUTH_PREFLIGHT_ENV_KEYS AUTH_PREFLIGHT_ENV_KEYS didn't have an entry for the new AuthType.USE_OPENAI_RESPONSES ('openai-responses'), and it isn't in AUTH_PREFLIGHT_WAIVED_AUTH_TYPES either, so the drift-detection test (AUTH_PREFLIGHT_AUDITED_AUTH_TYPES covers every AuthType) failed with expect(uncovered).toEqual([]). OPENAI_API_KEY is the correct env var, matching the existing mapping for this auth type in packages/cli/src/config/auth.ts:23 ([AuthType.USE_OPENAI_RESPONSES]: 'OPENAI_API_KEY') and packages/core/src/models/constants.ts's AUTH_ENV_MAPPINGS. Verification: authPreflight.test.ts 3/3 passing (was failing before this fix), tsc --noEmit clean, eslint clean. * fix(openai-responses): address review findings on the streaming pipeline and converter Addresses the automated /review findings (doudouOUC, qwen-code-ci-bot) left on #8169 after the AUTH_PREFLIGHT_ENV_KEYS fix. Critical fixes in responses-pipeline.ts's streamRequest: - baseUrl fallback used `??`, which does not catch an empty string -- USE_OPENAI_RESPONSES has no case in ModelRegistry.getDefaultBaseUrl, so an unconfigured baseUrl resolves to '' and every request hit the relative path /v1/responses. Switched to `||`. - streamRequest called the global `fetch` instead of the `fetch` buildRuntimeFetchOptions pins alongside its dispatcher. Node's built-in undici can be a different major version than the bundled one, and handing it a foreign dispatcher throws `invalid onError method` -- this broke every streaming request whenever a dispatcher was installed (which is the common case, proxy or not). - Network errors from fetch() and non-ok HTTP responses were thrown unredacted, leaking proxy credentials into error messages/logs. - No Content-Type validation: an HTTP 200 non-SSE body (a gateway/proxy block page, or a baseUrl pointed at a non-API endpoint) silently yielded zero events instead of a surfaced, retryable failure. - The SSE parser only flushed a pending event on a blank line and never after the read loop ended, so a stream that closes without a trailing blank line (a proxy stripping trailing whitespace, or an interrupted connection) silently dropped its last event -- including the terminal response.completed event carrying usageMetadata/finishReason. Critical fixes in index.ts's embedContent: - Missing redactProxyError on the embedding error path (present on the sibling openaiContentGenerator and on anthropicContentGenerator). - The embed client passed baseUrl to the OpenAI SDK unnormalized; this generator's own /v1-less baseUrl convention (the streaming pipeline strips/re-appends /v1 itself) 404s every embedding call against the SDK, which does not auto-append /v1 to a custom baseURL. Normalized the same way the streaming pipeline does. Suggestions fixed in responses-converter.ts: - `buf?.args ?? fc.arguments`: initFunctionCall seeds buf.args to '', so a buffer that received output_item.added but no function_call_arguments.delta events never fell through to the done item's own complete arguments. Switched to `||`. - A user turn with text + image parts was split into two separate 'message' items; the Responses API treats them as two distinct turns, so the model could fail to associate a question with its image. Parts now accumulate per-turn and flush as one message with a multi-part content array, in original relative order around any function_call/ function_call_output/reasoning items. - Non-image inlineData and all fileData references were silently dropped with no diagnostic. They now become a text placeholder noting the unsupported attachment (no input_file part type is wired up for this generator yet) instead of vanishing. - Tool results were sent as the raw { output } / { error } JSON envelope instead of the unwrapped string the sibling Chat Completions converter sends, double-encoding and quote-escaping any tool output that is itself JSON. - Removed ResponsesStreamState.reset(), an unused method contradicting the documented one-instance-per-stream contract. Other: - Extracted the embedContent text-extraction logic (byte-identical between this generator and the sibling openaiContentGenerator) into a shared extract-text-from-contents.ts helper. - Added test coverage: multi-part message merging, attachment placeholders, tool-result envelope unwrapping, the buf.args empty-buffer fallback, sanitizePromptCacheKey's truncation/hash path, the data-only SSE framing branch (the shape the Responses API actually emits, previously untested), and the USE_OPENAI_RESPONSES factory route in createContentGenerator. - responses-pipeline.test.ts previously stubbed the global fetch, which the dispatcher/fetch fix above now bypasses (the pipeline calls the pinned fetch instead); switched to mocking buildRuntimeFetchOptions directly so the mock intercepts the fetch the pipeline actually uses. * fix(openai-responses): close remaining review gaps from PR #8169 Re-audited every open review thread on PR #8169 against the current code (commit |
||
|
|
eaa2f5695f |
fix(sdk-python): repair import ordering to fix Ruff CI (#10496) (#10505)
Co-authored-by: 易良 <[email protected]> |
||
|
|
49f946b44c | fix(sdk-python): support large stream-json messages (#9337) | ||
|
|
27285a5243 |
refactor: centralize approval mode contracts (#9796)
* refactor: centralize approval mode contracts * fix: align Python SDK import grouping * test: restore approval mode exports in CLI mocks * fix: close approval mode drift gaps * test(cli): preserve core exports in serve mocks * fix: close approval mode review gaps * test(cli): complete permission request fixture * test(sdk): match approval mode route * test(approval): close review coverage gaps * test(sdk): cover approval mode global scope path |
||
|
|
867ded5bd5 |
fix(sdk): support "auto" permission mode (#9003)
* fix(sdk-python): support "auto" permission mode * refactor(sdk-python): derive permission-mode validation from PermissionMode type * fix(sdk-java): support "auto" permission mode * refactor(sdk-python): derive auth-type and effort validation from type aliases * chore: rerun CI |
||
|
|
ac78acd3c5 |
fix(core): resolve Qwen 3.8 reasoning budget conflicts (#8525)
* fix(core): resolve Qwen 3.8 reasoning budget conflicts
* fix(core): cover unconfigured Qwen 3.8 conflicts
* chore: preserve latest main formatting
* fix(core): harden DashScope thinking precedence
* fix: honor DashScope thinking knob precedence
* test(core): assert same-layer thinking knob drop warning for request pairs (#8525)
* fix: align effort override reporting with wire resolution
* fix: resolve thinking knob review findings
* fix: sort Python SDK test imports
* fix(core): ignore null thinking knobs
* fix(core): register enable_thinking true in thinking knob selection (#8525)
selectFromLayer only registered enable_thinking === false, so a
higher-priority enable_thinking: true was invisible to cross-layer
resolution: a lower-priority samplingParams disable won selection and
rewrote the shipping tier to reasoning_effort 'none', inverting the
documented extra_body > samplingParams precedence. Register the
on-switch as the weakest knob in its own layer (an off-switch rewrites
the tier, an on-switch never does) and make the drop branch
value-aware: true keeps the shipping tier and drops only the redundant
knobs, false keeps the canonical 'none' disable.
getReasoningEffortOverride no longer reports an on-switch as shadowing
the tier (the wire drops the switch and ships the tier), except for a
request-level effort override that still shadows from under it.
Also corrects the dropConflictingThinkingKnobs contract comment (only
effort tiers ship alone; the 'none' disable and a winning budget keep
a co-present enable_thinking) and the model-providers.md precedence
callout, which overstated samplingParams precedence for older qwen
hybrids where the reasoning-derived enable_thinking: true overrides it.
* fix(core): preserve budget beneath thinking on-switch
* fix(core): canonicalize disabled thinking knobs
* fix: resolve round-6 thinking knob review findings (#8525)
Co-authored-by: Qwen-Coder <[email protected]>
* fix(external-context): read the response body with a reader, not for-await (#8525)
Port of #8764 (
|
||
|
|
c5dddb5e5d |
fix(sdk-python): validate max_tool_calls and max_subagent_depth as integers (#7548)
* fix(sdk-python): validate max_tool_calls and max_subagent_depth as integers max_session_turns rejects bools and non-ints before its range check, but the two sibling limits only range-checked, so values their own error messages rule out were accepted and stringified onto the CLI: max_tool_calls=True -> --max-tool-calls True max_tool_calls=2.5 -> --max-tool-calls 2.5 max_subagent_depth=True -> --max-subagent-depth True bool is the sharp edge: it subclasses int, so True passes both isinstance and the range comparison as 1. Extract the bool-aware check the max_session_turns branch already performed into _is_int and apply it to all three. max_session_turns keeps its exact previous behavior; the expression is the same, only named. * fix(sdk-python): say integer in the max_subagent_depth range message A float like 2.5 is between 1 and 100, so the old message described a constraint the value already satisfied and gave no hint that the type was the problem. --------- Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
5176ed0fa3 |
fix(sdk-python): require canonical form in validate_session_id (#7532)
uuid.UUID() accepts several non-canonical spellings — braced
{...}, urn:uuid:..., and dash-less hex — so validate_session_id let them
through after the RFC 4122 variant check. The value is then forwarded to
the CLI verbatim as --session-id/--resume, producing a malformed session
id downstream rather than a clear error at the SDK boundary.
Reject anything whose canonical form differs from the input. Case is
deliberately not part of the comparison: UUID() lowercases, and an
all-uppercase spelling is still valid canonical input.
Co-authored-by: Shaojin Wen <[email protected]>
|
||
|
|
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 |
||
|
|
85c1c0741c |
feat(sdk): expose transport and query options in both SDKs (#6491)
* feat(sdk): expose transport and query options in both SDKs
Consolidated PR covering pure SDK-side option additions:
- fork_session (--fork-session)
- max_tool_calls (--max-tool-calls)
- max_subagent_depth (--max-subagent-depth)
- agents (via initialize control request)
- include_directories (--include-directories)
- extra_args (pass-through CLI flags)
- extensions (--extensions)
- allowed_mcp_server_names (--allowed-mcp-server-names)
- mcp_servers (Python SDK, via initialize control request)
- fallback_model (--fallback-model, max 3)
- proxy (--proxy, deprecated)
- sandbox (--sandbox)
- safe_mode (--safe-mode)
- insecure (--insecure)
- worktree (--worktree)
- disabled_slash_commands (--disabled-slash-commands)
All options implemented in both Python SDK and TypeScript SDK
with validation and unit tests.
* fix(sdk): expand extraArgs blocklist and add TS SDK tests
- Expand reserved CLI flags blocklist from 3 to 34 flags, covering all
SDK-managed options and security-sensitive flags (--model, --auth-type,
--approval-mode, --insecure, --dangerously-skip-permissions, etc.)
- Add Zod refine validation for extraArgs in TS SDK (previously no validation)
- Update extraArgs JSDoc to document security implications
- Add 12 ProcessTransport tests for new CLI argument building
- Add queryOptionsSchema.test.ts with 20 validation tests
- Add createQuery.test.ts option passthrough test for all new fields
- Add Python parametrized tests for expanded blocklist
* fix(sdk): address review feedback for consolidated options
Security fixes:
- Fix --flag=value bypass: split on = before checking reserved flags
- Add missing dangerous flags: --yolo/-y, --openai-base-url, --openai-api-key,
--mcp-config, --prompt, --add-dir, --input-file, --json-schema/fd/file
- Remove ghost flags (--dangerously-skip-permissions, --allow-dangerously-skip-permissions)
Bug fixes:
- Fix Python mcp_servers key: snake_case -> camelCase (mcpServers)
- Remove duplicate agents declarations in Zod schema and types.ts
- Add maxToolCalls range validation (.int().min(-1)) in Zod schema
- Fix agents validation cross-SDK: reject empty strings in TS (matching Python)
Tests:
- Add --flag=value bypass tests (both SDKs)
- Add tests for new dangerous flags
- Add maxToolCalls range validation tests
- Add agents empty-string rejection test
* fix(sdk): add short flag aliases, fix zod validator, add forkSession validation
- Add short flag aliases (-m, -p, -i, -s, -e, -o, -c, -r) to reserved
CLI flags blocklist in both Python and TS SDKs to prevent blocklist
bypass via short flags
- Fix z.custom validator for agents: move error message from && chain
to 2nd argument so Zod produces the descriptive error on failure
- Fix TS2345: coerce split('=')[0] with ?? '' for noUncheckedIndexedAccess
- Add forkSession prerequisite validation: requires resume to be set,
matching CLI behavior that rejects --fork-session without --resume
- Remove dead agents field from TransportOptions (agents flow through
initialize payload, not transport CLI args)
- Add tests for short flags, forkSession validation in both SDKs
* fix(sdk): add --no-* negation flags, comma validation, fork session ID fix
- Add --no-sandbox, --no-safe-mode, --no-insecure, --no-worktree,
--sandbox-image, --sandbox-session-id to reserved CLI flags blocklist
in both SDKs to prevent yargs boolean negation bypass
- Add per-element comma validation for comma-joined list fields
(includeDirectories, extensions, allowedMcpServerNames,
fallbackModel, disabledSlashCommands) to prevent CLI comma-split
injection
- Add min(1) validation for extraArgs items to reject empty strings
- Fix fork_session validation to also accept continue_session (not just
resume), matching CLI behavior
- Allow session_id with resume when fork_session is True
- Fix fork session ID mismatch: generate new UUID for forked session
instead of reusing resume (source) session ID, so getSessionId()
returns the correct forked session ID
* fix(sdk): add fork session ID test assertions and mcp_servers validation
- Add assertions to forkSession test verifying sessionId is a new UUID
different from the resume value
- Add test for forkSession with explicit sessionId
- Add structural validation for mcp_servers in Python SDK to reject
non-mapping configs before sending to CLI
* fix: fork session ID discarded by Query constructor
Query.ts:97 used `options.resume ?? options.sessionId` which always
picked resume when forkSession was true, ignoring the new fork UUID
generated in createQuery.ts. Now uses fork UUID when forkSession is true.
Python SDK: query() now generates a fresh UUID for fork_session instead
of reusing the resume (source) session ID. _session_id_locked is False
for fork sessions, allowing the CLI to correct the session ID via
control responses.
* fix: mypy type error in fork_session session_id annotation
Add explicit `str | None` type annotation to session_id variable
to resolve mypy error where fork branch inferred `str` but else
branch assigns `str | None`.
* test: address review suggestions for test coverage and validation
- Add comma validation negative tests for all 5 list fields (both SDKs)
- Add maxSubagentDepth boundary tests (0, 101, 1, 100) for TS SDK
- Add from_mapping tests for all new fields and default values (Python)
- Add ProcessTransport negative test verifying flags absent when unset
- Remove --no-worktree dead code from RESERVED_CLI_FLAGS (both SDKs)
- Add --fork-session and other new flags to reserved-flags test list
- Add continue field to TS schema to match TransportOptions type
- Fix forkSession refine to accept resume OR continue (matching Python)
---------
Co-authored-by: qwen-code-dev-bot <[email protected]>
|
||
|
|
8e6a572562 |
feat(sdk): add control request methods for effort, models, usage, context (#6492)
* feat(sdk): add control request methods for effort, models, usage, context Add 4 control request methods across CLI, Python SDK, and TypeScript SDK: - set_effort: Set reasoning effort tier (low/medium/high/xhigh/max) at runtime via config.setReasoningEffort(), also accept initial effort in initialize payload - get_available_models: Return models available for current auth type via config.getAvailableModels() - get_usage_info: Return usage dashboard data via loadUsageDashboard() with optional range filter (today/week/month/all) - get_context_usage: Add Python SDK method (TS SDK already has it) CLI: Add request types, dispatcher routing, and SystemController handlers with capability flags (can_set_effort, can_get_available_models, can_get_usage_info). TS SDK: Add ControlRequestType enum values, protocol interfaces, QueryOptions.effort field, Zod schema, and Query methods. Python SDK: Add Effort type, validation, protocol TypedDicts, and Query methods. * fix(sdk): address CI feedback for control request methods - Fix TransportOptions.effort type to literal union instead of string - Fix Python CLIControlGetUsageInfoRequest.range to Literal type - Remove redundant signal.aborted checks in handleGetUsageInfo - Add Python SDK tests for set_effort, get_available_models, get_context_usage, get_usage_info, effort in initialize - Add TS SDK tests for setEffort, getAvailableModels, getUsageInfo * fix(sdk): address review feedback for control request methods - Remove dead effort field from TransportOptions (flows via QueryOptions) - Throw on invalid effort in handleInitialize (matching handleSetEffort) - Wrap setReasoningEffort in try/catch (matching addMcpServers pattern) - Sanitize get_available_models response to exclude baseUrl/envKey - Rename Python get_usage_info(range=) to time_range= to avoid shadowing built-in - Use Effort type alias and Literal type in Python set_effort/get_usage_info - Make set_effort/setEffort return None/void to match set_model/setModel - Add TS test for effort in initialize payload * fix(cli): add effort read-back check and CLI-level unit tests - Add read-back check after setReasoningEffort() in both handleSetEffort and handleInitialize to detect silent no-op when thinking is disabled - Return applied flag in set_effort response so SDK consumers can detect when effort was not applied - Remove user input from error messages to match handleSetModel convention - Add unit tests for set_effort, get_available_models, get_usage_info, and initialize with effort handlers * fix(sdk): surface applied flag from setEffort, move effort validation before side effects - Move effort validation in handleInitialize before MCP servers and subagents processing to prevent config-mutating side effects if effort is invalid - Return applied boolean from setEffort/set_effort so SDK consumers can detect when effort was a no-op (thinking disabled) - Fix spread order in handleGetUsageInfo to prevent subtype overwrite - Update JSDoc to reflect actual behavior |
||
|
|
e384338145 |
feat(SDK) Add Python SDK implementation for #3010 (#3494)
* Codex worktree snapshot: startup-cleanup Co-authored-by: Codex * Add Python SDK real smoke test Adds a repository-only real E2E smoke script for the Python SDK, plus npm and developer documentation entry points. Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): address review findings — bugs, type safety, and test coverage - Fix prepare_spawn_info: JS files now use "node" instead of sys.executable - Fix protocol.py: correct total=False misuse on 7 TypedDicts (required fields were optional) - Fix query.py: add _closed guard in _ensure_started, suppress exceptions in close() - Fix sync_query.py: prevent close() deadlock, add context manager, add timeouts - Fix transport.py: handle malformed JSON lines, add _closed guard in start() - Fix validation.py: use uuid.RFC_4122 instead of magic UUID - Fix __init__.py: export TextBlock, widen query_sync signature - Remove dead code: ensure_not_aborted, write_json_line, _thread_error - Add 12 new tests (29 → 41): context managers, JSON skip, closed guards, spawn info, timeouts Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): address wenshao review — session_id, bool validation, debug stderr - Fix continue_session=True generating a wrong random session_id - Add _as_optional_bool helper for strict type validation on bool fields - Default debug stderr to sys.stderr when no custom callback is provided Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): address remaining wenshao review feedback Co-authored-by: Qwen-Coder <[email protected]> * test(cli): harden settings dialog restart prompt test Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): review fixes — UUID compat, stderr fallback, sync cleanup - Remove UUID version restriction to support v6/v7/v8 (RFC 9562) - Always write to sys.stderr when stderr callback raises (was silent when debug=False) - Prevent duplicate _STOP sentinel in SyncQuery.close() via _stop_sent flag - Add ruff format --check to CI workflow - Fix smoke_real.py version guard: fail early before imports instead of NameError - Apply ruff format to existing files Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): remaining review fixes — exit_code attr, guard strictness, sync timeout - Add exit_code attribute to ProcessExitError for programmatic access - Strengthen is_control_response/is_control_cancel guards to require payload fields, preventing misrouting of malformed messages - Expose control_request_timeout property on Query so SyncQuery uses the configured timeout instead of a hardcoded 30s default - Use dataclasses.replace() instead of direct mutation on frozen-style QueryOptions in query() factory - Add ResourceWarning in SyncQuery.__del__ when not properly closed Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): add exit_code default and guard __del__ against partial GC - Give ProcessExitError.exit_code a default value (-1) so user code can construct the exception with just a message string - Wrap SyncQuery.__del__ in try/except AttributeError to prevent crashes when the object is partially garbage-collected Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): review fixes — resource leak, type safety, CI matrix, docs - Fix SyncQuery.__del__ to call close() on GC instead of only warning - Replace hasattr duck-type check with isinstance(prompt, AsyncIterable) - Type-validate permission_mode/auth_type in QueryOptions.from_mapping - Use TypeGuard return types on all is_sdk_*/is_control_* predicates - Add 5s margin to sync wrapper timeouts to prevent error type masking - Expand CI matrix to test Python 3.10, 3.11, 3.12 - Change ProcessExitError.exit_code default from -1 to None - Add stderr to docs QueryOptions listing - Update README sync example to use context manager pattern Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): preserve iterator exhaustion state and suppress detached task warning - Add _exhausted flag to Query.__anext__ and SyncQuery.__next__ so repeated iteration after end-of-stream raises Stop(Async)Iteration instead of blocking forever. - Remove re-raise in _initialize() to prevent asyncio "Task exception was never retrieved" warning on detached tasks; the error is already surfaced via _finish_with_error(). Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): reject mcp_servers at validation time and add iterator/init tests - Reject mcp_servers in validate_query_options() with a clear error instead of advertising MCP support to the CLI and then failing at runtime when mcp_message arrives. - Remove dead mcp_servers branch from _initialize(). - Add tests for async/sync iterator exhaustion, detached init task warning suppression, and mcp_servers validation. Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): fix ruff lint errors in new tests - Use ControlRequestTimeoutError instead of bare Exception (B017) - Fix import sorting for stdlib vs third-party (I001) - Break long line to stay within 88-char limit (E501) Co-authored-by: Qwen-Coder <[email protected]> * style(sdk-python): apply ruff format to new tests Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: jinye.djy <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |