Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2014563
feat(frontend): add structured human input cards for ask_clarification
AnoobFeng Jul 5, 2026
8330fbc
perf(frontend): optimize HumanInputCard UI interactions
AnoobFeng Jul 5, 2026
d320741
test(frontend): add unit test cover optimize HumanInputCard UI intera…
AnoobFeng Jul 5, 2026
aac9a03
feat(frontend): disabled chatbox when has new human-input-card
AnoobFeng Jul 5, 2026
7f8179b
fix(style): lint error fix
AnoobFeng Jul 5, 2026
7a985f4
Fix(frontend): Fix mobile workspace and accessibility blockers (#3740)
18062706139fcz Jul 5, 2026
67c8ade
feat(sandbox): warm pool for boxlite (#3951)
Vanzeren Jul 5, 2026
186c6ea
feat: add branching support for assistant turns (#3950)
18062706139fcz Jul 5, 2026
0664ea2
fix(subagents): surface turn-budget cap as MAX_TURNS_REACHED with par…
hata33 Jul 5, 2026
eb5eb9c
fix(docker): keep config mounts stable across host saves (#3954)
Huixin615 Jul 6, 2026
f2960a1
Merge remote-tracking branch 'upstream/main' into feat/20260706-visua…
AnoobFeng Jul 6, 2026
155a80e
fix: sanitize hidden human input replies
AnoobFeng Jul 6, 2026
cb83edb
fix(sidecar): correct text-selection toolbar actions inside the side …
18062706139fcz Jul 6, 2026
28f2b07
feat(memory): add staleness review to prune silently-outdated facts (…
sontianye Jul 6, 2026
8cde7f2
feat(gateway): refuse multi-worker startup on non-Postgres backends (…
heart-scalpel Jul 6, 2026
06d38fd
fix: tighten human input response validation
AnoobFeng Jul 6, 2026
f122594
refactor(frontend): extract placeholder detection utility with unit t…
MeloMei Jul 6, 2026
fd41fdb
feat(middleware): add structured tool result meta and tool-progress s…
whhe Jul 6, 2026
2ebe5cf
fix(composer): stop bottom mask strip from clipping the focus ring (#…
18062706139fcz Jul 6, 2026
64b89af
Merge branch 'main' into feat/20260706-visualized-card
WillemJiang Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,8 @@ Gateway-generated follow-up suggestions now normalize both plain-string model ou

Interrupted first-turn runs still persist a fallback conversation title, so stopping a streaming response does not leave the thread as "Untitled" after refresh.

In the Web UI, completed assistant turns can be branched into a new main conversation. The new thread starts from that turn's checkpoint. Because workspace files are not checkpointed, the branch only receives a best-effort copy of the current workspace when you branch from the latest turn; branching from an older turn keeps just the restored message history so the branch never inherits files that were created in a later part of the conversation.

```
# Paths inside the sandbox container
/mnt/skills/public
Expand Down
2 changes: 2 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ Tools 也是同样的思路。DeerFlow 自带一组核心工具:网页搜索

Gateway 生成后续建议时,现在会先把普通字符串输出和 block/list 风格的富文本内容统一归一化,再去解析 JSON 数组响应,因此不同 provider 的内容包装方式不会再悄悄把建议吞掉。

Web UI 支持从已完成的 assistant 回复分叉出一个新的主对话。新 thread 会从该回复对应的 checkpoint 开始,并尽力复制当前 thread 的工作区文件。

```text
# sandbox 容器内的路径
/mnt/skills/public
Expand Down
65 changes: 42 additions & 23 deletions backend/AGENTS.md

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions backend/app/gateway/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import asyncio
import logging
import os
from collections.abc import AsyncGenerator, Callable
from contextlib import AsyncExitStack, asynccontextmanager
from typing import TYPE_CHECKING, TypeVar, cast
Expand All @@ -45,6 +46,27 @@
_RUN_DRAIN_TIMEOUT_SECONDS = 5.0


def _enforce_postgres_for_multi_worker(config: AppConfig) -> None:
"""Refuse to start when GATEWAY_WORKERS > 1 and the DB backend is not Postgres.

SQLite write-locks cannot support concurrent multi-process access.
This gate runs once at startup before any persistence engine is
initialised so the error message is clear and the process exits
immediately.
"""
try:
workers = int(os.environ.get("GATEWAY_WORKERS", "1"))
except (TypeError, ValueError):
workers = 1

if workers <= 1:
return

backend = getattr(config.database, "backend", None)
if backend != "postgres":
raise SystemExit(f"GATEWAY_WORKERS={workers} requires database.backend='postgres', but database.backend is '{backend}'. SQLite cannot support concurrent multi-process access. Set GATEWAY_WORKERS=1 or switch to Postgres.")


async def _drain_inflight_runs(run_manager: RunManager) -> None:
"""Drain in-flight runs before the checkpointer is torn down (issue #3373).

Expand Down Expand Up @@ -209,6 +231,12 @@ async def langgraph_runtime(app: FastAPI, startup_config: AppConfig) -> AsyncGen
from deerflow.runtime.checkpointer.async_provider import make_checkpointer
from deerflow.runtime.events.store import make_run_event_store

# ------------------------------------------------------------------
# Multi-worker safety gate: reject SQLite when GATEWAY_WORKERS > 1.
# SQLite write-locks cannot support concurrent multi-process access.
# ------------------------------------------------------------------
_enforce_postgres_for_multi_worker(startup_config)

async with AsyncExitStack() as stack:
config = startup_config

Expand Down
15 changes: 15 additions & 0 deletions backend/app/gateway/routers/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ class MemoryConfigResponse(BaseModel):
...,
description="Token ceiling for guaranteed-category facts (displaces regular lines in the common case; additive only when guaranteed alone overflows max_injection_tokens)",
)
staleness_review_enabled: bool = Field(..., description="Whether staleness review is enabled for aged facts")
staleness_age_days: int = Field(..., description="Facts older than this many days are candidates for staleness review")
staleness_min_candidates: int = Field(..., description="Minimum stale facts required to trigger a review cycle")
staleness_max_removals_per_cycle: int = Field(..., description="Maximum number of facts staleness review can remove per cycle")
staleness_protected_categories: list[str] = Field(..., description="Fact categories exempt from staleness review")


class MemoryStatusResponse(BaseModel):
Expand Down Expand Up @@ -360,6 +365,11 @@ async def get_memory_config_endpoint() -> MemoryConfigResponse:
token_counting=config.token_counting,
guaranteed_categories=config.guaranteed_categories,
guaranteed_token_budget=config.guaranteed_token_budget,
staleness_review_enabled=config.staleness_review_enabled,
staleness_age_days=config.staleness_age_days,
staleness_min_candidates=config.staleness_min_candidates,
staleness_max_removals_per_cycle=config.staleness_max_removals_per_cycle,
staleness_protected_categories=config.staleness_protected_categories,
)


Expand Down Expand Up @@ -391,6 +401,11 @@ async def get_memory_status(http_request: Request) -> MemoryStatusResponse:
token_counting=config.token_counting,
guaranteed_categories=config.guaranteed_categories,
guaranteed_token_budget=config.guaranteed_token_budget,
staleness_review_enabled=config.staleness_review_enabled,
staleness_age_days=config.staleness_age_days,
staleness_min_candidates=config.staleness_min_candidates,
staleness_max_removals_per_cycle=config.staleness_max_removals_per_cycle,
staleness_protected_categories=config.staleness_protected_categories,
),
data=MemoryResponse(**memory_data),
)
Loading
Loading