812 Commits

Author SHA1 Message Date
Totoro
0baa99ca90
feat(models): add scoped model controls to Web chats (#7249)
* test: stabilize reporter evidence and guard missing chat nodes

* feat(models): scope Web model choices to tasks, turns and conversations

* test(e2e): mock model-control preview for ordinary chat submissions

* fix(models): harden scoped choices and stale Agent submissions

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-22 08:08:23 +08:00
yi-coding
ab43439a54
fix(image-search): distinguish DDG failures from empty results (#6680)
* fix(image-search): distinguish DDG failures from empty results

* fix(image-search): preserve ambiguous DDGS dispatcher failures

---------

Co-authored-by: xyi74976-del <290725798+xyi74976-del@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 22:06:16 +08:00
yi-coding
c04946a73f
fix(mcp): retain durable control claims through slow calls and cancellation (#6780)
* fix(mcp): retain durable control claims through slow calls and cancellation

* docs(mcp): distinguish poll and cancel claim fencing

---------

Co-authored-by: xyi74976-del <290725798+xyi74976-del@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 22:03:21 +08:00
Qiu
a674132eed
fix(channels): recover from undecodable thread-binding state (#7233)
Fixes bytedance/deer-flow#7232.

Co-authored-by: Jun516 <174629376+Jun516@users.noreply.github.com>
2026-10-21 21:56:12 +08:00
Storm
c277c1572e
fix(harness): complete Lark device auth via a single-flight login run (#7182)
* fix(harness): complete Lark device auth via a single-flight login run

The web authorization polling loop reused the request wait budget as the
lark-cli auth login subprocess lifetime: each /lark/auth/complete poll ran
the CLI with subprocess timeout=wait_timeout_seconds (8s from the frontend)
and killed it on timeout. The login transaction is not atomic — poll, redeem
the single-use device code, fetch user info, then persist credentials — so a
kill after redemption but before persistence stranded the flow, and the next
poll with the same code failed with 'device_code is invalid'.

Own the login process by the flow generation instead: the first poll spawns
one background run per (user_id, generation) and later polls attach to it;
wait_timeout_seconds only bounds the caller's wait (unchanged 504 contract)
and never terminates the CLI. Advancing the generation or re-issuing a device
code abandons and kills the orphaned process, and a finished run's outcome
stays attachable for 120s so late polls return the same result instead of
re-consuming the redeemed code.

* fix(lark): tolerate vanished credential-tree entries and bound owner-alive grace

Publication could kill the login worker with an unhandled FileNotFoundError
when a concurrently-written temp file vanished between iterdir() and lstat(),
leaving the marker live but no result file ever published; every foreign poll
then blocked 504 until the 600 s attempt bound, and could respawn an
already-redeemed code.  Also, a bare-pid owner_alive check trusted for the
full 600 s let a recycled-pid or crashed-thread marker outlive any process
that could still publish.

- _credential_tree_path_kind returns "gone" on FileNotFoundError so the
  walk skips vanished entries instead of aborting; the same tolerance is
  applied to iterdir().
- _write_lark_login_result_once treats the re-harden as best-effort: a
  filesystem hiccup is logged and publication continues so the marker sweep
  still runs.
- The marker now records owner_pid_started_at and
  _read_live_lark_login_marker_locked only keeps an owner-alive marker
  within a 10 s publication grace, so a stale marker is swept and the flow
  respawns instead of wedging every foreign poll.

Regression: test_walk_and_harden_tolerates_a_vanished_entry,
test_write_result_once_continues_when_hardening_fails,
test_abandoned_marker_with_dead_owner_is_swept_within_grace.
Focused suites: 139 passed, 12 skipped.

* fix(lark): harden surviving credentials through filesystem churn

Atomic login publication can remove a descendant after lstat but before
chmod. Catch FileNotFoundError around the entire per-entry walk so that
lstat, chmod and directory enumeration tolerate those disappearances.
A vanished root, permission error or symlink remains a hard failure.

Exercise real removals at each boundary and concurrent result publication;
assert that injection ran and surviving directories/files reach 0700/0600.
The regressions detect a skipped walk, swallowed permission failures and
loss of directory-churn tolerance in isolated mutation runs.

* fix(lark): retain login ownership through durable completion

Browser approval can take longer than the publication grace. Start that
grace at the first observed CLI exit, then reject abandoned attempts
without retrying a device code that may already have been redeemed.

Serialize atomic result publication under the credential lock. Preserve
published results through cleanup failures and retain terminal failures
on the marker when result storage fails. Never bypass credential hardening.
Reserve storage before spawning and roll back failed marker, process or
thread startup by reaping the CLI, clearing the registry and waking waiters.

Add process/filesystem failure regressions, a long-login two-worker case,
and strict unhandled-thread gates. Fix invocation logging in both workers
and reap forked test CLIs after the parent observes their launch. Document
the authorization lifecycle and required regression assertions.

Validated the reproduced failures and ten isolated mutations; final
focused, blocking-I/O, full-suite and independent review details are in PR #7182.
2026-10-21 21:00:35 +08:00
Tsai Yuan
c70dd685a9
fix(scripts): select sandbox containers by anchored name (#7189) 2026-10-21 20:57:02 +08:00
Nan Gao
f39cdd6077
fix(mcp): keep rotating OAuth refresh tokens across restarts and Gateway instances (#7207)
* fix(mcp): keep rotating OAuth refresh tokens across restarts and Gateway instances

MCP servers on the OAuth refresh_token grant lost their rotated refresh
token whenever a new process or manager started (restart, tool-cache
reset for non-task servers, every personal-connection run, a second
Gateway instance) and re-seeded with the consumed configured token,
getting invalid_grant for good.

- New table mcp_oauth_tokens (migration 0047_mcp_oauth_tokens), one row
  per refresh_token connection keyed (scope, owner_user_id, server_key):
  ('deployment', '', name) or ('personal', owner, personal runtime
  name). Access and refresh tokens are DEER_FLOW_CREDENTIALS_KEY
  ciphertext; a config fingerprint (token_url, client_id, grant, scope,
  audience, seed) makes a changed seed win.
- OAuthTokenManager re-reads the row under its process lock, adopts a
  peer's valid token, and otherwise claims the refresh with a lease plus
  a version compare-and-set (database clock), fetches outside any
  transaction, writes under the lease and releases it on failure.
- The Gateway installs the store (sync engine via to_thread) around the
  LangGraph runtime for sqlite/postgres; personal managers pass their
  owner. client_credentials stays in process; an unloadable key falls
  back to process memory; database errors fail the refresh.
- credentials_key_consumers lists refresh_token MCP OAuth servers, so a
  declared multi-instance deployment needs the shared key.

Docs: MCP_SERVER.md, CONFIGURATION.md, Helm README, README, mcp and
migrations AGENTS.md, CHANGELOG (en/zh).

* docs(changelog): link the MCP OAuth token store entry to #7207

* fix(mcp): prune removed connections' OAuth token rows and ask owners to re-authorize on invalid_grant

Review follow-up (willem-bd):
- Both MCP config writers drop mcp_oauth_tokens rows of connections a
  save removes (deployment: _commit_mcp_config_write; personal:
  _mutate, keeping each connection's enabled/disabled runtime-name
  variants so a toggle keeps its chain). Best effort; no startup sweep,
  since a missing config file would look like every server removed.
- invalid_grant on a refresh_token request raises
  McpOAuthReauthorizationRequired: the tool error names the server and
  asks for a new refresh token, plus the shared-key hint when this
  instance could not decrypt the stored chain.
- Annotate the gateway helpers' config as AppConfig.

* fix(mcp): renew the OAuth refresh lease, keep unstored rotations, and key access tokens by request parameters

Review follow-up (willem-bd, second round):
- HTTPX timeouts bound each I/O wait, not a whole response: the refresh
  holder now renews its lease every third of REFRESH_LEASE_SECONDS while
  the token request runs and abandons it after REFRESH_MAX_SECONDS (120 s);
  waiters outlast a live holder. Regression uses a real loopback endpoint
  that streams its body for three lease lengths.
- A refresh whose tokens could not be stored keeps them pending
  (unsaved_base_version): they are written back before an older stored
  token is adopted or refreshed with, retried every few seconds while the
  access token is still valid, and dropped once a peer stored a newer
  chain. A failed save releases the lease.
- New token_request_fingerprint (extra_token_params and response fields):
  a stored access token is reused only under the same request parameters;
  otherwise the stored refresh-token chain is refreshed.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 18:17:29 +08:00
luckiest-draw
3f3cd23965
fix(config): avoid Unicode failure after generating configuration (#7102)
* fix(config): avoid Unicode failure after generating configuration

* fix(setup): handle legacy stdout encodings in setup commands

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 16:03:39 +08:00
ningkexin96
4f948c7b8d
feat(deploy): select production Compose env files (#6522)
* feat(deploy): select production Compose env files

Use an operator-selected dotenv file consistently for interpolation, container environment, secret probes and build extras. Preserve default selection and shutdown after a profile is removed.

Co-Authored-By: Codex <noreply@openai.com>

* fix(deploy): address production env selection review

Clarify Docker launcher entry points and render real Compose through the launcher to verify both service environments.

Co-Authored-By: Codex <noreply@openai.com>

* test(deploy): cover unreadable env selectors

Reject unreadable explicit selectors before Docker is invoked, using POSIX permission checks and restoring the fixture mode in finally.

Co-Authored-By: Codex <noreply@openai.com>

* test(deploy): scope dotenv assertions to selected secrets

---------

Co-authored-by: Codex <noreply@openai.com>
2026-10-21 15:58:30 +08:00
Qiu
8b68832f32
fix(channels): release standalone DingTalk upload sandboxes (#7203)
Refs bytedance/deer-flow#3296.

Co-authored-by: Jun516 <174629376+Jun516@users.noreply.github.com>
2026-10-21 10:53:49 +08:00
Stella-xixi
5cef1a11ef
fix(checkpoints): run conservative retention after completion (#6051)
* fix(checkpoints): wire conservative retention after runs

* fix(checkpoints): fence retention before checkpoint reads

* fix(checkpoints): address retention review feedback

* fix(checkpoints): retry delta reads after retention race

* fix(checkpoints): bound admission retention to the root tail

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 22:23:47 +08:00
trent87
0d73798beb
feature(skills): keep allowed-tools as compatibility metadata (#6935)
* refactor(skills): keep allowed-tools as compatibility metadata

* docs(skills): align current guides with agent tool authority

* test(skills): simplify authority regressions and sync Korean docs
2026-10-20 19:34:28 +08:00
Totoro
059758f321
fix(runtime): stop Goal continuations after loop hard stops (#7167)
* fix(runtime): stop Goal continuations after loop hard stops

* chore: remove redundant SVG sources from Goal fix

* test(goal): remove PR-only screenshot generation

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-20 16:55:13 +08:00
Dragon ze
d85e0e8608
fix(channels): keep staging cleanup from replacing the save error (#7106)
* fix(channels): keep staging cleanup from replacing the save error

The channel stores publish state through a staged temporary file and, if the
rename fails, close and unlink that file inside the handler before re-raising.
Neither step was guarded, so a failing close skipped the unlink and a failing
unlink replaced the error that actually caused the save to fail -- leaving the
staged copy, which for the runtime-config and WeChat stores holds an 0600 IM
token, behind with nothing reporting it.

Make both steps independently best-effort, name the leaked file in a WARNING,
and suppress the diagnostic itself so a broken log handler cannot become the
reported failure. This is the same contract PR #6871 establishes for browser
captures, applied to the three JSON publishers in app/channels.

* fix(channels): address staging cleanup review follow-ups

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 16:52:47 +08:00
Zicheng (Axelsen) Xie
1ce0b765de
fix(uploads): preserve separate bold spans in outline titles (#7150) 2026-10-20 16:39:33 +08:00
Fake1new
d288935865
fix(skills): preserve Markdown table cells in data-analysis exports (#7163)
* fix(skills): preserve Markdown table cells in data-analysis exports

* docs(skills): document Markdown cell escaping contract

Clarify handling of Markdown exports and escaping rules.

* docs: describe data-analysis Markdown export escaping

Clarify data-analysis skill features and behavior in README.

* test(skills): cover Markdown export headers and multiline cells

Add a test for markdown export functionality in data analysis.
2026-10-20 14:30:57 +08:00
Lucien
2c880a3133
fix(helm): forward Live Browser WebSocket upgrades (#7148)
* fix(helm): proxy Live Browser WebSocket upgrades

* fix(helm): use default browser connect and send timeouts
2026-10-20 13:17:54 +08:00
SumEn
53f09dc152
fix(projects): preserve document upload outcomes during cleanup (#7141)
* fix(projects): preserve upload outcomes during staging cleanup

* fix(projects): guard staging discard failures

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 13:15:40 +08:00
李刚 / Bryan Li
1fd266e0c0
feat(decisions): add an OpenAI Decisions backend for the decision slots (#7127)
* feat(decisions): add an OpenAI Decisions backend for the decision slots

Issue #7005 asked for a second decision provider behind the interfaces the
TypeSafe (Jev) integration already uses, not a new decision system.
`deerflow/openai_decisions` speaks `POST /v1/decisions`: a `noul` question
becomes a `predicate` whose true/false rubric folds into `instructions` (the
endpoint has no criteria field, and dropping it would move the decision
boundary while the adapter kept reporting the same criteria in its policy
identity), answers arrive as an array rather than a mapping, and a refusal is an
answer `type` rather than an HTTP failure -- so it lands in the existing
per-question error channel and each consumer keeps its own failure direction.

Transport is reused rather than copied. `provider_label` and the connection's
`endpoint_path` are the only two seams a dialect needs, so retry and backoff,
the deadline budget, the bounded never-decoded body read and the error taxonomy
keep one owner; `resolve_decisions_connection` pairs the two so a Decisions
connection cannot be built pointing at System One. The three adapters override
their connection resolution, their client, and the value the endpoint judges
(`input` text instead of a `state` object), and inherit thresholds, rubrics,
caches and audit policy unchanged. Wiring stays a `use:` class path, so no
config schema is added.

* fix(decisions): isolate endpoint identities and correct diagnostics

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 11:50:46 +08:00
LuckVd
2174c1d81e
fix(memory): quarantine unreadable memory summaries (#7076)
* fix(memory): quarantine unreadable memory.json in the read-error branch too

MarkdownMemoryStorage._load_memory_file quarantined summaries that failed
to decode as JSON/Markdown, but the earlier except (OSError, UnicodeError)
branch (unreadable bytes, e.g. a UTF-16 BOM) still returned None without
quarantining. The leftover file was then silently overwritten by the next
save, destroying the user's memories with no backup.

Call the existing best-effort self._quarantine_unreadable(path) there as
well, matching the documented quarantine contract, and add a regression
test asserting the original bytes survive a subsequent load+save.

Fixes #7072

* fix(memory): preserve summaries on transient read failures

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 11:25:01 +08:00
杰尼龟
68d4e5527c
fix(tui): accept whitespace separators in slash commands (#7117)
* fix(tui): accept whitespace separators in slash commands

* fix(tui): align slash palette with Unicode whitespace

Close the slash palette on every whitespace separator accepted by the command resolver, including pasted tabs and Unicode spaces. Document the accepted delimiters and cover parser arguments, palette closure, Enter submission and Tab completion.

Validation: 352 focused tests, 34,089 offline backend tests and 262 blocking-I/O tests passed. Backend formatting, frontend check and agent guidance passed.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 11:19:45 +08:00
LingYi
ee02e51df9
fix(memory): preserve consecutive pre-compaction snapshots (#7152)
* fix(memory): preserve consecutive pre-compaction snapshots

* fix(memory): warn on growing emergency snapshot backlogs

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 10:24:16 +08:00
OUAO-FRANK
5e5913f4cf
fix(codex): honor Retry-After in the provider retry loop (#7151)
* fix(codex): honor Retry-After in the provider retry loop

* style(codex): format retry-after regression helper

* fix(codex): honor HTTP-date Retry-After hints

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 10:01:47 +08:00
lakers-abing
f23a411325
fix(storage): resolve single-module colon backend imports (#7017)
* fix(storage): resolve single-module colon backend imports

* docs(storage): describe both backend import separators

---------

Co-authored-by: lakers-abing <236384465+lakers-abing@users.noreply.github.com>
2026-10-20 09:35:56 +08:00
yu1128-L
badfd7d3f1
fix(docker): make dev provisioner opt-in via compose profile (#6948)
* fix(docker): make dev provisioner opt-in via compose profile

Gate the Kubernetes provisioner behind a Compose profile and have the
wrapper enable it only when config selects provisioner sandbox mode.
This lets the default Docker Compose stack start without kubeconfig.

Fixes #3232

* test(docker): render every compose profile in the env-file regression test

main added backend/tests/test_compose_default_bind_host.py, whose _render helper asserted that the dev provisioner still loads ../.env. `docker compose config` without a profile omits profile-gated services, so that assertion failed once the provisioner became opt-in.

Pass `--profile *` from the helper: the test is about an existing .env reaching every service, so it has to render the services a profile gates rather than only the ones `up` starts by default.

* docs(docker): restore script mode and localize direct Compose guidance

The dev provisioner is now opt-in, but the earlier edit also flipped scripts/docker.sh from 100755 to 100644 even though README.md still documents direct `./scripts/docker.sh` invocation. Restore the executable bit.

Also add the direct-Compose guidance to the translated READMEs so readers of README_zh.md, README_ja.md, and README_fr.md learn that `--profile provisioner` selects the Kubernetes mode and the default stack does not need Kubernetes.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 09:10:24 +08:00
MsNBBc3R
2259df1130
fix(skill-creator): tolerate malformed timing measurements (#7078)
* fix(skill-creator): tolerate non-object timing measurements

Treat optional timing JSON values that are not objects as missing measurements so they cannot abort benchmark aggregation. Preserve grading durations, character-count fallback and recorded zeros; add regression coverage for both layouts and subsequent runs.

* fix(skill-creator): warn and validate timing measurements

* fix(skill-creator): tolerate unreadable timing files

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 08:30:37 +08:00
yetuge
18ca09b2da
fix(memory): parse a BOM-prefixed DeerMem fact instead of calling it corruption (#7116)
* fix(memory): parse a BOM-prefixed DeerMem fact instead of calling it corruption

Facts are hand-editable Markdown files, so a Windows editor can save one as
"UTF-8 with BOM" -- the same exposure #7083 fixed for memory.json. The fact
parser checks `text.startswith("---\n")` against a plain-utf-8 read, so the
BOM makes a valid fact read as "missing YAML front matter" and raises
MemoryStorageCorruption, which the module docs describe as taking down the
whole agent.

Read the fact with utf-8-sig, matching the summary loader and the SOUL.md /
SKILL.md convention already recorded in AGENTS.md. `_parse_listed_fact`
delegates here, so the directory-scan path is covered by the same change.

Measured: the new BOM test fails on the base revision with exactly that
"missing YAML front matter" corruption and passes at head; the no-BOM control
passes both. 49 tests in test_memory_storage.py, 106 across the memory
suites, ruff check/format clean. Mutation-checked: reverting to plain utf-8
fails only the BOM case.

* test(memory): cover BOM facts through the public storage API

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 23:27:04 +08:00
RongJie G
cd28a1cfe9
fix(subagents): bound hard timeout terminalization (#7053)
* fix(subagents): bound hard timeout terminalization

Signed-off-by: CorgiBoyG <111257566+CorgiBoyG@users.noreply.github.com>

* fix(subagents): address timeout lifecycle review

Bound durable terminal cleanup waits to 60 seconds while unfinished execution resources stay supervised. Preserve terminal results when cancellation races a child failure, and register deferred registry removal when its fence wait expires.

Add five lifecycle regression cases and preload the sandbox prewarm dependency in the executor test fixture. Validate 470 focused tests, 261 strict blocking-I/O tests, and 50 repeated regression executions under asyncio debug.

---------

Signed-off-by: CorgiBoyG <111257566+CorgiBoyG@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 23:13:12 +08:00
EastonJiang
b8bb728368
fix(frontend): stop chat sends after unsuccessful upload responses (#7122)
Co-authored-by: Jaxon1216 <224378083+Jaxon1216@users.noreply.github.com>
2026-10-19 23:10:33 +08:00
SumEn
fee0cd0c18
fix(wechat): isolate downloaded attachments before inbound ingestion (#7055)
* fix(wechat): isolate downloaded attachments before inbound ingestion

* docs(channels): preserve the existing guide heading

* fix(wechat): release owned inbound attachment staging

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 16:31:48 +08:00
zzr
3e6e83c87c
fix(browser): preserve publication errors during failed cleanup (#6871)
* fix(browser): preserve publication errors during failed cleanup

* fix(browser): log atomic output cleanup failures

* fix(browser): keep cleanup logging best effort

* fix(skills): preserve projection manifest publication failures

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 16:27:15 +08:00
RongJie G
bf2cb0e1df
fix(runtime): recover terminal Redis streams after producer loss (#7093)
Signed-off-by: CorgiBoyG <111257566+CorgiBoyG@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 15:53:54 +08:00
ChengFa
5a9a8042b4
fix(streaming): avoid false replay gaps after cursor eviction (#6812)
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 15:27:56 +08:00
Xuehao Xu
b6ce047510
feat(workflows): add durable restart recovery (Phase 3) (#7075)
* feat(workflows): recover durable workflows after Gateway restart

* docs(workflows): keep recovery guidance within inherited budget

* fix(workflows): retain cancellation ownership and stabilize turn-limit test

* fix(workflows): log service-owned action failures
2026-10-19 15:24:53 +08:00
spud
850b3b2547
fix(runtime): attribute terminal commits and isolate lease renewals (#6919)
* fix(runtime): confirm terminal commits and isolate lease renewal

* fix(runtime): diagnose unsupported terminal store contracts

* fix(runtime): attribute atomic claims and close lease lifecycle gaps

* fix(runtime): require explicit terminal completion capability

* fix(runtime): retry renewals that race rollback admission
2026-10-19 15:06:35 +08:00
zzr
afc96c667a
docs(feishu): explain CA trust troubleshooting for TLS failures (#6869)
* docs(feishu): explain CA trust troubleshooting for TLS failures

* docs(feishu): sync Chinese TLS and notification pointers
2026-10-19 11:51:38 +08:00
Dragon ze
4ff0b0bff1
fix(github-research): resolve the tree against the repository's default branch (#7074)
* fix(github-research): resolve the tree against the repository's default branch

The tree command asked for "main" and, when that was missing, for "master".
A repository whose default branch is neither gets a name-guessing fallback, and
when the stale "master" still resolves GitHub returns it happily: the agent is
handed the tree of an abandoned branch and reports it as the current structure.

Ask for HEAD instead, which GitHub resolves to the repository's own default
branch, so no candidate names are guessed and no extra request is added.

* test(github-research): clarify stale branch tree comment

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 11:38:13 +08:00
Qiu
a1f23b294a
fix(frontend): preserve memory mutations against stale reads (#6959)
* fix(frontend): preserve memory mutations against stale reads

* test: register memory guidance in scoped inventory

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 11:33:35 +08:00
PeaceMaker-best
dbebd18dd7
fix(deer-flow): retry failed agent setup saves (#6628)
* fix(deer-flow): retry failed agent setup saves

Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>

* fix(frontend): fence superseded agent save callbacks

---------

Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 10:55:50 +08:00
Totoro
92e9afe6ea
feat(context): support client context capabilities (#7051)
* feat: support client context capabilities

(cherry picked from commit b7c223f9415dcd545cbe05a5f5a6e47073a42d68)
[Totoro-qaq: resolved onto main. The persisted-reminder middleware hunks and their tests are dropped (delivery is redesigned in a follow-up); the RunCreateRequest description moved to app/gateway/run_models.py.]

* fix: address client context review comments

(cherry picked from commit bce6da6b819228eda58e887872720b6f9b5fab69)
[Totoro-qaq: middleware hunks dropped with the redesigned delivery.]

* feat(agents): deliver client context as a request-scoped data block

The cherry-picked #3106 put context.client into the persisted first-turn
reminder, with change detection and a "not provided" clear marker. Main
has since moved the date reminder to a SystemMessage, made request-scoped
context (the project block, #5443) request-only, and agents/AGENTS.md now
requires user-influenced values to ride the HumanMessage data channel,
never system text.

Render runtime.context["client"] on every model call in
DynamicContextMiddleware.wrap_model_call/awrap_model_call as one hidden
HumanMessage right after the leading SystemMessages, where DurableContext
puts its data. It is never a state update and never flagged
dynamic_context_reminder, so nothing is checkpointed, summarized or
replayed, and a run without a client gets no block. An unchanged client
keeps the previous request as the next request's prefix.

The renderer sanitizes again (direct graph callers bypass the Gateway),
html-escapes values and frames them with frame_untrusted_text: the same
--- BEGIN/END USER INPUT --- boundary the user's own turn and the MCP task
notification get. The lead prompt treats everything outside those markers
as framework data, so unframed values would rank above the user's own
text; framed, context.client can say nothing the same caller could not
already say in input.messages. client_context joins _BLOCKED_TAG_NAMES so
a forged block in user input or a tool result is escaped, and one static,
value-free sentence in the lead prompt names the block as untrusted hints,
never instructions, permissions or identity. The now unused empty-context
marker is removed.

* fix(gateway): log dropped context.client fields without values

A run request is never rejected over context.client: unknown fields,
nested values and malformed entries are silently dropped, the same
leniency as the other context keys. That left no signal when a custom
client sent something the Gateway ignored (review on #3106).

Route both the config.context.client and the body context.client paths
through one helper that emits a debug line whenever sanitizing changed
the value. Like the malformed client_timezone branch, the line carries no
values; unlike client_timezone, client.name and preference strings are
free caller-controlled text, so they are never logged, not even
sanitized. A clean client, an explicit null or an absent key logs
nothing.

Tests also pin the config-only path (sanitized before the early return)
and the end-to-end start_run admission: the sanitized client reaches
config["context"] and never configurable.

* fix(runtime): bound numeric client context preferences

String preferences are capped at 200 characters, but integers were kept
at any size. A JSON body may carry integers of up to 4300 digits, so 32 of
them rendered a block of about 138,000 characters, re-sent on every model
call of the run. In-process callers could also pass an int past the str()
digit limit, which failed the model call, or a number subclass with its
own __str__.

Keep integers within JavaScript's Number.MAX_SAFE_INTEGER, the range a
browser client can send exactly, and coerce numbers to plain int and
float.

* docs(api): describe client context as advisory request-scoped hints

The cherry-picked section described a hidden reminder that no longer
exists. Document what a custom client can rely on: the allow-list and its
limits (strings, the integer range, and only the first 32 entries of each
map), debug-level logging of dropped fields, runtime-only forwarding, and
delivery as a per-request user-role block, framed like user input, rather
than system text or thread history.

Only top-level context.client is documented. An explicit
config.context.client is sanitized for the run as well, but the run record
keeps the caller's config as sent, so it is not offered as an alternative.

It also answers the #3106 review: capabilities and preferences are
advisory hints, not enforcement, which belongs outside the model; name is
informational and never for authorization; the same object should be
sent on every run to keep the cached prefix stable; subagents and
embedded DeerFlowClient runs do not receive it, and server-started runs
(schedules, IM channels, GitHub agents, MCP notifications, extensions
unless the plugin passes one) carry none.

* fix(context): declare client delivery and keep client out of configurable

The request-only <client_context> block is model-visible, so
DynamicContextMiddleware now declares its delivery and the sanitizer's
limits in release_policy_parameters(); runs before and after this change
no longer share a policy fingerprint.

build_run_config copies body.config['configurable'] verbatim and
configurable is persisted with checkpoints, so a client placed there is
dropped instead of being stored raw. Client context stays runtime-only.

Document context.client in the README and in the middleware guide.

* fix(context): apply the PII redaction policy to the client block

DynamicContextMiddleware adds the <client_context> message inside the
outer PiiRedactionMiddleware, and the hidden message is not re-sanitized,
so with pii_redaction enabled a preference such as an email address
reached the model verbatim while the same address in the user turn was
redacted. Pass the rendered block through the shared redact_text seam
with this middleware's app_config.pii_redaction, and declare the setting
in release_policy_parameters().

* fix(context): keep client hints out of stored run config

---------

Co-authored-by: Blair Leng <25675774+BlairLeng@users.noreply.github.com>
Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-19 10:41:18 +08:00
yi-coding
a3c65c72e5
fix(skills): preserve source and SQL semantics across data analysis (#6739)
* fix(skills): preserve source and SQL semantics across data analysis

* test(skills): use a portable filename for quoted SQL aliases

* test(guidance): register data-analysis scoped instructions

* test(guidance): keep skill registration independent of new examples

* fix(skills): require explicit data analysis dependency setup

---------

Co-authored-by: xyi74976-del <290725798+xyi74976-del@users.noreply.github.com>
2026-10-19 09:19:01 +08:00
ShiYuan Xiong
2254ec6ed5
feat(benchmark): add OpenSandbox provider coverage (#7059) 2026-10-19 07:45:59 +08:00
haotianqi666
81daa578f7
fix(workspace): keep snapshots when files vanish during hashing (#7057)
Co-authored-by: haotianqi666 <250384991+haotianqi666@users.noreply.github.com>
2026-10-19 07:40:52 +08:00
Ryker_Feng
d1d0958bea
feat(skills): add guarded evolution host APIs (#5645)
* feat(skills): add guarded evolution host APIs

* docs(agents): avoid growing saturated guidance chains

* fix(skills): address mutation host review findings

* fix(skills): order name fence before projection lock

* fix(skills): preserve composite toggle lock order

* fix(skills): address catalog and pagination review feedback

* docs: keep subagent guidance within merged instruction budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-18 21:17:02 +08:00
早上肚子疼
51352fd2bc
fix(sandbox): search long lines for literal grep patterns (#6969)
* fix(sandbox): search long lines for literal grep patterns

* test(sandbox): cover literal long lines across providers

* fix(sandbox): bound case-insensitive literal grep work

Replace repeated-prefix regex searches with KMP over single-character Unicode case matches. Keep local long-line literal results and existing regex and preview limits. Add a subprocess-deadline regression for the review counterexample and case-semantics coverage.

* docs(sandbox): keep grep guidance within inherited size limit

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-18 20:58:35 +08:00
MsNBBc3R
3dbee8f4aa
fix(skill-creator): preserve recorded token counts in benchmarks (#7033)
* fix(skill-creator): preserve recorded token counts in benchmarks

Read timing.json token measurements independently of grading duration and retain recorded zero values. Keep the existing character-count fallback for missing measurements and add benchmark/report regression coverage.

* fix(skillscan): distinguish token metrics from credentials

Recognize typed token counts, numeric benchmark summaries, and signed deltas so skill-creator documentation passes deterministic review. Preserve credential names, string values, duplicate-key detection, and cloud-token findings; add regression coverage and document the boundary.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-18 20:44:39 +08:00
BUG设计工程师
218fff3c76
fix(artifacts): preserve balanced ASCII delimiters in references (#6967) 2026-10-18 19:23:19 +08:00
yetuge
73f6749068
fix(searxng): contain malformed search payloads instead of crashing on them (#6931)
* fix(searxng): contain malformed search payloads instead of crashing on them

`_search_page` indexed the decoded body directly:

    data = resp.json()
    return data.get("results") or []

so a self-hosted instance that answers HTTP 200 with something other than a
result page -- an HTML or plain-text error page from a reverse proxy or captive
portal -- reached the model as a raw Python error, or as an empty search:

    {"results": "maintenance window"}  -> {"error": "'str' object has no attribute 'get'"}
    {"results": {"a": 1}}              -> {"error": "'str' object has no attribute 'get'"}
    {"results": [1, 2, 3]}             -> {"error": "'int' object has no attribute 'get'"}
    {"results": [None]}                -> {"error": "'NoneType' object has no attribute 'get'"}

Worse, a single malformed entry in an otherwise valid page crashed the whole
call and discarded every good row:

    {"results": [{"title": "ok", ...}, 7]} -> {"error": "'int' object has no attribute 'get'"}

This is the containment rule the sibling providers already follow (brave #6550,
groundroute #6603, tavily #6645, sofya #6719, serper #6779, serply #6796,
tencent-wsa #6799): a payload that is not a result page is a format error, not an
empty result, because "No results found" tells the agent the query legitimately
matched nothing and ends a research loop as "no hits".

The client now raises `SearxngResponseError` for a payload that is not an object,
a body that is not JSON, a `results` container that is not a list, and a
non-empty list holding no object entries, while still skipping non-object entries
in an otherwise valid page so the valid rows survive. `web_search_tool` reports
the provider's format error instead of leaking the exception text. Missing, null,
and empty `results` keep the existing "No results found" behavior.

Diagnostics log the container field name and its type, or the absence of usable
objects -- never the query, credentials, or payload values.

* test(searxng): pin the mid-walk format-error trade-off and fix the docstring

The paginating client aborts the walk and discards already-collected rows when
a later page comes back malformed, which is a different outcome from the
single-request siblings. Pin that trade-off with a test so the behaviour is a
deliberate decision rather than an accident, and reword the
_unexpected_format_error docstring: it promises the query is not echoed, but
the sibling providers all echo it back on purpose and only withhold the
payload.

* test(searxng): log skipped entries and pin the dedicated format-error handler

Two follow-ups from review, both about making the family contract observable
and pinned:

- Skipped non-object entries now log their index ("Skipping malformed SearXNG
  result entry at results[1]"), matching the merged InfoQuest sibling. SearXNG
  is the provider where drift is most expected -- a self-hosted instance serves
  whatever sits behind its reverse proxy -- and a search spans up to
  _MAX_PAGES requests, so rows could otherwise be shed with no trace at all.
- The dedicated `except SearxngResponseError` branch had no test that could
  fail: the exception is a ValueError whose message is already the format-error
  string, so the generic handler returns byte-identical JSON. The new test
  asserts the generic branch's log line is absent, which is what actually
  distinguishes the two paths.

* docs(searxng): name the empty result it actually returns, and pin the dedicated except

Two low findings from the follow-up review:

- The README said missing/null/empty `results` keep the existing "No results
  found" response. That response does not exist in this provider: all three
  produce a JSON empty list, and the string appears only in comments
  explaining what must NOT be reported. The family convention distinguishes
  (Tavily: "`[]` for search, Error: No results found for fetch"; InfoQuest:
  "keep the existing empty result"), so this now reads "keep the existing
  empty result (`[]`)".
- The client-level `except SearxngResponseError: raise` was unpinned: deleting
  it left the whole file green, because the tool-level test mocks the client
  and the payload tests only assert the raise type. The observable regression
  is the generic branch logging "An unexpected error occurred" for every
  contained page, so the existing payload test now asserts that line is absent
  from caplog.

Mutation check: removing the clause now fails 8 parameterised cases (was 33
green); with the clause in place the suite is 33 passed and 91 across the two
provider files, ruff check/format clean.
2026-10-18 13:12:23 +08:00
RongJie G
1098fb4c60
fix(harness): scope deferred tool promotion to user turn (#6977)
* fix(harness): scope deferred tool promotion to user turn

Signed-off-by: CorgiBoyG <111257566+CorgiBoyG@users.noreply.github.com>

* fix(harness): preserve legacy promotions during routing resume

---------

Signed-off-by: CorgiBoyG <111257566+CorgiBoyG@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-18 11:24:41 +08:00
haotianqi666
07f0d74f70
fix(agents): clear incompatible knowledge defaults on self-update (#6995)
* fix(agents): clear incompatible knowledge defaults on self-update

* fix(agents): share knowledge default cleanup with HTTP updates

---------

Co-authored-by: haotianqi666 <250384991+haotianqi666@users.noreply.github.com>
2026-10-18 11:12:57 +08:00