Repository navigation
DeerFlow 2.2.0 #7896
WillemJiang
announced in
Announcements
DeerFlow 2.2.0
#7896
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
DeerFlow 2.2.0
DeerFlow 2.2 turns the 2.1 super-agent harness into a deployment-grade platform
with a focus on operating at scale and delegating with trust: scheduled
tasks grow into goal-driven workflows with fair multi-owner execution, every
subsystem from IM channels to workflows runs across multiple Gateway
instances, the extension platform ships full-stack plugins, knowledge
retrieval becomes provider-independent with verifiable citations, and models,
memory and channels gain production-grade management — hardened by fail-closed
defaults, at-rest credential encryption, and a much stricter uploads and paths
layer.
This release closes the 2.2.0 milestone
with 1068 merged PRs since the 2.1.0 release.
make dev/make startno longer listen onall interfaces; ports 2026/8001/3000 and
/setupstay off LAN/VPN. SetBIND_HOST=0.0.0.0in.envto expose the stack, and note that valuesnginx would misread (
0,1.2.3, bracketed IPv6) are now rejected beforeany service stops. ([fix(scripts): bind local make dev and make start to loopback #6587], [fix(scripts): reject local BIND_HOST values nginx would misread #6635])
DEER_FLOW_CREDENTIALS_KEY(Fernet, comma-separated for rotation) protectsIM channel credentials and MCP OAuth refresh tokens; multi-instance
deployments with
channel_connectionsor refresh-token OAuth refuse tostart without it. IM bot credentials move from plaintext
channels/runtime-config.jsoninto the sharedchannel_runtime_configstable — existing bots are imported once on first start, and a downgrade must
re-enter UI-configured bots. ([fix(security): add DEER_FLOW_CREDENTIALS_KEY and wire the channel credentials cipher #6611], [fix(mcp): keep rotating OAuth refresh tokens across restarts and Gateway instances #7207], [feat(channels): share encrypted runtime channel config across Gateway instances #6988])
GATEWAY_WORKERS > 1(ordeployment.multi_instance: true, orWEB_CONCURRENCY) now requires theRedis stream bridge; the memory bridge no longer starts multi-process. The
new
channels.leader_leasesection must stay enabled when a non-GitHub IMchannel is configured. ([fix(gateway): gate declared multi-instance deployments and require the redis stream bridge #6328], [feat(channels): add channels.leader_lease and gate an explicitly disabled lease under multi-process signals #6746], [fix(gateway): read uvicorn's WEB_CONCURRENCY in the multi-worker safety gates #6252])
/api/scheduled-tasks*errors return astructured
{"detail": {"code", "message", "params"}}envelope (readdetail.message); a newscheduler.max_concurrent_runs_per_user(default2) caps one owner's concurrent scheduled runs; IM notices are honest,
localized, and sent at most once per occurrence; DST-skipped
oncewalltimes are rejected with 422. ([feat(scheduled-tasks): finish the user-facing side of conversation schedules (RFC #6340, 1/2) #6378], [feat(scheduled-tasks): signals, honest notices and fair queueing for server-started runs (RFC #6340, 2/2) #6393], [fix(scheduler): reject nonexistent local times at admission #7281])
run events and feedback, so deleted threads can no longer be read or
inherited by recreated thread ids. ([fix(threads): clean persisted records safely on thread deletion #5535])
write_filepayloadelision becomes opt-in (
tool_output.elide_superseded_writes, defaultfalse) because elision rewrote earlier request segments and invalidated
provider prompt caches; skill
allowed-toolsis compatibility metadata onlyand no longer hides or blocks tools. ([[bug] elide_superseded_writes rewrites earlier history (one prefix-cache miss per elision) and its placeholder text gets written into files #7319], [fix(tool-output): preserve prompt prefixes by default #7455], [feature(skills): keep allowed-tools as compatibility metadata #6935])
write_todosandextension middleware tools now pass Layer-1 authorization like any other
tool; deployments with an explicit
toolspolicy that omitted them stopbinding them. ([feat(authz): middleware-declared tool path Layer 1 now covers tools declared by middlewares #6105])
web_fetch/web_capturebackends (Browserless, Crawl4AI, Firecrawl, fastCRW) screen their base URL
and refuse loopback/private defaults until confirmed; the SSRF guard refuses
the CGNAT range
100.64.0.0/10(tailnets needallow_private_addresses: true); IMallowed_usersparse fail-closed onSlack, DingTalk, QQ and Telegram (an invalid list now denies everyone
instead of silently allowing); external
system/developermessages arerejected with 400; permission checks now cover routes that previously had
authentication only, and
USER.mdis scoped per user.([fix(community): Fail closed on delegated fetch backends to close SSRF gap #6531], [fix(community): refuse non-global addresses in the shared SSRF guard #6202], [fix(telegram): deny all users when allowed_users has no valid ID #6230], [fix(channels): parse Slack and DingTalk allowed_users fail-closed #6742], [fix(qq): parse allowed_users with the shared fail-closed allowlist #6730], [fix(security): prevent external system-role message injection #5651], [fix(authz): cover create/run/memory/agent routes with permission checks; scope USER.md per user #4989])
superscript
COM¹–COM³andCONIN$/CONOUT$) are rejected acrossuploads, sandbox writes and skill files; same-name uploads claim the next
_Nsuffix instead of truncating; converted Markdown companions neverreplace an existing file; rejected attachments are recorded in
skipped_filesandsuccessmeans at least one file stored; outline/previewrecords missing timestamps are rejected — re-upload to restore conversion.
([fix(paths): reject Windows reserved names on host-visible paths #6102], [fix(paths): reject superscript Windows device names #6148], [fix(paths): reject Windows console device aliases #6279], [fix(uploads): reject Windows staging aliases before batch ingestion #6149], [fix(uploads): never truncate an existing upload on same-name collision #7388], [fix(client): preserve existing Markdown when publishing upload companions #7054], [fix(uploads): report unsafe filenames as skipped instead of success #7165], [fix: invalidate converted uploads after same-size source edits #6165])
/now lists only the built-in/goaland/compact; select skills as inline@references instead.([feat(frontend): reserve slash menu for built-in commands #6154])
waitendpoints return an SDK-compatible error envelope for completedfailed runs; an explicit empty
event_typesfilter matches nothing; keyedresume fails closed (503) for RunStores that can't persist request identity;
web-editor MCP connections become per-user; SQLite
file:URIs arerejected; integer config fields reject YAML booleans;
gpt-6family modelsdefault to the Responses API;
--print/--jsonexit 1 on LLM-errorfallback. ([fix(gateway): answer malformed run bodies with 4xx instead of 500 #7362], [fix: propagate current run failures from wait endpoints #6217], [fix(events): honor empty event type filters in DB store #7352], [fix(runtime): require explicit resume identity capability #6571], [fix(gateway): make resume retries idempotent #6499], [fix(mcp): isolate personal connections from deployment configuration #5966], [fix(persistence): reject SQLite file: URIs instead of opening them as filenames #6069],
[fix(config): reject boolean loop detection thresholds #6017], [fix(config): reject boolean integer settings in app_config #6171], [fix(models): route gpt-6 tool calls through the OpenAI Responses API by default #6863], [fix(cli): exit non-zero when a headless run ends in an LLM error fallback #6056])
✨ Highlights
create and manage owner-bound schedules with per-run goals, stop conditions
and safety caps ([feat(scheduler): create conversation schedules and support agent self-stop #6229], [feat(scheduled-tasks): finish the user-facing side of conversation schedules (RFC #6340, 1/2) #6378]); lifecycle and limits are at parity
across page, REST and chat, with a live task card in the web chat ([feat(scheduled-tasks): finish the user-facing side of conversation schedules (RFC #6340, 1/2) #6378]);
fair execution slots cap each owner (
max_concurrent_runs_per_user) and thewaiting queue drains owner by owner ([feat(scheduled-tasks): signals, honest notices and fair queueing for server-started runs (RFC #6340, 2/2) #6393]); lifecycle events are recorded
durably in the creating chat ([feat(scheduled-tasks): signals, honest notices and fair queueing for server-started runs (RFC #6340, 2/2) #6393]); IM notices are honest, localized and
self-contained ([feat(scheduled-tasks): signals, honest notices and fair queueing for server-started runs (RFC #6340, 2/2) #6393]); tasks are searchable by title or prompt ([feat(scheduled-tasks): search task titles and prompts #5355]),
and agent stops, auto-pauses and goal misses are explained in plain language
with links to the deciding run. ([feat(scheduled-tasks): show goal outcomes and end conditions in run history #6326])
via per-provider leader leases with fencing epochs and automatic failover
([feat(channels): arbitrate IM transports with a per-provider leader lease #6843], [feat(channels): add channels.leader_lease and gate an explicitly disabled lease under multi-process signals #6746]); Helm and compose gain a multi-replica story with a shared
JWT secret, surge-then-drain rollouts and PodDisruptionBudgets ([fix(deploy): wire the multi-replica prerequisites into the Helm chart and compose #6347]);
provisioner replicas keep sandbox creation alive through restarts ([fix(helm): let the sandbox provisioner run with several replicas #6543]);
the workflows extension coordinates across Gateways over shared Postgres
with an
ExecutionFence([feat(workflows): add multi-Gateway coordination and session-authorized failover #7138]) and authenticated restart recovery([feat(workflows): add durable restart recovery (Phase 3) #7075]); and
make dev-multiboots a two-Gateway cluster locally.([feat(dev): add a two-Gateway multi-instance harness script #6613])
pages, conversation actions and model tools in one namespace ([feat(plugins): full-stack plugin APIs and bookmarks #5647]) with
versioned manifests and credentialed ES-module graphs ([feat(plugins): support manifests and static asset directories #5685]); plugins can
contribute composer
@mention providers ([feat(frontend): support extension providers in composer mentions #6189]) and drive full Agentruns through the Gateway's normal admission ([feat(extensions): add host-bound Agent run control #6190]); packaged extensions
can invoke host models under operator-granted limits ([feat(extensions): grant bounded host model invocation #5796]); agent-teams
lets Custom Agents collaborate as a team via
@mentions, with a sharedactivity feed and durable team state ([feat(extensions): add full Custom Agent team collaboration plugin #6243], [feat(extensions): add durable team public state #7611]); a default-disabled
Script workflows extension sequences and branches Agent runs in a
capability-restricted QuickJS subprocess ([feat(extensions): add opt-in script workflow MVP #6858], [feat(workflows): add Phase 2 approvals, immutable artifacts and configuration evidence #6920]); new Jev example
plugins prune stale context, classify texts and flag injected instructions
([feat(plugins): add standalone Jev context pruning example #5731], [feat(plugins): add standalone Jev text classification example #5735], [feat(examples): add opt-in advisory fetched-content screening #5833]); the extension API advances 0.2.2 → 0.2.7 with
run-evidence readers, structured
PluginToolResultartifacts and guardedskill-evolution host APIs ([feat(extensions): add request-scoped run evidence access #5727], [feat(extensions): allow ModelTool plugins to return structured artifacts #6861], [feat(skills): add guarded evolution host APIs #5645]); and the Capability
Center gallery ships a bundled extension catalog. ([feat(extensions): show the bundled catalog by default #6188])
catalogs are decoupled from RAGFlow through extension-API registration
([feat(knowledge): decouple scope and read-only catalogs (phase 2) #6904]); retrieval results, capabilities and source evidence are unified
across LightRAG and RAGFlow, and citations survive budgets, delegation and
replay ([feat(knowledge): unify read-only retrieval and source contracts (phase 1) #6716]); RAGFlow results carry verifiable inline citations backed
by bounded native excerpts ([feat(knowledge): add verifiable RAGFlow source citations #5551]); custom-agent chats can scope retrieval
per message with fail-closed allowlists ([feat(knowledge): add per-message RAGFlow retrieval scope #5238]); and new WeKnora and
knowledge-platform extensions add managed ingestion plus hierarchical
logical spaces with inherited roles. ([feat(knowledge): add WeKnora read-only extension (phase 3) #6950], [feat(knowledge): add optional ingestion and processing extension #7293], [feat(knowledge): add optional platform connections and hierarchical spaces (phase 3) #7417])
Settings → Models with an encrypted catalog, optimistic revision checks and
provider presets ([feat(models): manage shared models from Settings #5596], [feat(models): register managed models with provider presets #6757]); a declarative reasoning capability
contract (
thinking,dialect, effort maps) normalizes thinking policyacross lead agent, subagents and helpers and drives the frontend effort menu
([feat(models): add declarative reasoning capability contract #5780]); scoped model controls resolve natural-language model selection
per task/turn/conversation, with up to 16 aliases per model and per-child
overrides ([feat(models): add scoped model controls to Web chats #7249]); and new presets and providers arrive: Kimi K3.1
([feat(models): add managed Kimi K3.1 Pro capabilities #7566]), Opper ([feat(wizard): add Opper as an LLM provider #6399]), API Route ([feat: add API Route setup wizard preset #6579]), You.com ([feat(community): add You.com web search provider #6515]), Webz
News ([feat(search): add Webz news search provider and setup integration #5880]), Unbrowse ([feat(community): add Unbrowse web fetch provider #5981]), plus Exa-compatible endpoints via
EXA_BASE_URL. ([feat(exa): support a configurable Exa-compatible base URL #7306])official Open Platform WebSocket with no public callback URL ([feat(channel): add QQ WebSocket channel #6081]);
WeChat pairs by scanning a QR code from the web UI ([feat(channels): support WeChat QR login from the web UI #5582]); proactive
notifications generalize to Feishu, QQ and WeCom with opt-in run-completion,
failure and human-input notices from a durable outbox ([feat(channels): generalize group notifications and add opt-in task/goal IM notices #6903], [feat(channels): control IM notifications and add WeChat/QQ delivery #6850],
[feat(scheduler): push scheduled task results to IM channels via a notification outbox #4843]); Feishu renders clarifications as interactive button cards ([feat(channels): Feishu interactive clarification cards #6670]);
/modelswitches an IM conversation's model in place ([feat(channels): select the model per conversation via /model #6534]);/connectbindings move to a persistent dialog with copyable codes ([fix(channels): keep IM binding codes on screen and report goal state in replies #7591]); and IM
runs survive Gateway restarts with
on_disconnect: continueand idempotentredeliveries. ([fix(channels): keep IM runs alive when the creating request disconnects #6876], [fix(channels): run each IM message once across instances, restarts and late redeliveries #7104])
user.cognitiveStylestores collaboration preferencesseparately from personal context ([feat(memory): add cognitiveStyle for collaboration preferences #3182]); opt-in lexical relevance ranking
blends IDF-weighted query coverage with confidence using a CJK bigram
tokenizer ([feat(memory): add opt-in relevance-aware retrieval ranking #5251]); tolerant Markdown storage recovers corrupt or partially
written files instead of crashing the agent ([feat(memory): opt-in tolerant MarkdownMemoryStorage (#3124) #5545]); the management API
and a new
memory_gettool work per agent bucket ([feat(memory): scope management API by agent #5565], [feat(memory): add scoped fact lookup by ID #6443]); opt-inTypeSafe pre-screening can skip extraction calls and classify
reinforcement/correction signals ([feat(memory): add Jev pre-screening and signal classification over a shared TypeSafe client #5906]); and extraction watermarks
persist across restarts so already-extracted turns don't re-run. ([fix(memory): persist the DeerMem extraction watermark across restarts and replicas #7205])
skills into a searchable catalog with configuration forms ([feat(capabilities): unify catalog, plugin configuration and agent selection #5497]); the
composer gains inline
@references to skills, project documents and otherconversations, up to 16 per message ([feat: add inline @ references with multiple skill activation #6063]); tool-produced paths, URLs and
task ids become short stable
art_xxxartifact handles that survive contextcompaction ([feat(agents): persist tool artifacts as resolvable handles (#4676) #4929]); the answer toolbar shows which skills a run loaded,
with inspectable snapshots ([feat(skills): show loaded skill snapshots in answer details #5758]); Custom Agents export/import as portable
packages ([feat(agents): add portable custom agent packages #6127]); and projects gain a budgeted
search_project_documentstool ([ feat(projects): search shelf documents by keyword #7286]) and opt-in LLM summaries on the document shelf. ([feat(projects): best-effort LLM summaries for shelf documents #6581])
run.terminaltrace eventseals completed runs ([feat(runtime): publish owner-confirmed run.terminal evidence #7134]); run events carry per-LLM-call telemetry —
caller category, provider, model, stop reason, token counts ([feat(runtime): add per-LLM-call telemetry and summarization no-op event #6236]);
inspect_run_statusgives agents a content-free usage and context-fillsnapshot ([feat(agent): add read-only run status introspection (phase 1) #7297]); client context hints and the web UI locale travel into
runs as sanitized request-only context ([feat(context): support client context capabilities #7051], [feat(runtime): honor the web UI language in replies and DeerMem updates #7294]); and subagent
batches gain browser result review with cited evidence ([feat(batch): add browser review for persisted results #7006]),
read_batch_resultinspection ([feat(subagents): add bounded same-thread batch result inspection #6416]), RAG evidence snapshots ([feat(subagents): preserve RAG evidence in durable batch exports #6413]),and
csv-columns/json-validacceptance criteria. ([feat(acceptance): verify required CSV header columns #6714], [feat: add bounded JSON syntax acceptance criteria #5947])duration-only leaves while protecting the resume head ([feat(gateway): checkpoint retention service on the #4189 deletion contract #5308]); a
content-addressed blob store (sha256-verified, opt-in) lets load-balanced
Gateways restore externalized tool results and viewed images from shared
bytes instead of hitting broken local paths. ([feat(storage): add a content-addressed blob store contract (issue #4189 item 2) #5361], [feat(storage): restore externalized tool outputs across Gateways #6159], [feat(storage): persist viewed images across Gateway instances #6160])
with a new admin Users page ([feat(authz): account disable/enable across every authentication surface (#4063 gap 3) #6566], [feat(authz): admin user-management UI and disabled-account copy (#4063 gap 3) #6641]); custom RBAC roles are
assignable through the admin API ([feat(authz): admin role-assignment API for configured custom roles (#4063 gap 2) #6382]); the
skillspolicy governsvisibility on listing surfaces without an existence oracle ([feat(authz): filter per-caller skill visibility on the skill listing surfaces (#4063 Phase 4) #5489]); opt-in
deterministic PII redaction rewrites model-bound context with stable
placeholders ([feat(middleware): add deterministic PII redaction for model-bound context #5527]); an opt-in TypeSafe risk gate denies tool calls at a
configurable threshold ([feat(guardrails): add TypeSafe (Jev) pre-execution tool risk gate #5712]); and plugin actions and management routes
are authorization-gated. ([feat(authz): add plugin resource targets, provenance, action and management enforcement #5842])
🚀 Performance
previously produced the unchanged summary. ([perf(summarization): skip the summarizer LLM call for an input already proven no-op #6318])
response for managed async runs and native subagents. ([feat(sandbox): add bounded AIO prewarming and lifecycle benchmarks #6998])
are rediscovered ([feat(mcp): reuse unchanged per-server tool discovery results #6795]); config edits retire only the affected stdio
sessions via connection fingerprints. ([fix(mcp): selectively reconcile changed deployment servers #6487])
InfoQuest, Tavily, Firecrawl and DuckDuckGo image search become coroutines
or offload to workers ([feat(community): migrate You.com tools to httpx.AsyncClient #7186], [feat(community): migrate GroundRoute tools to httpx.AsyncClient #7183], [feat(community): migrate infoquest client to httpx.AsyncClient #5782], [feat(community): migrate tavily tools to AsyncTavilyClient #5784], [feat(community): migrate firecrawl tools to AsyncFirecrawlApp #5786],
[feat(community): offload image search to a thread via asyncio.to_thread #5788]); config reads, auth routes and route-permission resolution load in
workers instead of stat+sha256 per request. ([fix(community): resolve async tool config off the event loop #7580], [fix(auth): read the live config off the event loop on public auth routes #7576], [fix(gateway): read the route authorization config off the event loop #7557])
blocking SDK call that stalled every stream ([fix(channels): give DingTalk AI cards the channel token, not the SDK fetch #7597]); Feishu stores thread
aliases once per reply instead of rewriting the store per chunk ([fix(channels): write each Feishu thread alias once per streamed reply #7593]);
channel
stop()tears down SDK threads off the loop. ([fix(channels): keep blocking SDK teardown off the event loop in stop() #6134])cold import no longer stalls all requests ([fix(gateway): resolve the agent factory off the event loop #6206]); subagent batch export
reads 10 rows per page and item lists return previews, cutting hundreds of
MB per request ([fix(gateway): read subagent batch exports in small pages #6706], [fix(subagents): leave full results out of the polled batch item list #6781]); skills, user-profile and thread-deletion
routes offload directory scans and rmtree to workers. ([fix(gateway): load skills off the event loop in the listing and detail routes #5945], [fix(gateway): keep /api/user-profile filesystem work off the event loop #5935],
[fix(threads): remove a deleted thread's files off the event loop #6319])
Cache-Control: no-cache, no-transformso compressingproxies stop buffering run streams into bursts. ([feat(scheduled-tasks): signals, honest notices and fair queueing for server-started runs (RFC #6340, 2/2) #6393])
view_imagere-injection caps at four images / 28 MiB per request, sore-viewing an older image no longer resends the whole image history.
([fix(agents): bound viewed image context per model request #6828], [fix(view-image): bound re-injected image context with per-request budgets #6829])
shelf-document conversion writes outside the SQLite write lock. ([fix(projects): convert shelf documents outside the SQLite write lock #6305])
strip_think_blocks([fix(llm): avoid quadratic think-block stripping on unclosed tags #5854]) andskill-review link extraction ([fix(skills): keep unmatched markdown brackets out of quadratic backtracking #5884]) lose their quadratic paths;
structured tool-output previews respect excerpt budgets ([fix(agents): bound structured tool-output previews #6422]); one-shot
chat models build off-loop ([fix(utils): construct one-shot models off the event loop #7484]); memory pre-summary saves run in a
worker ([fix(memory): flush pre-compaction memory off the event loop #7530]); E2B reconciliation stops
connect()-probing warm pools([fix(sandbox): stop E2B reconciliation from reviving warm-pool sandboxes #5562]); thread skill-view freshness fingerprints end a full NFS recopy
per acquire. ([fix(skills): stop rebuilding the thread skill view every turn on network #7333])
🔒 Security
web_searchprovider screens resultURLs through the shared public-URL policy ([fix(community): screen web_search result URLs in every provider #7037]); InfoQuest organic,
news and image results are screened ([fix(infoquest): screen result URLs through the shared public-URL owner #7031]); image search shares one
policy across DuckDuckGo, Brave and Serper with Unicode-host, NAT64 and
multicast coverage ([fix(image-search): share public result URL screening #6740], [fix(image-search): normalize Unicode host separators #6819]); the CGNAT range
100.64.0.0/10isrefused across web tools, browser and MCP ([fix(community): refuse non-global addresses in the shared SSRF guard #6202]); the agentic browser
routes TCP through a per-session SOCKS5 proxy that resolves DNS under SSRF
policy, defeating rebinding ([fix(browser): pin browser connections to the SSRF-vetted addresses #6201]); and Unbrowse redacts authenticated
finalUrls from error responses. ([fix(unbrowse): bound target error final URLs #7399])authentication only, and
USER.mdis scoped per user ([fix(authz): cover create/run/memory/agent routes with permission checks; scope USER.md per user #4989]); LiveBrowser WebSockets require
threads:write([fix(auth): enforce write permission for Live Browser WebSockets #5621]);view_imagerequiressandbox:execute([fix(sandbox): enforce execution authorization for image reads #5799]); the suggestions endpoint enforcesmodel:use([fix(suggestions): enforce model use authorization #5816]); skill activation is authorized at assembly, including via
read_fileof aSKILL.md([feat(authz): enforce skill authorization at assembly and slash-activation (#4063 Phase 3) #4541]); viewed-image blobs requireowner-checked
threads:read([fix(artifacts): authorize shared image reads and offload MIME lookup #7604]); and middleware-declared tools joinLayer-1 tool authorization. ([feat(authz): middleware-declared tool path Layer 1 now covers tools declared by middlewares #6105])
scoped to the authenticated user and thread, and external
input.sandboxisrejected ([fix(sandbox): scope restored references to authenticated threads #5736]); viewed-image host reads are scoped to the current thread
([fix(sandbox): scope viewed image host reads to the current thread #5824]); external
system/developermessages are rejected beforecheckpoint writes ([fix(security): prevent external system-role message injection #5651]); client uploads no longer write through symlinks
and document conversion reads private descriptor-backed copies ([fix(client): stop embedded uploads from writing through symlinks #5578],
[fix(uploads): convert the bytes we wrote, not the name they landed under #5611]); and Redis checkpoint purge escapes glob metacharacters so a custom
prefix can't delete another namespace. ([fix(checkpoints): confine Redis history purge to literal namespaces #6709])
sk-tokens, GitHub fine-grained PATs andGoogle API keys are detected in SkillScan and scrubbed from outgoing IM
notifications ([fix(skillscan): detect modern sk API tokens #6394], [fix(skillscan): detect fine-grained PATs and Google API keys, scan .zsh #6423]); MCP logs stop emitting credentials
([fix(mcp): avoid leaking credentials in discovery error logs #6794]); 500 details no longer echo host paths for uploads, skills,
custom agents and scheduled tasks ([fix(uploads): keep OSError text out of upload, delete and attach 500s #7217], [fix(agents): keep host paths out of agent and user-profile error details #7255], [fix(skills): keep load error text out of user-facing skill read 500s #7155]); urllib3
retry/header dumps redact signed URLs ([fix(logging): redact space-carrying origin-form targets in urllib3 retry lines #5745]); and IM channel credentials
gain at-rest encryption keyed by
DEER_FLOW_CREDENTIALS_KEY. ([fix(security): add DEER_FLOW_CREDENTIALS_KEY and wire the channel credentials cipher #6611])structure ([fix(skillscan): match fork bombs by shape, not spelling #6689]); the
.builtin is treated as asourceexecutioncontext, including process-substitution forms ([fix(sandbox): treat the . builtin as a source execution context #7247]);
curl | sudo -uvariants are caught ([fix(skillscan): catch a shell behind a sudo option that takes a value #6372], [fix(harness): detect shell pipe variants with sudo, full paths and other shells #6312]); binary magics are sniffed past up to
63 bytes of padding and a BOM across preflight, extraction guard, scan and
nested-zip checks ([fix(skills): sniff executable binaries past leading filler bytes #7081]); executable-content classification uses the
shared
is_code_filerule ([fix(skills): treat only top-level scripts/ paths as executable in skill_manage #7088]); Windows script suffixes counteverywhere ([fix(skills): windows interpreter scripts outside scripts/ escape security scanning #6321]); secret rules parse Python via AST, cutting false
positives ([fix(skillscan): read Python secret assignments from the AST #5648], [fix(skills): stop reporting an env path, shebang or flag as an env dump #6297], [fix(skillscan): classify env command launchers #6428], [fix(skillscan): report secrets bound by unpacking and container or conditional literals #6247], [fix(skills): report secret assignments whose key is quoted #6276]); a planted 300 MB
file can no longer OOM the scanner ([fix(skillscan): gate the directory scan file size before reading #6359]); and tool-discovery regexes are
bounded against ReDoS. ([fix(tools): bound deferred catalog regex execution #6676])
allowed_usersparse strictly onTelegram, Slack, DingTalk, QQ and Buzz ([fix(telegram): deny all users when allowed_users has no valid ID #6230], [fix(channels): parse Slack and DingTalk allowed_users fail-closed #6742], [fix(qq): parse allowed_users with the shared fail-closed allowlist #6730], [fix(buzz): treat a scalar allowed_users entry as one member pubkey #6811]);
PII redaction covers the queued memory payload,
invalid_tool_calls, andthe
/goalevaluator's inputs ([feat(memory): redact queued memory payloads (pii_redaction follow-up) #5577], [fix(memory): redact invalid and legacy tool-call arguments #6387], [feat(goal): show why a chat goal stopped and when it was met, and redact the goal evaluator and continuation input #6556]); first-adminclaiming is atomic under concurrency ([fix(auth): claim the first admin atomically instead of counting first #5776]);
image/svgis classified asactive content ([fix(gateway): classify Windows SVG MIME alias as active content #5594]); and model-written
ms-msdt:-style tool-step linksare gated by a scheme allowlist. ([fix(frontend): gate tool-step links through the href scheme allowlist #5526])
🐛 Notable fixes
orphan_recoveredstop reason ([fix(runtime): mark cancel takeovers with the orphan-recovery stop reason #6674]); consumers recover when a producerdies between terminal persistence and stream END ([fix(runtime): recover terminal Redis streams after producer loss #7093]); disappeared
Redis streams and ahead-of-retention cursors signal a replay gap instead of
heartbeating forever ([fix(streaming): report gaps when Redis retention disappears #6951], [fix(streaming): report replay gap for future Redis Last-Event-ID #6605], [fix(streaming): avoid false replay gaps after cursor eviction #6812]); shutdown drains runs with
staged-but-uncommitted terminal status ([fix(runtime): drain staged-terminal runs on shutdown before checkpointer teardown #6620]); heartbeats keep leases
through deferred terminal writes ([fix(runtime): keep renewing the lease while a staged terminal run finalizes #6263]); interrupted runs persist their
streamed assistant text ([fix(runtime): retain interrupted assistant output #7153]); delta-mode resume refuses while
checkpoints hold pending execution ([fix(runtime): reject delta resumes that discard pending tools #7441]); JSONL stores survive torn
final lines and remember deleted sequence watermarks ([fix(events): preserve JSONL framing after interrupted appends #6520], [fix(events): preserve JSONL sequences after run deletion and restart #6546]);
Gateway-issued message ids let rejoining clients dedupe ([fix(gateway): preserve IDs for id-less run inputs #7349]); and a
per-thread event high-water mark prevents sequence reuse. ([fix(events): preserve thread seq watermark across run deletion #6477])
BoxLite, Tenki and OpenSandbox ([fix(sandbox): keep shutdown retryable while idle reaper is live #6231], [fix(e2b): keep shutdown retryable while maintenance workers are live #6244], [fix(aio): keep renewal shutdown retryable #6317], [fix(boxlite): keep private-loop shutdown retryable #6283],
[fix(sandbox): make failed AIO teardown safely retryable #7001]);
sandbox.bash_command_timeoutis enforced with server-side hardtimeouts ([fix(sandbox): enforce bash command timeouts in AIO sandbox #5634], [fix(sandbox): bound AIO list_dir with a directory deadline #5662], [fix(sandbox): forward bash command timeout to remote providers #6782]); ambiguous AIO session creation is
tombstoned instead of replayed ([fix(sandbox): fence ambiguous AIO session creation ownership #5711]); AIO probe scripts run in a
subshell so they can't wedge the persistent shell ([fix(sandbox): wrap implicit-session probe commands in a subshell to prevent wedged shell #5546]); remote
glob/grep/read reach LocalSandbox parity — newline-only records,
-Fliteral patterns, quoted
--include, exact-cap probing ([fix(sandbox): number remote read_file line ranges like LocalSandbox #6585], [fix(e2b): preserve special characters in search results #6573],[fix(e2b): grep literal patterns verbatim instead of re-escaped #6952], [fix(e2b): quote grep filename filters #6529], [fix(sandbox): search long lines for literal grep patterns #6969], [fix(sandbox): report an exactly-full search result as complete in the remote providers #5534]); the local path guard closes
$HOME-prefix and awk-program bypasses ([fix(sandbox): reject shell-variable path prefixes in the host-bash guard #6930], [fix(sandbox): exempt awk field arithmetic from the shell-variable path guard #7023]); PowerShell pathescaping survives spaces,
$and quotes ([fix(sandbox): preserve mapped path spaces in PowerShell commands #7245]); CRLF/LF and UTF-8fidelity hold on Windows and Git Bash ([fix(sandbox): keep line endings in local sandbox reads, writes and str_replace #6343], [fix(sandbox): decode Git Bash output as UTF-8 so CJK output is not garbled #6548], [fix(sandbox): pin utf-8 decoding on the local sandbox backend's subprocess calls #5905], [fix(sandbox): decode PowerShell native-child output with the locale fallback #6727]);
E2B restores persisted workspace before exposure and releases slots when
VMs vanish ([fix(sandbox): restore saved thread files when creating E2B sandboxes #6824], [fix(sandbox): release local E2B capacity when reconnect confirms the VM is gone #6547]); and OpenSandbox mirrors outputs before warm
parking. ([fix(opensandbox): mirror thread outputs before warm reuse #6699])
([fix(channels): give DingTalk AI cards the channel token, not the SDK fetch #7597]); channel stops surface
ChannelStopTimeoutand retain liveworkers instead of orphaning them ([fix(channels): make Feishu and DingTalk transports stoppable in-process #6798], [fix(channels): retain live SDK workers after stop timeout #6215], [fix(telegram): retain polling worker on stop timeout #6333]); lifecycle
mutations serialize per channel with epoch-versioned updates ([fix(channels): serialize lifecycle mutations under per-channel lock #6304],
[fix(channels): fence restart during service shutdown #6503], [fix(channels): fence manager startup across completed shutdown #6521]); multi-worker saves no longer overwrite each other's bots
([fix(channels): merge runtime channel config saves across Gateway workers #6844]); IM redeliveries carry idempotency keys ([fix(channels): run each IM message once across instances, restarts and late redeliveries #7104]); Slack admits
file_sharemessages and Discord admits file-only messages ([fix(slack): accept file_share events and download inbound files #6855],[fix(discord): accept inbound attachments and file-only messages #6857]); Telegram splits by UTF-16 code units so emoji replies don't 400
([fix(channels): split Telegram messages by UTF-16 code units, not code points #6067], [fix(telegram): measure the rich-message cap in UTF-16 code units #6174]); Slack entity decoding is restored ([fix(channels): decode Slack entity escapes in inbound message text #6045]); concurrent
attachment uploads claim filenames with
O_EXCL([fix(channels): preserve inbound files on concurrent uploads #6476]); andFeishu/DingTalk inbound files get sandbox-readable permissions. ([fix(channels): grant sandbox read bits to Feishu/DingTalk inbound uploads #5998])
thinking blocks, fixing HTTP 400 thread death ([fix(models): keep Claude prompt-cache markers out of thread messages #5892], [fix(models): exclude thinking blocks from Claude cache breakpoints #6339]);
extended-thinking budgets validate against real constraints ([fix(models): validate Claude extended-thinking budgets #6060]);
Retry-After handling is unified, including HTTP-date parsing and
status-less streaming errors ([fix(models): honor HTTP-date Retry-After hints in Claude retries #7295], [fix(codex): honor Retry-After in the provider retry loop #7151], [fix(middleware): guard OverflowError when parsing non-finite Retry-After hints #6455], [fix(llm-error): retry provider errors reported inside a 200 stream #6897]);
DeepSeek/Moonshot cache-write usage is restored ([fix(models): preserve Moonshot cache_write_tokens in usage metadata #7428]); MiniMax keeps
reasoning_content([fix(models): preserve MiniMax reasoning_content responses #6071]); Codex completes onresponse.completedandtolerates null usage and empty tool-call ids ([fix(models): honor Codex SSE terminal events #6155], [fix(models): preserve completed Codex output when usage is null #6335], [fix(models): omit Codex tool results with blank call IDs #6234]);
MindIE extracts XML tool calls before decoding escaped newlines ([fix(models): preserve escaped newlines in MindIE XML JSON arguments #6334],
[fix(models): parse numeric MindIE XML tool arguments #6030], [fix(models): keep escaped newlines inside fences in MindIE native stream #6478]); vLLM falls back to legacy reasoning fields ([fix(vllm): fall back to legacy reasoning_content field #6048]);
credential loading tolerates malformed sources instead of losing valid
fallbacks ([fix(models): fall through an unusable credential source instead of ending the lookup #7140], [fix(models): skip undecodable Claude descriptor handoffs #6323], [fix(models): skip Claude credentials sources with a non-numeric expiresAt #5591], [fix(models): tolerate a null Codex account_id before it reaches the request header #5601], [fix(models): decode CLI credential files as UTF-8 #6277]); and the circuit
breaker fences settlements by generation with monotonic recovery. ([fix(llm): fence circuit probe settlement #5602],
[fix(agents): measure LLM circuit breaker recovery on a monotonic clock #6806])
shutdown ([fix(mcp): close thread-scoped MCP sessions on thread deletion #5956], [fix(gateway): close pooled MCP sessions on shutdown #5961]); notification launches are atomic with
dead-lettering for deterministic rejections ([fix(mcp): reserve notification launches atomically #5965], [fix(mcp): consume resolved notification snapshots instead of launching empty events #6693]); durable-task
claims renew via heartbeat and fence stale writers ([fix(mcp): retain durable control claims through slow calls and cancellation #6780], [fix(mcp): skip tasks a cancel released while the poll claim was in flight #6859],
[fix(mcp): make durable task claims cancellation-safe #4966]); stdio bindings carry epochs and ownership domains so equal server
names can't reuse the wrong session ([fix(mcp): fence superseded stdio server bindings #6435], [fix(mcp): isolate pooled sessions by ownership domain #6133]); cache invalidation
fires only on effective config changes, and error logs drop credentials
([fix(mcp): invalidate cache only for effective MCP config changes #5750]); workspace-relative path correlation handles whitespace and
Windows separators ([fix(mcp): resolve generated file paths containing spaces #5819], [fix(mcp): preserve backslash paths during bare-filename rewriting #5815], [fix(mcp): insert bare-filename rewrites literally #5522]); and
ResourceLinkresultsbecome gated placeholders with read-time healing of poisoned threads.
([fix(mcp): stop persisting URL-sourced ResourceLink blocks and heal bricked threads #6606])
are already terminal, so a failed completion hook no longer stalls schedules
([fix(scheduler): reconcile running occurrences from terminal durable runs #7269]); manual trials no longer consume the recurring cursor or clear
claim leases ([fix(scheduler): preserve recurring cadence after manual trials #7209], [fix(scheduler): keep a one-time task's scheduled run after an early trial #6512]); DST fall-back runs fixed-hour cron once and
one-time edits preserve the saved instant ([fix(scheduler): prevent fixed-hour cron tasks from firing twice on DST fall-back #6066], [fix(frontend): align one-time DST conversion with the scheduler #6756], [fix(scheduled-tasks): preserve unchanged one-time execution instants #5330]);
dispatch and lease races are closed under database locks ([fix(scheduler): re-read the run row under the writer before guarding update_status #5860], [fix(scheduler): read the lease owner under SQLite's writer lock #5777],
[fix(scheduler): preserve fresh launch claims during expired recovery #6975]); and only real conflicts retry — plain 409s fail the occurrence
with the reason persisted. ([fix(scheduler): treat only busy-thread 409s as retryable #7196])
via journal-sequence re-anchoring ([fix(threads): re-anchor re-persisted history rows to their earliest seq #6128], [fix(frontend): preserve first-turn order during chat creation #6074]); failed sends keep the
draft, quotes and attachments ([fix(frontend): keep composer context when an attachment upload fails #6412], [fix(frontend): keep the side-chat draft when a send fails #6407]); reconnect rejoins the live
run and replays gaps safely ([fix(frontend): rejoin active runs after reopening chats #5536], [fix(frontend): retry a failed same-tab reconnect after refresh #6400], [fix(frontend): preserve newer reconnect pointers during gap recovery #6662], [fix(frontend): preserve submitted input during initial stream gap recovery #6800]); HTML
previews honor only a real
<base>element ([fix(frontend): ignore literal base tags in HTML artifact previews #6176]); CSV/TSV samplessplitting a CRLF no longer reject complete rows ([fix(artifacts): handle CRLF splits in CSV/TSV previews #6180]); artifact saves
key on file identity so same-size replacements refresh ([fix(artifacts): invalidate digests after external file replacement #6195]);
Dockerfile/Makefileopen as editable source ([fix(frontend): recognize extensionless artifact source files #6091]); unsaved draftspersist per account, conversation and path ([fix(artifacts): preserve account-scoped drafts across navigation #6700]); isolated HTML previews
get authenticated static resources ([fix(artifacts): authenticate isolated HTML preview resources #6701]); docs search ships a pinned
pagefind index ([fix(frontend): make docs search work on desktop and mobile #6715]); truncation counts code points so emoji survive
([fix(frontend): count code points when previewing table cells and memory facts #7503], [fix(scheduled-tasks): count code points when shortening a run summary #7452], [fix(frontend): bound notification bodies by code points #7474]); and Escape/Enter stay with the IME during
composition. ([fix(composer): keep IME-composition Enter out of the slash-skill catalog #5741], [fix(frontend): ignore message edit shortcuts during IME composition #6768])
policy instead of crashing runs ([fix(memory): honor Mem0 read policies during context deduplication #7561], [fix(memory): apply Mem0 read policies to record conversion failures #7307], [fix(memory): honor Mem0 failure policies for malformed JSON responses #7202]); planning text
carrying tool-call intent no longer becomes memory content ([fix(memory): exclude invalid and raw assistant tool calls #6379]); free
form fact categories like
personalare preserved ([fix(memory): keep non-core fact categories instead of rewriting to other #6733]); explicitconfidence: 0.0ranks below the default ([fix(memory): keep a zero confidence from scoring as the default #5586]); reads ofdeleted-since-listed facts no longer 500 ([fix(memory): treat facts deleted during unlocked DeerMem scans as removed #6255]); jieba tokenization drops
punctuation-only tokens ([fix(memory): drop punctuation tokens from jieba relevance ranking #6388]); quarantined summaries preserve the
original bytes ([fix(memory): quarantine unreadable memory summaries #7076]); and BOM-prefixed facts and summaries load.
([fix(memory): parse a UTF-8 BOM in the DeerMem summary instead of raising corruption #7084], [fix(memory): parse a BOM-prefixed DeerMem fact instead of calling it corruption #7116])
Center instead of vanishing ([feat(skills): show YAML load errors for owned custom skills #6623]); symlink cycles no longer cause
multi-second rescans ([fix(skills): prune ancestor cycles during discovery #5921]); managed skill discovery stops at
SKILL.mdpackage boundaries ([fix(skills): stop integration discovery at package boundaries #5890]); skill toggles work without anexisting
extensions_config.jsonand reset caches on every replica([fix(skills): create a missing extensions_config.json in the project root #7048], [fix(skills): share skill cache invalidation across Gateway replicas #6495], [fix(multi-instance): publish skill_manage cache resets and tolerate a concurrent namespace create #7099]); ppt-generation fixes EXIF orientation and
alpha backgrounds ([fix(skills): preserve EXIF orientation in generated presentations #7193], [fix(skills): preserve transparency in composed PowerPoint slides #7312]); data-analysis exports handle bare
filenames, NaN, duplicate columns and Markdown escaping ([fix(skills): support basename data-analysis exports #7132], [fix(skills): reject non-finite JSON export values before writing #7456],
[fix(skills): preserve Markdown table cells in data-analysis exports #7163], [fix(skills): reject lossy JSON exports with duplicate columns #7305]); podcast, video and chart CLIs exit non-zero with stderr
diagnostics ([fix(skills): report podcast CLI failures with a nonzero status #7236], [fix(video): preserve reference image MIME types #7340], [fix(chart-visualization): wire generate_spreadsheet and fail CLI nonzero #7395]); and github-research filters PRs out
of issue listings and paginates fully. ([fix(skills): exclude pull requests from GitHub issue research #7516])
disconnected client can't silently drop a commit, across agents, managed
models, subagents, memory, MCP, projects and channels ([fix(gateway): drain managed subagent mutations on cancellation #6023], [fix(gateway): drain managed model saves on cancellation #6024],
[fix(gateway): drain agent and user-profile writes across cancellation #6087], [fix(memory): drain persistence mutations on cancellation #6092], [fix(mcp): drain personal config mutations on cancellation #6093], [fix(channels): keep runtime config commit owned across cancellation #6198]); Postgres bootstrap survives
duplicate-database races and preserves DSN query bytes ([fix(persistence): treat a peer's database creation as success #7489], [fix(persistence): keep a literal plus in a postgres DSN query #5978]);
advisory locks poll so long migrations don't time out ([fix(persistence): wait out a peer's Postgres bootstrap past command_timeout #6306]); staged temp
files register for cleanup before content is written ([fix(persistence): discard the staged temp when the write itself fails #7504], [fix(extensions,persistence): keep staged-write cleanup best-effort #7485]);
schema-drift warnings compare the connected database's spellings ([fix(persistence): compare safe_add_column drift as the database spells it #6865]);
concurrent first ratings upsert instead of 500 ([fix(persistence): retry feedback and conversation upserts that lose a race #6852]); and integer
metadata filters match without overflow. ([fix(persistence): match integer metadata without overflowing stored values #6000])
([fix(goal): show the active goal to the agent on every model call #5917]); evaluator evidence includes assistant tool calls, tool results
and Human Input Card answers ([fix(goal): show the goal evaluator the tool calls and results #6068], [fix(agent,goal): stop coaching scripts when no bash tool is bound, and show the goal evaluator Human Input Card answers #6082]); a satisfied goal survives
failed artifact delivery ([fix(goal): preserve active goals when artifact delivery fails #5980]);
loop_cappedstops continuations whilepreserving the goal ([fix(runtime): stop Goal continuations after loop hard stops #7167]); no-progress fingerprinting keys on observed
operations instead of evaluator text ([fix(goal): base no-progress counting on successful operations #7271]); evaluator failures persist
reasons ([fix(goal): record a failed evaluator call as a stand-down #6073]); and invalid goal writes are rejected with 400. ([fix(goal): validate goal text and preserve the current goal on message replay #6685])
code ([fix(tui): preserve multiline pasted input (carries #4630 forward) #6168]); refused messages stay in the composer for editing ([fix(tui): keep a refused message in the composer #6666],
[fix(tui): preserve drafts when Down is not navigating history #6219]); each run owns its cancellation flag and delivery identity ([fix(tui): isolate callbacks from interrupted runs #6302],
[fix(tui): guard conversation switching and invalid resume references #6249]);
/threadsand/modelpicker labels render as written ([fix(tui): show picker labels as written instead of parsing markup #7631]);headless
--print/--jsonfailures print errors instead of tracebacks([fix(tui): report headless --print/--json failures instead of leaking tracebacks #5963]); and streamed deltas append as sent so repeated tokens render
correctly. ([fix(tui): append streamed assistant deltas verbatim #7325])
that exhausted the turn limit after ~209 s ([fix(workflows): stop idle agent waits from consuming worker turns #7024]); agent-teams locks and
workers isolate per team ([fix(extensions): isolate team workers and rotate admissions #6754], [fix(extensions): exclude unadmitted team waits from active budget #6298]); lark skill, launcher and shim
publishes are atomic ([fix(lark): publish the skill file and manifest atomically #6683], [fix(lark): publish the extracted runtime and launcher atomically #6671], [fix(lark): publish the CLI runtime files atomically #6618]); and failed extension
rollbacks restore
pyproject.toml/uv.lock/config.yamlatomically withcaptured permission bits. ([fix(extensions): publish a snapshot restore atomically #6366])
📦 Deploy & ops
nginx:1.31.2-alpinein both compose files,dodging a musl 1.2.6
pwritev2seccomp denial that restart-loopedcontainers on older Docker releases ([fix(docker): pin nginx to 1.31.2-alpine before the musl 1.2.6 pwritev2 build #7643]); hosts without IPv6 start
([fix(docker): strip the IPv6 listen in the production nginx launcher too #5900]); project-document uploads get their own 100 MB streamed location
([fix(docker): give project-document uploads their own nginx location so they are not capped at 1 MB #5934]); the
/api/catch-all and skills routes allow 600 s ([fix(nginx): allow model-bound /api/ and /api/skills requests past 60 seconds #5524]);env_fileentries are optional so fresh checkouts runmake upwithout a.env([fix(deploy): let make up start on a checkout without .env files #6370]);DEER_FLOW_COMPOSE_ENV_FILEselects the root dotenvexplicitly ([feat(deploy): select production Compose env files #6522]); and production launch paths
execthe server soSIGTERM shuts down gracefully. ([fix(deploy): exec the gateway server so SIGTERM reaches uvicorn #6836])
AUTH_JWT_SECRET,DEER_FLOW_MULTI_INSTANCE, surge-then-drain rollouts andPodDisruptionBudgets ([fix(deploy): wire the multi-replica prerequisites into the Helm chart and compose #6347]);
provisioner.replicaskeeps sandboxcreation alive through provisioner restarts ([fix(helm): let the sandbox provisioner run with several replicas #6543]); a
databaseUserSecrethelper unifies Secret-name precedence ([fix(helm): give each Secret pair one precedence chain #7291]); the provisioner receives
DEER_FLOW_HOST_BASE_DIRfor shared-PVC skill projections ([fix(helm): align provisioner state root for PVC mounts #5625]); lark-cliimages are configurable ([fix(helm): plumb lark-cli runtime images into the provisioner #5835]); and the userdata PVC is declared once per
sandbox pod and reused via subPaths. ([fix(provisioner): reuse userdata PVC across sandbox mounts #7274])
/health/readyreports 503 while the Redis streambridge is unreachable. ([fix(gateway): probe the Redis stream bridge and provisioner in /health/ready #6447])
DEER_FLOW_ENV_FILEselects one explicit dotenv file([feat(config): select an explicit backend dotenv file #6227]); blank env vars are treated as unset instead of crashing startup
([fix(gateway): treat a blank GATEWAY_WORKERS and Lark broker port/timeout as unset #5840], [fix(gateway): treat a blank GATEWAY_HOST/GATEWAY_PORT as unset #5801]);
make config-upgradeandmake doctorhonorDEER_FLOW_CONFIG_PATHand.envoverrides ([fix(config): upgrade the config.yaml the Gateway loads #5991], [fix(doctor): check the config file the Gateway actually loads #5987], [fix(scripts): honor project root when detecting optional extras #6934]);every Gateway sharing one
extensions_config.jsonsees other processes'MCP/skill changes ([fix(config): revalidate the extensions config singleton by file signature #6386]); numeric config fields gain range bounds and
boolean rejection ([fix(config): guard numeric config fields that accepted silently-broken values #6026], [fix(database): validate pool settings and command_timeout against booleans and non-finite values #6009], [fix(config): reject invalid tool output budgets #6172]); and operator configs tolerate
a leading UTF-8 BOM. ([fix(config): accept UTF-8 BOM in extensions configuration #5983], [fix(config): accept a UTF-8 BOM in operator-authored env and secret files #7112])
channel credentials, MCP OAuth tokens, resume-request identity, account
suspension, per-thread event sequences, scheduled stop conditions and
workflow execution leases ([fix(channels): share chat-to-thread bindings through the database #6558], [feat(channels): share encrypted runtime channel config across Gateway instances #6988], [fix(mcp): keep rotating OAuth refresh tokens across restarts and Gateway instances #7207], [fix(gateway): make resume retries idempotent #6499], [feat(authz): account disable/enable across every authentication surface (#4063 gap 3) #6566],
[fix(events): preserve thread seq watermark across run deletion #6477], [feat(scheduled-tasks): finish the user-facing side of conversation schedules (RFC #6340, 1/2) #6378], [feat(workflows): add multi-Gateway coordination and session-authorized failover #7138]); Windows read-only files no longer wedge thread
deletion or trash purge ([fix(config): delete read-only thread directories on Windows #7446], [fix(projects): delete read-only documents when purging trash #7499]); and
migrate_user_isolation.pyreads owners from the configured database. ([fix(migration): read thread owners from the configured database #6450])
skill-testsCI job runs the public skill regressionsuite independently of package review ([ci(skill): run public skill script tests in Skill Review CI #7350]); CI checks bundled extension
packaging ([ci(extensions): check all bundled example packages #6788]);
make testruns four duration-balanced shards([test(backend): run make test in four parallel shards #6324]);
verify_versions.shrejects a staleuv.lock([fix(release): reject a stale backend/uv.lock in the version gate #6588]);make dev/startparse.envdotenv-style so values can't execute commands([fix(scripts): read the checkout .env as data in serve.sh #7606]);
make cleanrefuses to run while a live Docker Gateway mountsbackend/.deer-flow([fix(make): name what make clean deletes and refuse under a live gateway #6351]); dev ports are reclaimed across siblingworktrees with spaces in their paths ([fix(dev): keep whole worktree paths when serve.sh builds DEERFLOW_ROOTS #5856]);
make doctorvalidates realCLI credentials ([fix(doctor): validate CLI credential contents #5567]); and the setup wizard defaults Anthropic to
claude-sonnet-5-5, replacing the retired model id. ([fix(wizard): replace retired Claude Sonnet 4 default with claude-sonnet-5-5 #7585])🙌 Thanks
Huge thanks to the 213 contributors who landed 1068 merged PRs in the 2.2.0
milestone, and to everyone who filed issues, tested builds, and shared
feedback. DeerFlow 2.2 wouldn't exist without you.
In alphabetical order:
See the full author and PR list on the
milestone page.
This discussion was created from the release DeerFlow 2.2.0.
All reactions