mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-21 10:15:47 +00:00
* feat(memory): pluggable + self-contained memory system (MemoryManager plan phases 1 & 2)
Phase 1 — Pluggable (steps 0-10):
- ABC MemoryManager (9 methods) + singleton factory + drop-in backend discovery
- DeerMem default backend with core/ (storage/queue/updater/prompt/message_processing)
- NoopMemoryManager backend (proves pluggability)
- All call sites (middleware/hook/prompt/gateway/client/app) routed through manager
- hasattr capability probing for DeerMem-internal methods (no hard imports)
- MemoryConfig gains manager_class field; shared vs DeerMem-private annotated
Phase 2 — Self-contained DeerMem (steps 11-18):
- backend_config passthrough + DeerMemConfig (all DeerMem-private fields moved off MemoryConfig)
- DI: DeerMem owns storage/queue/updater/llm as instance attributes (no global singletons)
- Storage independence: core/paths.py with own root (~/.deermem or ),
factory auto-injects deer-flow's runtime_home() as absolute base_dir (zero-config)
- LLM independence: core/llm.py via langchain init_chat_model (no create_chat_model)
- Trace independence: optional tracing_callback replaces inject_langfuse_metadata/request_trace_context
- Message processing independence: hide_from_ui default-skip + optional should_keep_hidden_message hook
- Internal imports → relative (only deer_mem.py ABC import is host-relative)
- Carrier (deer_mem.py adapter) / portable (deermem/ config+core) split
- New tests: test_deermem_self_contained + test_memory_manager_pluggable; all memory tests migrated
- Other-agent demo: samples/other_agent_demo/ + automated portability test
- config.example.yaml memory section updated to phase-2 schema
* feat(memory): port consolidation + staleness fix into self-contained DeerMem; phase-2 host hooks
Port upstream #3996 (memory consolidation) and #3993 (staleness KeyError fix)
from origin/MemoryManager into the pluggable, self-contained DeerMem structure
(backends/deermem/deermem/), adapted to the DI MemoryUpdater (config injected,
not get_memory_config globals):
- DeerMemConfig: add consolidation_enabled (opt-in, default false) /
consolidation_min_facts / consolidation_max_groups_per_cycle /
consolidation_max_sources
- prompt.py: factsToConsolidate JSON field + {consolidation_section} placeholder
+ CONSOLIDATION_PROMPT constant
- updater.py: _coerce_source_confidence / _select_consolidation_candidates /
_build_consolidation_section module helpers (matching the existing
_select_stale_candidates style); consolidation normalization in
_normalize_memory_update_data; consolidation apply in _apply_updates (after
max_facts trim, with apply-time guardrails mirroring staleness); staleness
KeyError fix (f["id"] -> f.get("id") is not None) applied to both the
staleness guardrail and the consolidation allowed_source_ids comprehension
- config.example.yaml: consolidation section under memory.backend_config
- tests/test_memory_consolidation.py: 40 DI-adapted tests (running, not skipped)
incl. the staleness KeyError regression
Also includes in-flight phase-2 host-integration work: storage_path semantics
(any absolute/relative value = root dir) and host-default tracing_callback /
should_keep_hidden_message hooks injected into backend_config by the factory.
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(memory): add noop backend template and backends guide
- backends/noop/: complete drop-in template (config.py with zero deer-flow
imports, noop_manager.py with a 6-step new-backend walkthrough in its
docstring, commented optional fact-CRUD capabilities).
- backends/README.md: which files to touch when adding/swapping a backend,
the 5-item backend contract, and common pitfalls.
- manager.py: generalize backend examples in comments (drop mem0-specific
references).
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(frontend): guard formatTimeAgo against invalid timestamps
Return a neutral placeholder when the input date is invalid (e.g. an empty lastUpdated from a backend with no memories) instead of throwing 'Invalid time value' from date-fns.
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(memory): wire tool-driven memory mode through the MemoryManager ABC
tools.py (memory_search/add/update/delete) now calls get_memory_manager()
instead of the removed host memory module, so tool mode (memory.mode: tool)
works for any backend. DeerMem.search is implemented (case-insensitive
substring match, ranked by confidence) as a stand-in for the planned
semantic retrieval; noop.search returns [] (unchanged). Fact-CRUD tools
use getattr+callable probing -- backends lacking those ops (noop) get a
clear JSON error instead of crashing.
Tests: test_memory_tools rewired to mock the manager (handler tests) +
TestModeGating retained; test_memory_search now covers DeerMem.search;
pluggable stubs test updated (search no longer a stub).
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: resolve lint errors (import sorting, type annotation quotes, E402 in skipped tests)
* docs: restore explanatory comments in config.example.yaml memory section
* fix(security): port html-escape memory facts fix (#4097) to vendored DeerMem prompt.py
* fix(memory): address review + port dropped upstream memory fixes
Review blockers (vendored DeerMem):
- #4044 restore _escape_memory_for_prompt (current_memory blob in
MEMORY_UPDATE_PROMPT) - prevents </current_memory> breakout
- #4028 html.escape staleness-section cat/content in _build_staleness_section
- #4119 add _escape_summary for injection-path summaries (Work/Personal/
Current Focus/Recent/Earlier/Background)
- default-model silent no-op: factory injects host default chat model via a
new host_llm slot (create_chat_model(name=None)); DeerMem prefers host_llm
over build_llm(model). Zero-config extraction works out of the box again
- MemoryConfigResponse: fix stale docstring (backend-agnostic shape; DeerMem
knobs live under backend_config, not top-level - restoring flat would
re-couple the API to DeerMem). Frontend audited: does not read /memory/config
- _host_default_tracing_callback: restore langfuse assistant_id/environment
- search: push category onto the ABC signature; DeerMem filters BEFORE the
top_k slice (was filtered client-side after slicing -> starved results)
- _do_update_memory_sync: split into wrapper+impl; bind trace_id into the
request-trace ContextVar on the Timer/executor worker via a new
trace_context_manager host hook (None trace_id left unbound - no fabrication)
- client.py fact-CRUD now passes user_id (was writing to the global bucket
while get_memory reads per-user)
- _resolve_manager_class: fail-fast (raise ValueError) on an unresolved
explicit manager_class instead of silently falling back to DeerMem (memory is
persistent state - a wrong store is a silent data-integrity footgun)
Upstream memory fixes dropped by the host->vendored rename conflict, re-ported
to backends/deermem/deermem/core/ (+ deer_mem.py):
- #4073 queue busy-timer-spin -> _reprocess_pending flag (core/queue.py)
- #4074 null source.confidence in staleness -> _coerce_source_confidence
(core/updater.py: _build_staleness_section + _apply_updates stale sort)
- #4075 factsToRemove is optional (drop from _REQUIRED_MEMORY_UPDATE_TOP_LEVEL_KEYS)
- #4076 null confidence in search ranking -> _coerce_source_confidence
(deer_mem.py DeerMem.search)
host_llm + trace_context_manager are host-injected via backend_config (factory
in manager.py), keeping backends/deermem/ at exactly one `from deerflow` line
(the ABC contract) - portability test preserved.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: resolve lint errors (F541 f-string without placeholders, E501 line too long)
* fix(memory): restore hide_from_ui clarification preservation, expose mode
Two memory-system fixes (F541/E501 lint was already fixed on this branch):
- filter_messages_for_memory: restore default preservation of well-formed
human_input_response clarification answers (v2 regression). The
self-containment refactor made the bare function skip ALL hide_from_ui when
no hook was passed, but upstream preserves well-formed clarification
responses by default (test_hide_from_ui_human_input_response_is_preserved).
Inline a host-agnostic _is_human_clarification_response mirror of
read_human_input_response as the default keep-decision; the host-injected
should_keep_hidden_message hook still overrides (production path unchanged).
Portable package stays zero `from deerflow`.
- /memory/config: expose `mode` (middleware|tool) in MemoryConfigResponse +
the config/status endpoints + client.get_memory_config. mode is a host-
shared, behavior-determining field missing from the response projection.
Sync tests (mock .mode; e2e assert mode present).
- Align manager_class field docstring with fail-fast behavior.
Tests: filter/self-contained/portability (35) + memory-config (4) pass;
ruff clean.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(memory): resolve ruff format failures in memory module + tests
`make lint` runs `ruff format --check` in addition to `ruff check`; 8 memory
files had pending format changes -- 7 pre-existing (deer_mem, updater, tools,
test_memory_queue/router/search/tools) + message_processing from the
hide_from_ui fix. Apply `ruff format`: whitespace/wrapping only, no logic
change. 109 memory tests pass; ruff check + format --check both clean.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(memory): address PR review - legacy field migration, fact_id contract, path/docs
Address willem-bd's review on PR head bc8bf0d4 (risk:high, persistent state):
- config: auto-migrate pre-abstraction top-level memory.* DeerMem fields
(storage_path, max_facts, debounce_seconds, model_name, token_counting,
staleness_*, consolidation_*) into backend_config on load + warn, so an
upgrade does NOT silently revert customized settings (was: silent
extra='ignore' drop). model_name -> backend_config.model.model. Unknown
top-level keys warned.
- factory: resolve a relative backend_config.storage_path against runtime_home()
(base_dir-relative, CWD-independent) to preserve pre-abstraction semantics;
paths.py stays portable (no runtime_home import).
- tools: memory_add uses the fact_id returned directly by create_fact instead of
re-deriving it via content-key matching (coupled the tool to the backend's
content normalization; could misreport a storage cap). create_fact now returns
(memory_data, fact_id); gateway/client/tool updated. Fix terse
{"error":"content"} -> {"error":"empty content"}.
- app.py: update stale token_counting=="char" warm-up comment to point at
manager.warm (DeerMem.warm re-checks char and returns early).
- router: comment explaining reload_memory silent fallback vs fact 501 asymmetry
(read-only degrade vs write fail-loud).
- CHANGELOG: document breaking changes (/memory/config + client.get_memory_config
shape flat->backend_config; custom storage_class path moved + __init__ must
accept config) and the legacy-field auto-migration.
- tests: add regression test pinning the per-user memory path
({storage_path}/users/{safe_user_id}/memory.json == host make_safe_user_id)
across the abstraction; update create_fact mocks for (memory_data, fact_id).
Tests: 273 passed (memory suite); ruff check + format clean.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(memory): address PR review - storage_path, max_facts, tracing, parsing
Six review findings (willem-bd), each verified against upstream:
- storage_path semantics (file -> root dir): migration drops file-style
(.json) legacy values with a warning; factory raises if storage_path
resolves to an existing file (avoid silent NotADirectoryError write
failure). CHANGELOG + config.example.yaml comment updated.
- create_memory_fact enforces max_facts again (via _trim_facts_to_max) and
returns (memory, None) when the cap evicts the new fact; memory_add tool
reports "not stored", client raises ValueError, POST /memory/facts -> 409.
- max_facts trim uses _coerce_source_confidence (was raw f.get("confidence",
0) -> TypeError on non-float imported/legacy confidence, swallowed as
silent update failure).
- memory-tracing assistant_id restored to "memory_agent" (was "lead-agent"
copy-paste; matches upstream + DeerMem run_name).
- _is_human_clarification_response cross-checked against
read_human_input_response (drift guard test).
- empty-string legacy values skipped silently in migration (narrow fix, not
broad "if not value" which would skip explicit bool False).
8 new regression tests. make lint + 406 memory tests pass.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(memory): address internal review - storage fail-fast, build_llm degrade, config warn, noop template
Addresses 4 findings from the PR #4122 internal supplemental review
(parallel to willem-bd's review, no overlap):
- create_storage fail-fast: a misspelled/unimportable storage_class now
raises ValueError instead of silently falling back to FileMemoryStorage.
Memory is persistent state, so a wrong store is a data-integrity footgun;
mirrors the existing manager_class resolution policy. (storage.py)
- noop template create_fact signature: the commented template used
keyword-only `content` and returned a bare dict, while DeerMem's actual
create_fact takes positional `content` and returns tuple[dict, str|None]
(the memory_add tool passes content positionally; gateway/client/tools all
tuple-unpack). A backend copied from the template would 500 on fact-CRUD.
Template fixed; delete_fact/update_fact templates left (callers compatible).
(noop_manager.py)
- build_llm graceful degrade: wrap init_chat_model in try/except, degrade to
None + WARNING on failure (mirroring _host_default_llm) so a misconfigured
explicit model does not crash app startup -- non-LLM memory ops still work
and an update raises at runtime with the error logged. (llm.py)
- from_backend_config unknown-key warning: log a WARNING for unknown
backend_config keys (mirrors the host layer's load_memory_config_from_dict)
so a typo like `storage_pat` does not silently fall back to the default and
write memory to an unintended location. (config.py)
Tests: rewrote 3 create_storage fallback tests to expect ValueError; added 4
tests (build_llm zero-config/degrade, from_backend_config warn/silent).
make lint green; full memory suite passes.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: lllyfff <2281215061@qq.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: lllyfff <122260771+lllyfff@users.noreply.github.com>
30 KiB
30 KiB
Changelog
All notable changes to DeerFlow are documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
[Unreleased]
⚠ Breaking changes
- memory: The memory system is now pluggable (
memory.manager_classselects a backend; defaultdeermemis self-contained). DeerMem-private settings moved from the top level ofmemory:intomemory.backend_config, and the/memory/configresponse (andclient.get_memory_config()) changed shape. - memory:
/memory/configandclient.get_memory_config()no longer return flat DeerMem fields (storage_path,max_facts,debounce_seconds,token_counting,guaranteed_*,staleness_*, ...). They return{enabled, mode, injection_enabled, manager_class, backend_config}wherebackend_configis an opaque dict the active backend self-interprets. Memory data responses (/memory,/memory/statusdata) are unchanged. External API/SDK clients reading the old flat fields must readbackend_configinstead. - memory: Custom
memory.storage_classmoved: the old default pathdeerflow.agents.memory.storage.FileMemoryStorageno longer exists (nowdeerflow.agents.memory.backends.deermem.deermem.core.storage.FileMemoryStorage). CustomMemoryStoragesubclasses must acceptconfigin__init__(was no-arg). A broken/oldstorage_classlogs an error and falls back toFileMemoryStorage(won't crash) -- update the path + signature to restore it. - memory:
storage_pathsemantics changed from a FILE path to a root DIRECTORY. Pre-abstraction, an absolutestorage_pathwas the shared memory file (opting out of per-user isolation) and a relative value was the global file under the data base_dir. Nowstorage_path(absolute or relative) is the root directory; per-user memory lives at{storage_path}/users/{uid}/memory.json. An upgrade keeping the old defaultstorage_path: memory.json(a relative file name) would orphan per-user memory or hitNotADirectoryErroron save, so the legacy migration drops file-stylestorage_pathvalues (ending in.json) with a warning and the factory raises ifstorage_pathresolves to an existing file. Setmemory.backend_config.storage_pathto a directory for a custom root.
Changed
- memory: Pre-abstraction top-level
memory.*DeerMem fields (storage_path,max_facts,debounce_seconds,model_name,token_counting,staleness_*,consolidation_*, ...) are auto-migrated intobackend_configon load with a warning, so an upgrade does NOT silently revert customized settings to defaults (model_name->backend_config.model.model). Move them undermemory.backend_configinconfig.yamlto silence the warning. - memory: Added
memory.mode(middleware|tool);toolmode registers memory tools (memory_search/add/update/delete) the model calls directly instead of passive per-turn summarization.manager_classresolution is now fail-fast (raisesValueErroron an unknown backend instead of silently falling back).
Fixed
- models: Honor
api_baseon everyBaseChatOpenAIsubclass (VllmChatModel,MindIEChatModel,PatchedChatMiMo,PatchedChatStepFun,PatchedChatMiniMax), not justChatOpenAI/PatchedChatOpenAI. Those five previously dropped the configured endpoint silently and then failed every request with an opaqueunexpected keyword argument 'api_base'; the unknown-config-key warning was disabled for them as well. Both now gate onissubclass(BaseChatOpenAI). (#4146)
2.0.0 — 2026-06-15
DeerFlow 2.0 is a ground-up rewrite around a "super agent" harness with
sub-agents, persistent memory, sandbox execution, and an extensible
skills/tools system. It shares no code with the 1.x line, which now lives on
the main-1.x branch.
This release closes milestone 2.0.0 with 180 merged pull requests since the first 2.0 milestone tag.
⚠ Breaking changes
- harness: Hydrate runs from
RunStoreand persist interrupted status. Run cancellation/multitask semantics now require a working RunStore on the worker that owns the run; cross-worker cancels return 409 instead of silently appearing successful. (#2932)
Added
Agents & runtime
- agent: Custom-agent self-updates with user isolation — agents can persist
edits to their own
SOUL.md/config.yamlfrom inside a normal chat. (#2713) - loop-detection: Make loop detection configurable with per-tool frequency overrides; keep configurable on/off switch. (#2586, #2711)
- loop-detection: Defer warning injection so detector pairs cleanly with tool-call lifecycle. (#2752)
- run: Propagate
model_namefrom the gateway request through the runtime and persistence stack into the SQLite-backed store. (#2775) - subagents: Stream subagent token usage to the header via terminal task events. (#2882)
- memory: Add
memory.token_countingconfig to opt out of tiktoken for network-restricted deployments. (#3465) - suggest: Make AI follow-up question suggestions optional. (#3591)
Models & integrations
- models: Add StepFun reasoning model adapter. (#3461)
- community: Add Brave Search web search tool. (#3528)
- channels: Enhance Discord with mention-only mode, thread routing, and typing indicators. (#2842)
- im: Add user-owned IM channel connections — users can bind their own Slack/Telegram/Discord/Feishu/DingTalk/WeChat/WeCom accounts on top of the operator-configured bots. (#3487)
- models: Add patched MiMo reasoning content support. (#3298)
- models: Add MiniMax provider for image/video/podcast skills plus a new music-generation skill. (#3437)
- community: Add SearXNG and Browserless web search/fetch tools. (#3451)
- community: Add Serper Google Images provider for
image_search. (#3575) - channels: Stream Telegram agent replies by editing the placeholder message in place. (#3534)
Observability
- trace: Set the LangGraph trace name to
lead_agent(or the custom agent'sagent_name) for cleaner Langfuse/LangSmith traces. (#3101) - frontend: Refine token usage display modes. (#2329)
- defaults: Enable token usage tracking by default. (#2841)
- defaults: Raise default summarization trigger threshold. (#3174)
- trace: Attribute subagent spans to the parent thread's Langfuse trace. (#3611)
Skills
- skill: Add
blocking-io-guardskill for blocking-IO triage and runtime anchors. (#3503) - skill: Add maintainer issue and PR workflow skill. (#3554)
- skill: Strengthen the maintainer orchestrator review workflow. (#3606)
Performance
- harness: Push thread metadata filters into SQL instead of post-filtering in Python. (#2865)
- runtime: Index runs by
thread_idto avoid O(n) scans inRunManager. (#3499) - runtime: Index messages in
MemoryRunEventStoreto avoid O(n) scans. (#3531) - persistence: Cache
Base.to_dictcolumn reflection per class. (#3654) - sandbox: Speed up
should_ignore_namein glob/grep walks. (#3657)
Security
- upload: Reject symlinked upload destinations. (#2623)
- uploads: Add Windows support for safe symlink-protected uploads. (#2794)
- mcp: Mask sensitive values in MCP config API responses. (#2667)
- mcp: Harden the MCP config endpoint against malformed input. (#3425)
- auth: Reject cross-site auth POSTs. (#2740)
- gateway: Cap skill artifact preview decompression to prevent zip-bomb-style abuse. (#2963)
- sandbox: Mount the host Docker socket only in aio (DooD) sandbox mode. (#3517)
- sandbox: Do not bind-mount host CLI auth dirs by default. (#3521)
Fixed
Runtime, gateway & persistence
- runtime: Rollback restore checkpoint now supersedes newer checkpoints. (#2582)
- runtime: Persist run message summaries. (#2850)
- runtime: Bound
write_fileexecution-failure observations to keep failure traces from blowing out the context. (#3133) - runtime: Protect the sync singleton's init and reset paths. (#3413)
- runtime: Avoid PostgreSQL aggregate
FOR UPDATEon run events. (#2962) - runs: Restore historical runs from persistent store after a gateway restart. (#2989)
- gateway: Return ISO 8601 timestamps from threads endpoints. (#2599)
- gateway: Make cancel idempotent for already-interrupted runs. (#3058)
- gateway: Split
stream_existing_runinto per-method routes for unique OpenAPIoperationIds. (#3228) - events: Serialize structured DB event content. (#2762)
- persistence: Emit timezone-aware timestamps from SQLite-backed stores. (#3130)
- persistence: Reuse token usage model grouping expression. (#2910)
- runs: Ignore stale run reconnect conflicts. (#3284)
- nginx: Defer CORS to the gateway allowlist instead of double-applying it. (#2861)
- persistence: Fix runtime journal run lifecycle events. (#3470)
- gateway: Enforce thread ownership on stateless run endpoints. (#3473)
- runtime: Propagate interrupt through SSE values events for the LangGraph SDK. (#3605)
- serialization: Strip base64 image data from streamed values events. (#3631)
- history: Strip base64 image data from REST endpoint responses. (#3535)
- gateway: Attribute token usage to the actual models. (#3658)
Agents, subagents & middleware
- subagents: Make subagent timeout terminal state atomic. (#2583)
- subagents: Use model override for tools and middleware. (#2641)
- subagents: Consolidate
system_promptand skills into a singleSystemMessage. (#2701) - subagent: Isolate subagents from the parent run's checkpointer. (#3559)
- agents: Make
update_agenthonorruntime.contextuser_idlikesetup_agentdoes. (#2867) - agents: Resolve duplicate
todoschannel type conflict inTodoMiddleware. (#3200) - agents: Offload blocking filesystem IO in the custom-agent router off the event loop. (#3457)
- agents: Keep new agent bootstrap in user scope. (#2784)
- loop-detection: Keep tool-call pairing on warn injection. (#2725)
- middleware: Sync raw tool-call metadata. (#2757)
- middleware: Handle invalid tool calls in dangling pairing middleware. (#2891)
- middleware: Prevent todo completion reminder IM-message leak. (#2907)
- middleware: Normalize tool result adjacency before model calls. (#2939)
- agents: Require
config.yamlinresolve_agent_dirto skip memory-only directories. (#3481) - agents: Sync
agent_nameacross context/configurable and reject empty soul. (#3553) - middleware: Offload the uploads scan in
UploadsMiddlewareoff the event loop. (#3311) - middleware: Offload memory injection off the event loop to prevent tiktoken blocking. (#3411)
- middleware: Externalize oversized tool output into the sandbox for non-mounted sandboxes. (#3417)
- middleware: Preserve the sandbox reducer in middleware state. (#3629)
- subagents: Raise general-purpose
max_turnsto 150 and default timeout to 30 min. (#3610)
Memory & tracing
- memory: Replace short-lived
asyncio.run()with a persistent event loop. (#2627) - memory: Isolate queued memory updates by agent. (#2941)
- memory: Parse wrapped memory-update JSON responses. (#3252)
- tracing: Propagate
session_idanduser_idinto Langfuse traces. (#2944) - trace: Decode unicode escape sequences in non-ASCII memory trace info. (#3104)
Tools, sandbox & MCP
- mcp: Fix env resolution in MCP config lists. (#2556)
- models: Record Codex token usage in
usage_metadata. (#2585) - sandbox: Supplement
list_runninginRemoteSandboxBackend. (#2716) - sandbox: Disable MSYS path conversion for Git Bash on Windows. (#2766)
- sandbox: Avoid blocking sandbox readiness polling. (#2822)
- sandbox: Uphold the
/mnt/user-datacontract at theSandboxAPI boundary. (#2881) - sandbox: Scope provisioner PVC data by user. (#2973)
- sandbox: Merge idempotent sandbox state updates. (#3518)
- tools: Introduce
Runtimetype alias to eliminate Pydantic serialization warnings. (#2774) - tools: Preserve
tool_searchpromotions across re-entrantget_available_tools. (#2885) - harness: Wrap async-only config tools for sync client execution. (#2878)
- harness: Wrap all async-only tools for sync clients. (#2935)
- tool-search: Reliably hide deferred MCP schemas by removing the ContextVar. (#3342)
- search: Fix DDGS Wikipedia region handling. (#3423)
- web_fetch: Support a proxy for the Jina reader in restricted networks. (#3430)
- sandbox: Persist lazily-acquired sandbox state via
Command. (#3464) - sandbox: Fix stale AIO sandbox cache reuse. (#3494)
- sandbox: Create a shell session before retrying on a fresh id. (#3577)
- sandbox: Stop flagging string-literal path fragments as unsafe absolute paths. (#3623)
- sandbox: Return an actionable hint when
read_filehits a binary file. (#3624) - mcp: Make stdio MCP-produced files resolvable via virtual sandbox paths. (#3600)
- mcp: Surface admin-required state on the settings tools page. (#3533)
- mcp: Add a tools cache reset endpoint. (#3602)
- uploads: Fix the upload file size contract. (#3408)
Skills & channels
- skills: Enforce
allowed-toolsmetadata. (#2626) - skills: Harden slash skill activation across chat channels. (#3466)
- skills: Fix custom skill install permissions. (#3241)
- channels: Authenticate gateway command requests. (#2742)
- skills: Surface the offending line and a quoting hint on SKILL.md YAML errors. (#3335)
- skills: Keep skill archive installation off the event loop. (#3505)
- channels: Ignore hidden control messages when extracting replies. (#3270)
- channels: Reload config on channel restart. (#3514)
- channels: Surface WeCom WebSocket connection failures. (#3526)
- channels: Close the Discord file handle after upload. (#3561)
- channels: Require a bound identity for user-owned IM messages. (#3578)
- channels: Scope IM files and helper commands to the owner. (#3579)
- channels: Make runtime provider state authoritative. (#3580)
- channels: Harden runtime credential management APIs. (#3581)
- channels: Make the channel connect flow deterministic. (#3582)
- channels: Centralize shared channel retry helpers. (#3583)
- channels: Add operational guardrails. (#3584)
- channels: Unsubscribe channel listeners by equality. (#3608)
Auth
- auth: Replace setup-status 429 rate limit with a cached response. (#2915)
- auth: Persist auto-generated JWT secret so it survives restarts. (#2933)
- auth: Align auth-disabled mode with mock history loading. (#3471)
Frontend
- frontend: Restore
localhostfallback forgetGatewayConfigin prod mode. (#2718) - chat: Prevent the first user message from being swallowed in new conversations. (#2731)
- frontend: Use backend thread token usage for the header total. (#2800)
- frontend: Wait for async chat submit before clearing the input. (#2940)
- frontend: Resolve login page flickering and the resize-observer loop. (#2954)
- frontend: Deduplicate restored thread messages. (#2958)
- frontend: Avoid duplicate optimistic user message. (#3002)
- frontend: Hide the copy button for streaming assistant messages. (#3176)
- frontend: Show a new thread in the sidebar immediately on creation. (#3283)
- frontend: Isolate new chat thread messages. (#3508)
- frontend: Cap deeply nested list indentation to prevent render crashes. (#3393, #3570)
- token-usage: Dedupe token usage aggregation by message id. (#2770)
- frontend: Fall back to Streamdown clipboard copy. (#3397)
- frontend: Remove the Backspace shortcut for deleting prompt attachments. (#3410)
- frontend: Restructure the Memory settings toolbar into two rows. (#3433)
- suggestions: Strip inline
<think>reasoning before parsing follow-up questions. (#3435) - frontend: Stop fetching follow-up suggestions when they are disabled. (#3599)
- frontend: Paginate the workspace chat list beyond 50 threads. (#3485)
- frontend: Prevent user message bubble overflow with long unbreakable strings. (#3488)
- frontend: Keep the workspace interactive when the SSR auth probe cannot reach the gateway. (#3495)
- frontend: Render user messages as plain text and cap blockquote nesting. (#3502)
- frontend: Reset the active chat after deletion. (#3519)
- frontend: Improve the mobile workspace layout. (#3646)
- frontend: Render full content for multi-part AI messages. (#3649)
Build, deploy, scripts & config
- packaging: Add
postgresextra for store/checkpointer support; clarify install guidance. (#2584) - harness: Resolve runtime paths from the project root. (#2642)
- docker: Force nginx to resolve upstream names at request time. (#2717)
- docker: Default Gateway to a single worker to prevent multi-worker breakage. (#3475)
- scripts: Preserve
uvextras acrossmake devrestarts. (#2767, #2754) - scripts: Clean up local nginx on stop. (#3005)
- deploy: Fall back to
python/opensslwhenpython3is absent for secret generation. (#3074) - config: Make the reload boundary discoverable from code. (#3144, #3153)
- replay-e2e: Key replay fixtures by caller and conversation. (#3453)
- setup: Refresh LLM provider wizard defaults. (#3421)
- config: Coerce null
config.yamllist sections to an empty list. (#3434) - scripts: Exclude runtime state from gateway reload. (#3426)
- scripts: Create the backend/sandbox dir before the uvicorn reload-exclude. (#3460)
- scripts: Stop next-server correctly after
make start-daemon. (#3498) - makefile: Fix per-commit hooks installation. (#3569)
- replay-e2e: Match replay by conversation, not the living system prompt. (#3436)
Changed
- provider (refactor): Share assistant payload replay matching across providers. (#3307)
- lead-agent (refactor): Make
build_middlewarespublic to drop the last cross-module private import. (#3458) - todo (refactor): Remove the unused completion reminder counter. (#3530)
Documentation
- Document blocking-IO detection usage and maintenance. (#3233)
- Clean standalone LangGraph server remnants from docs. (#3301)
- Add AI assistance disclosure to the PR template and CONTRIBUTING. (#3398)
- Document custom AIO sandbox images. (#3548)
Internal
- dev: Add async/thread boundary detector. (#2936)
- runtime: Add lifecycle end-to-end coverage. (#2946)
- windows: Add
PYTHONIOENCODINGandPYTHONUTF8to backend Makefile targets. (#3069) - blocking-io: Fail-loud repo-root resolution and shared detector CLI shim. (#3512)
- runtime: Add a Blockbuster runtime anchor for
JsonlRunEventStoreasync IO. (#3313) - ci: Consolidate PR/issue labeling and fix the reviewing-job crash and label thrash. (#3455)