mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-10-31 19:52:43 +00:00
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: 易良 <1204183885@qq.com> |
||
|
|
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 09ff5d996). Most had already been fixed in that commit;
this addresses the six that were still open, all Suggestions (no
Criticals remained).
Two real behavior fixes:
- responses-pipeline.ts: a stream that closes with a final `data: `
line carrying no trailing newline at all was silently dropped.
`buffer.split('\n')` never routes an unterminated line through the
main per-line loop, and the existing post-loop flush only handled a
missing trailing *blank line* (an already-parsed event awaiting its
terminator), not an entirely unterminated line still sitting in
`buffer`. Added a targeted post-loop check for this case, with a
regression test confirmed failing before the fix (0 chunks yielded)
and passing after.
- index.ts: `getOpenAIClient()` never passed `customHeaders` to the
OpenAI SDK client, while the streaming pipeline
(responses-pipeline.ts) already applies them to every request --
headers configured for the streaming path (e.g. a proxy auth header)
silently never reached `embedContent`. Added `defaultHeaders`,
mirroring the pipeline. Regression test confirmed failing before the
fix and passing after.
Four test-coverage gaps closed:
- contentGenerator.test.ts: the USE_OPENAI_RESPONSES routing test
only asserted `totalTokens > 0`, which a mocked Chat generator
would also satisfy. Added a dedicated mock for
createOpenAIResponsesContentGenerator with a distinguishable
sentinel and asserted the Chat generator's mock was never invoked.
- index.test.ts: `embedContent`'s `model.includes('embed') ? model :
'text-embedding-ada-002'` branch was never asserted on either side;
added the missing `model` assertion to an existing case plus a new
case for the `includes('embed')` branch.
- index.test.ts: `generateContent`/`generateContentStream`'s
`request.config?.abortSignal ?? undefined` forwarding was only ever
exercised with `undefined`. Added cases passing a real
`AbortController` signal through both methods.
- responses-converter.test.ts: `normalizeResponsesParameters`'s wiring
into `convertGeminiToolsToResponsesTools` was untested end-to-end --
every existing tool-conversion case already had `properties`, making
normalization a no-op. Added a zero-arg-tool case that actually
exercises it.
Verified: 100 tests passing across the four touched suites (39 + 21 +
18 + 22), each new/changed assertion confirmed to fail on the
pre-fix code where applicable. Typecheck and lint clean.
* fix(core): address review round on the Responses wire (7 criticals + coverage)
Addresses the review round on #8169. Every finding was re-verified against
the code before acting; all were confirmed. Each behavior fix has a
regression test that was checked red-before / green-after in place.
Retry, timeout and error classification:
- generateContentStream returned a lazy async generator, so its promise
resolved before any network I/O and connection-time 5xx/429 could never
reach retryWithBackoff. Split the pipeline into an eager connect phase
(fetch + non-2xx handling + content-type guard) and a lazy body-iteration
phase behind connectStream(), and wired index.ts to it. executeStream is
retained and now delegates, so existing callers are unaffected.
- The stream read loop had no timeout layer at all and the undici dispatcher
uses headersTimeout:0/bodyTimeout:0, so a 200-then-silent upstream hung
the turn forever with no user present to cancel. Added an idle watchdog
honoring streamIdleTimeoutMs / QWEN_STREAM_IDLE_TIMEOUT_MS, reusing the
Chat pipeline's shared constants and its retryable ETIMEDOUT semantics.
(Note: the reviewer's cited helper `withStreamInactivityTimeout` does not
exist; the real sibling is `withStreamGuards`, whose structure is mirrored
here rather than imported, since importing it pulls in the whole OpenAI
SDK graph.)
- Mid-stream `error` / `response.failed` frames arrive after a 200 OK and
were thrown as plain Errors with no `.status` and no `.code`, so they
classified as `unknown` and missed the rate-limit retry, transport replay,
model fallback and persistent-mode wait. Map the known codes onto a status
before throwing.
- getOpenAIClient passed raw timeout/maxRetries to the SDK, bypassing
resolveRequestTimeout (so `timeout: 0`, a documented "disable" knob, meant
immediate abort) and DEFAULT_MAX_RETRIES.
Tool-call argument handling:
- A corrupt non-empty delta buffer shadowed the done item's authoritative
`arguments`, dispatching tool calls with `{}`. Fall back to the done
item's string on parse failure.
- Malformed-but-repairable arguments bypassed the safeJsonParse/jsonrepair
fallback every sibling wire applies to the same input class.
- Arguments parsing to an array/null/primitive flowed through the
`as Record` cast into functionCall.args; collapse non-objects to `{}` as
streamingToolCallParser does.
Fidelity and observability:
- max_output_tokens: buildRequest never read request.config.maxOutputTokens,
dropping the per-send window clamp geminiChat computes (#5950).
- functionResponse.parts media was silently discarded; tool-returned images
now reach the wire as a follow-up input_image message.
- The converter never called setGenAiUsageProvenance while always
materializing cachedContentTokenCount, so every turn reported cache
statistics as provider-reported.
Reachability and diagnostics:
- AuthType.USE_OPENAI_RESPONSES was missing from the --auth-type yargs
choices, so `qwen --auth-type=openai-responses` was rejected outright.
- The new PROTOCOL_ITEMS entry was unreachable UI. Added the auth type to
customProvider.protocolOptions; verified the type is wired end to end
(contentGenerator creation, AUTH_PREFLIGHT_ENV_KEYS, validateAuthMethod,
DEFAULT_BASE_URLS) so the wizard option resolves to a working generator.
- systemInfo.ts and the RUM base_url gate were not extended for the new auth
type, degrading /bug and /about output and losing endpoint attribution.
- De-duplicated the part->text lambda in extract-text-from-contents.
Coverage: filled the 29 flagged untested-behavior sites (multi-line data:
frames, UTF-8 split across chunk boundaries, extra_body precedence with an
explicit 0, custom header forwarding, abort-signal identity and mid-stream
abort, dispatcher forwarding, proxy-credential redaction, the non-SSE
content-type guard, malformed frames, empty-merge, and the auth/telemetry
entries).
Verification: 111/111 in openaiResponsesContentGenerator, 54/54 in the other
touched core suites, 402/404 in the touched CLI suites (the 2 failures are
pre-existing and reproduce on a pristine checkout -- config.test.ts reads
the ambient OTEL_EXPORTER_OTLP_ENDPOINT). tsc clean, eslint clean, prettier
clean.
* refactor(core): actually de-duplicate the part->text lambda in extractTextFromContents
Correction to dcd8a67d9, whose commit message claimed this was done. It was
not -- the subagent that owned the file returned a WONTFIX for it and the
claim went into the message unverified. The lambda was still duplicated
verbatim between the array-input and single-content branches of the very
helper whose JSDoc promises "a change to how Part text is shaped only needs
to be made once".
Extracted `textOfParts` and routed both branches through it. Behavior is
unchanged for every input shape.
Added the collocated test file the helper never had, including a guard that
converts the same Content bare and wrapped in a single-element array and
asserts the two agree -- the drift this extraction exists to prevent, and
which the pre-extraction code had already let happen once. Verified by
re-injecting a divergent copy into the array branch: that test alone goes
red, the other five stay green.
117/117 across the Responses directory plus the new file; tsc, eslint and
prettier clean.
* fix(core): address round-2 review on the Responses wire
The first automatic review to complete on this PR (previous three attempts
timed out). 30 inline findings plus five Criticals that were recorded in the
review ledger but never posted inline. Every finding was verified against
the code before acting.
Pipeline:
- max_output_tokens precedence reopened the user's ceiling. Last round used
`request.config?.maxOutputTokens ?? samplingParams.max_tokens`, which
returns the larger per-send value for config 1000 + per-send 8000. Now
routes through tokenLimits' `reconcileMaxTokens` "smaller wins" invariant,
which both sibling wires call and which exists precisely so a new provider
can't silently reopen that ceiling. The reviewer was right about our own
previous fix.
- `tool_choice: 'auto'` and `parallel_tool_calls: true` were sent
unconditionally. Toolless requests are reachable here (compaction via
baseLlmClient.generateText, text-mode side queries) and strict endpoints
400 on tool_choice with no tools, which can wedge compaction in a retry
loop. Gated on tools being present, matching the sibling.
- The connect phase had no timeout at all, and `ContentGeneratorConfig.timeout`
was never read by this pipeline. An endpoint that completes TCP/TLS but
never sends headers blocked the turn forever: the pinned undici Agent runs
headersTimeout:0/bodyTimeout:0, the idle watchdog only wraps reader.read()
inside iterateBody, and retryWithBackoff has no per-attempt timeout.
Added StreamConnectTimeoutError via the same resolveRequestTimeout helper
the embed client uses.
- Only the idle watchdog existed, so an upstream drip-feeding one chunk per
window re-armed it forever. Added StreamLifetimeExceededError using the
sibling's shared DEFAULT_STREAM_MAX_LIFETIME_MS /
QWEN_STREAM_MAX_LIFETIME_MS_ENV constants, and folded the idle/lifetime
resolvers into one helper that now warns on invalid values instead of
silently discarding them.
Converter:
- A delta buffer that parsed cleanly to a non-object (array/null/primitive)
bypassed the done-item authoritative-arguments fallback, which only ran in
the catch branch, so a tool call dispatched with `{}` while valid
`arguments` sat in the same event. Residual of last round's fix, which
covered only the parse-throwing case.
- Refusals were silently dropped: no `response.refusal.delta` / `.done`
handling, so a refusal produced zero content chunks, threw
NO_RESPONSE_TEXT, and retried four times re-sending the full prompt before
surfacing a misleading error. Added both events and the refusal content
part; deltas emit as text, mirroring output_text.delta.
- Tool-parameter precedence was inverted relative to both siblings
(`parameters ?? parametersJsonSchema`); swapped to match.
Generator, CLI and SDKs:
- LazyContentGenerator.useSummarizedThinking() omitted the new auth type.
- The custom-provider test mixed barrel imports (resolving to dist/) with
source imports, so the per-file vitest flow saw an undefined
AuthType.USE_OPENAI_RESPONSES against a stale dist. Both sides now come
from source.
- Both bundled SDKs enumerate every CLI auth type but omitted this one, so
SDK users could not select it: added to the TypeScript zod enum and union
and to the Python Literal and validation set, with a parametrized test
pinning the whole set against the CLI's --auth-type choices.
Coverage: filled the flagged gaps across all three files — per-output_index
function-call isolation, cached_tokens:0 absent-vs-zero, the reasoning
shape-guard, functionResponse text-append, string systemInstruction,
multi-Tool conversion, non-mutation recursion, proxy hand-off, apiKeyEnvKey
fallback, Content-Type, error-body excerpt, .status stamping, ETIMEDOUT
code, per-chunk timer reset, the no-body guard, empty-string baseUrl, the
countTokens fallback arithmetic, maxRetries pass-through, the no-baseUrl
embed branch, trailing-slash normalization, and abort-signal null handling.
Every behavior change is mutation-verified: fix reverted, test observed red,
fix restored.
Not changed, flagged for maintainers:
- `prompt_cache_key` is keyed on `userPromptId` (`${sessionId}####${counter}`),
which changes every turn and so defeats cross-turn prefix-cache affinity.
Keying on the stable session prefix is a behavior change, so it is raised
rather than made unilaterally.
- ACP `buildAuthMethods()` advertises only USE_OPENAI — it does not advertise
Anthropic, Gemini or Qwen OAuth either, so this is pre-existing scope
rather than a gap introduced here.
One ledger Critical (the pinned `fetch` being discarded in favour of Node's
global) is INVALID as written: it describes pre-round-1 code. `fetchFn =
runtimeOptions?.fetch ?? fetch` landed in 09ff5d996, and the pipeline tests
deliver their mock through `runtimeOptions.fetch` without stubbing the
global, so a regression to global fetch would fail the suite.
Verification: 174 core tests across the six touched core suites, 419 CLI
tests across seven, 119 Python SDK unit tests; tsc clean for core, cli and
sdk-typescript; eslint, prettier and ruff clean.
* fix(core): key the Responses prefix cache on the session, not the turn
`prompt_cache_key` was set to `sanitizePromptCacheKey(userPromptId)`, and
userPromptId is `${sessionId}########${counter}` -- it changes on every send.
Each turn therefore got its own cache namespace and no request in a session
could ever hit the prefix cache the previous one wrote. Nothing fails when
that happens; the entire cost is silent latency and spend, which is why it
survived until review flagged it.
Keyed on `qwen-code:${sessionId}` instead, matching the Chat wire's
`applyOfficialOpenAIPromptCaching` (openaiContentGenerator/prefix-caching.ts).
Note on provenance, since it changes how this reads: that sibling behavior
landed on main in
|
||
|
|
eaa2f5695f
|
fix(sdk-python): repair import ordering to fix Ruff CI (#10496) (#10505)
Co-authored-by: 易良 <1204183885@qq.com> |
||
|
|
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)
Some checks failed
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
SDK Python / Classify PR (push) Has been cancelled
SDK Python / SDK Python (3.10) (push) Has been cancelled
SDK Python / SDK Python (3.11) (push) Has been cancelled
SDK Python / SDK Python (3.12) (push) Has been cancelled
* 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 <qwen-coder@alibabacloud.com>
* 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 <shaojin.wensj@alibaba-inc.com> |
||
|
|
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 <shaojin.wensj@alibaba-inc.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 |
||
|
|
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 <qwen-code-dev-bot@users.noreply.github.com>
|
||
|
|
8e6a572562
|
feat(sdk): add control request methods for effort, models, usage, context (#6492)
Some checks are pending
E2E Tests / web-shell Browser Regression (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
SDK Python / Classify PR (push) Waiting to run
SDK Python / SDK Python (3.10) (push) Blocked by required conditions
SDK Python / SDK Python (3.11) (push) Blocked by required conditions
SDK Python / SDK Python (3.12) (push) Blocked by required conditions
* 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * fix(sdk-python): address remaining wenshao review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): harden settings dialog restart prompt test Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * 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 <qwen-coder@alibabacloud.com> * style(sdk-python): apply ruff format to new tests Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: jinye.djy <jinye.djy@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |