* fix(external-context): read the response body with a reader, not for-await
Async-iterating a ReadableStream needs [Symbol.asyncIterator] on the
TYPE, and whether it is there depends on which lib set the program
resolves — @types/node's stream has it, the DOM lib's needs
lib.dom.asynciterable. That resolution flipped underneath this file on
2026-08-08: #8693 installed @types/jsdom at the root, vitest's types
pull the jsdom types in wherever they exist, and jsdom's carry
/// <reference lib="dom" />. #8693 shipped the tsconfig `types` guard in
the same commit, so main stayed green — but the guard travels with the
BRANCH while node_modules travel with the TRUSTED BASE in the autofix
verification build, so every managed branch behind #8693 failed that
build with TS2504 on this line. Two legs measured on run 31276008548:
63 minutes of accepted agent work discarded per round, 18 more minutes
burned by a repair step that cannot fix a failure outside the PR's diff
(#8614 reached attempt 13 that way; #8616 died identically).
Reproduced locally in both directions before changing anything:
@types/jsdom installed + guard removed = the gate's exact error,
character for character; with the reader loop the same poisoned setup
builds clean. The guard stays — belt and suspenders — but the build no
longer depends on it, or on which lib set any future environment
resolves.
Behavior is unchanged and now pinned by tests the file never had:
multi-chunk assembly, the exact MAX_RESPONSE_BYTES boundary (bound is
strictly-greater), invalid-UTF-8 rejection, and the easy one to drop in
this rewrite — cancelling the stream on early exit, which `for await`
did implicitly via iterator return(). Mutation-tested: removing the
cancel fails exactly that test against an endless producer.
The package's other for-awaits iterate process.stdin (a Node stream,
async-iterable in every lib set) and are untouched.
* fix(external-context): await stream cancellation before rejecting the request
On early exit from the reader loop (the oversize throw) cancellation was
started fire-and-forget, so postJson() rejected while the stream's
teardown was still settling — `for await` had awaited its implicit
iterator return() before propagating. An immediate retry could overlap
the previous response transport's unfinished cancellation. Await
reader.cancel() before releaseLock(), and pin the sequencing with a
deferred-cancel regression test that fails against the fire-and-forget
form.
Also cover read() rejecting after a partial chunk was received: the
error maps to the request-did-not-complete transport error rather than
EOF-then-parse of the partial JSON, and the reader lock is still
released.
* fix(external-context): drop the types guard the reader rewrite made obsolete
The `"types": ["node"]` override existed solely to keep @types/jsdom's
lib.dom out of this program while http-client.ts read the response body
with `for await` — the DOM lib's ReadableStream is not async-iterable,
and the flip broke the build with TS2504 (#8693). The reader loop that
replaced the `for await` types identically in every lib set, so the
guard is no longer load-bearing: with it removed, lib.dom re-enters the
program and the package still builds cleanly. Drop it with its stale
comment instead of leaving maintainers two contradicting stories about
whether it is needed.
Also export MAX_RESPONSE_BYTES and import it in the boundary tests
instead of re-declaring it locally, so the tests pin the real constant
rather than a copy that can silently drift.
* test(external-context): make the invalid-UTF-8 test pin fatal decoding
---------
Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
* feat(external-context): add submitted-prompt auto recall
Add an opt-in Hook-only profile that derives bounded retrieval queries from submitted prompt provenance while preserving the existing on-demand MCP contract.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(external-context): harden auto recall query sanitization (#7877)
Address review feedback on the submitted-prompt auto recall Hook:
- Bound the sanitizer input before the redaction regexes run so a
worst-case prompt cannot drive the assignment regex into quadratic
backtracking that blocks the event loop past the wall-clock budget.
- Test the whole assignment for secret names so a leading label such as
"Deploy failed:" can no longer claim the match and leak an api_key=.
- Skip the interactive E2E under container sandboxes (docker/podman),
matching the cron-interactive precedent.
- Restore real undici coverage for a malformed proxy environment value.
- Make the wall-clock-budget test exercise the internal timer rather than
the provider timeout, and give the backtracking regression test a shape
that actually backtracks.
- Clarify that the v2 top-level timeoutMs applies only to the on-demand
MCP path, and note session-lifetime context accumulation in the design
doc.
* fix(external-context): complete secret redaction, guard MCP config version (#7877)
Anchor the secret keyword to the name that owns the separator so a leading
prose label can no longer claim the match. This redacts spaced separators
(api_key = sk-...) and inline JSON ({"api_key": "..."}), and stops
over-redacting ordinary prose such as "readme: token refresh flow".
Also reject non-version-1 configs in runMcp with a clear startup error so an
auto-recall (v2) config cannot silently expose a second retrieval surface.
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>