942 Commits

Author SHA1 Message Date
JasonH
5dc508b407
fix(auth): recover session checks after network failures (#7529)
* fix(auth): recover session checks after network failures

* fix(auth): retain reconnect wakeups during pending refreshes

* docs(auth): scope recovery guidance within budget

* fix(auth): preserve reconnects until tabs become visible

* test: include auth guidance in approved inventory

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-26 19:29:33 +08:00
Hyeonsang Cho
9c00fd839a
fix(frontend): return SSO sign-ins to their next path (#7467)
* fix(frontend): return SSO sign-ins to their next path

The (auth) layout redirected every signed-in request to /workspace. The
Gateway sets the session cookie on the same redirect that lands on
/auth/callback?next=..., so the callback page never rendered and every SSO
sign-in landed on the workspace instead of the page that asked for it. A
signed-in visit to /login?next=... lost its target the same way.

Layouts cannot read searchParams, so the layout now renders the page for a
signed-in session and each page sends the user on: the callback and login to
the validated next path, setup to the workspace. The login page shows a
short wait message instead of the form while it redirects, and uses
router.replace so Back does not return to it.

* docs(changelog): reference #7467 for the SSO next-path fix

* fix(auth): reject control characters in the next redirect path

Browsers delete tab, LF and CR from a URL before parsing it, so a next of
"/\n//evil.example" passed both validators' "//" check and then navigated
to //evil.example. The login page's post-sign-in push already followed it,
and the signed-in login and SSO callback redirects reach it too.

validateAuthNextPath and validate_next_param now reject every C0 control
character and DEL. Percent-encoded forms are left alone: URL parsers do
not decode them, so they stay on the local origin.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-26 19:24:35 +08:00
Xinmin Zeng
d48ea2a9a3
refactor: remove unused proxy policy and private constants (#7554)
* refactor: remove unused proxy policy and private constants

* refactor(sandbox): share provisioner runtime mount path
2026-10-26 17:02:39 +08:00
jianyx1
4b1e06bbf9
feat(runtime): honor the web UI language in replies and DeerMem updates (#7294)
* feat(runtime): honor the web UI language in replies and DeerMem updates

Carry Settings > Appearance > Language as request-only run data: the
Gateway validates context.locale against a finite allowlist, the lead
agent prefers it for replies, and bundled DeerMem writes new facts and
summary prose in it. Explicit user language requests still take
precedence; checkpoint configurable, static prompts, and memory backend
APIs are unchanged.

Refs #7287

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(runtime): guard language context and pin locale maps

* test(runtime): pin UI locales across frontend and Gateway

Add the shared locale fixture and document the intentional language fallback for internal runs. Clarify live-locale precedence in the run context contract.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-26 13:24:30 +08:00
Xinmin Zeng
5010a74a03
chore: remove unused frontend components and RAGFlow helper (#7543) 2026-10-26 13:17:06 +08:00
YongxinFu
8337a80ae6
fix(frontend): count code points when previewing table cells and memory facts (#7503)
* fix(frontend): count code points when previewing table cells and memory facts

artifact-table-preview cut a cell at 120 UTF-16 code units and
memory-settings-page's truncateFactPreview measured and cut fact previews
at 140, so an emoji spanning two units could be split at the cut — the
lone half renders as U+FFFD — and a preview that already fit the
documented code-point budget was still shortened. Both previews now share
truncateToCodePoints in core/utils/text, which measures and cuts by code
points, the same rule briefReason in core/scheduled-tasks/format.ts
already follows.

Add unit tests for the helper and a DOM regression test covering both the
fits-exactly and the cut-inside-an-emoji cases.

* fix(frontend): count code points in the artifact-cell expand gate too

The expandable gate still measured value.length (UTF-16 units), so a
120-code-point cell ending in an astral character opened the expand
control even though the inline preview already showed the whole cell —
the same units-vs-code-points mismatch the preview cut fixed.

The gate now counts Array.from(value), and the boundary test asserts
the fits cell renders as a plain cell with no expand control while the
overflows cell keeps its button and whole-emoji cut.

Suggested-by: gdut4140 (port of the #7509 expandable gate)
2026-10-26 08:23:49 +08:00
Luck
ce0fa4c114
feat(batch): add browser review for persisted results (#7006)
* feat(batch): add browser review for persisted results

* fix(batch): address browser review feedback

* test(batch): cover persisted result review regressions

Exercise running-to-terminal detail refresh, scoped citation dialogs, omitted evidence, translated page labels, and legacy evidence bounds. Fix component lint and formatting and synchronize the review contract documentation.

* docs(frontend): keep batch review guidance within budget

* fix(batch): refresh retried reviews and account for omitted evidence

* fix(ci): keep batch review guidance within size budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 23:27:35 +08:00
ShaoLOWu
21fb080530
fix(frontend): allow non-ASCII local parts in auth password forms (#7488) (#7498)
* fix(frontend): allow non-ASCII local parts in auth password forms (#7488)

The sign-in, register, admin-setup and change-password forms relied on the
browser's type="email" constraint validation, which rejects any non-ASCII
local part before the submit handler runs. The backend identity schema
deliberately accepts such addresses (see emailSchema in @/core/auth/types),
so an account created via the API, an administrator or OIDC could never sign
in through the password form.

Forms are now noValidate, and the permissive isValidEmail helper (one @,
non-empty local part and domain, no whitespace) runs in the handlers instead,
staying strictly weaker than what the backend accepts.

* fix(auth): normalize emails and restore form password checks

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 22:09:58 +08:00
dependabot[bot]
4285e2b60c
chore(deps): bump zod from 4.3.6 to 4.6.7 in /frontend (#7512)
Bumps [zod](https://github.com/colinhacks/zod) from 4.3.6 to 4.6.7.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v4.3.6...v4.6.7)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-25 22:02:40 +08:00
Agustin M.
66f00e7b86
fix(frontend): make docs search work on desktop and mobile (#6715)
* fix(frontend): make docs search work on desktop and mobile

Docs search on deerflow.tech was broken twice over: no build step ever
generated the Pagefind index that Nextra's <Search> loads, and the custom
docs header that replaces Nextra's navbar never rendered a search box.

- `pnpm build` now runs `search:index` (pagefind 1.5.2, pinned) to write
  public/_pagefind/ (gitignored).
- The landing Header and its mobile sheet take an optional `search` slot;
  only the docs layout fills it, with localized labels and an aria-label.
- Each docs page carries a Pagefind `lang` filter and queries are scoped to
  it, since the root <html lang> is fixed and would mix en/zh results.
- globals.css restyles the results panel with app tokens because Nextra's
  <Head> variables are unset, lifts it above the mobile sheet, and keeps it
  tappable; the sheet no longer closes before a result navigates and closes
  after navigation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(frontend): add DESIGN.md describing the shipped design system

Records the existing tokens, typography, components, docs (Nextra)
overrides, motion, accessibility and copy rules, plus the landing-only
decorative exceptions, so UI changes have something to be checked
against. Linked from frontend/AGENTS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(frontend): address Codex review of docs search

- Chinese search: the root <html lang> is fixed, so Pagefind indexed zh
  pages with the English tokenizer and multi-character words (e.g. 偏好)
  never matched. scripts/build-search-index.mjs now sets each page's lang
  from its route before indexing, producing separate en-US and zh-CN
  indexes, and DocsHtmlLang syncs <html lang> at runtime (resetting
  Pagefind on a language switch) so queries hit the right index.
- Mobile menu: reset the open state when the route changes instead of
  comparing against the saved path, so Back no longer reopens the sheet.
- Shortcut: stop mounting a second <Search> in Nextra's unused mobile menu,
  which captured Cmd/Ctrl+K and focused an offscreen input at 640-767px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(frontend): keep guidance within size budget

* fix(frontend): address docs search review feedback

Keep Nextra results within the mobile sheet scroll boundary and dismiss same-hash selections by pointer or keyboard. Rebuild Pagefind output without stale fragments and replace UI source-string checks with behavioral coverage.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 21:31:11 +08:00
王一帆
bd62f96732
feat(knowledge): add optional platform connections and hierarchical spaces (phase 3) (#7417)
* feat(knowledge): add optional platform connections and hierarchical spaces

* fix(knowledge): return actionable errors for unavailable policy storage

* perf(knowledge): resolve space policy once per snapshot
2026-10-25 19:52:31 +08:00
Totoro
a1b89d0432
fix(frontend): reflect tool outcomes in chat steps (#7465)
Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-25 16:25:29 +08:00
一片一片
6b2ac58555
fix(scheduled-tasks): count code points when shortening a run summary (#7452)
briefReason() measured and sliced UTF-16 code units, so a cut inside an
emoji's surrogate pair left a lone high surrogate that the outcome notice
renders as U+FFFD. Measuring code points also stops shortening a summary
that fits the documented budget.

Add a regression test for both the fits-exactly and truncating cases.

Co-authored-by: 一片一片 <192845873+gdut4140@users.noreply.github.com>
2026-10-25 15:49:10 +08:00
lihongyuan99
07a4770e32
docs(harness): scope the memory guides per user and nest DeerMem knobs (#7463)
* test(memory): pin the memory guides to the layout DeerMem resolves

The en and zh harness guides still document the pre-abstraction storage
shape, and nothing in CI reads prose, so the drift stayed invisible.
Derive the per-user manifest and per-agent facts paths from
memory_file_path()/agent_facts_directory() and take the allowed memory:
top-level keys from the public MemoryConfig model.

* docs(harness): scope memory docs per user and nest DeerMem knobs

Both guides claimed a shared {base_dir}/memory.json plus one memory.json
per agent; DeerMem keeps one manifest per user and puts agent state in a
facts directory beside it. The zh Configuration example also still showed
DeerMem-private keys at the memory top level, which config.example.yaml
and the en twin already place under backend_config.

* docs(memory): align per-agent facts overviews in both languages

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 15:14:29 +08:00
一片一片
6213fee192
fix(frontend): bound notification bodies by code points (#7474)
Both chat pages capped the "conversation finished" notification body with
String#length / substring, which count UTF-16 code units: a cut inside an
astral character's surrogate pair rendered U+FFFD, and a body that fits 200
code points was shortened early.

Extract notificationBody() so both pages share the code-point bound and the
"Conversation finished" fallback, and add boundary regressions.

Co-authored-by: 一片一片 <192845873+gdut4140@users.noreply.github.com>
2026-10-25 15:13:16 +08:00
jianyx1
db7f919118
fix(frontend): keep artifact lists scrollable (#7284)
Use the shared ScrollArea so long artifact lists remain reachable in desktop panels and mobile drawers. Add browser coverage for short and long lists before clicking the final file.

Adapt the ScrollArea approach from LeonSGP43 in bytedance/deer-flow#2076 and verify it against current main for issue #2074.
2026-10-25 14:55:58 +08:00
Hyeonsang Cho
1792e4e432
fix(frontend): stop re-validating account emails the backend accepted (#7449)
* fix(frontend): stop re-validating account emails the backend accepted

userSchema checked email with zod's email(), which rejects many addresses
pydantic EmailStr registers (a&b@example.com, a@b.c, Unicode). /auth/me for
such an account parsed as malformed: SSR answered gateway_unavailable, the
offline banner never recovered, and the admin user list failed for every
administrator. The backend owns address validation; keep a non-empty string.

* docs(changelog): reference #7449 for the account email schema fix
2026-10-25 10:09:49 +08:00
dependabot[bot]
04691473d4
chore(deps): bump zod from 3.25.76 to 4.6.6 in /frontend (#7392)
* chore(deps): bump zod from 3.25.76 to 4.6.6 in /frontend

Bumps [zod](https://github.com/colinhacks/zod) from 3.25.76 to 4.6.6.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.6)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(frontend): complete Zod 4 migration

Use explicit string keys for extension setting records so the discovery
schema remains assignable to the extension loader contract under Zod 4.
Cover accepted primitives, rejected non-primitive settings and inferred
public types through the discovery API.

Keep Nextra 4.6.1 and its docs theme on their previous Zod 4.3.6 dependency
until upstream fixes required children validation. The app remains on Zod
4.6.6. Document and lock the targeted compatibility overrides.

* docs: keep frontend guidance within hard budget

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 22:36:41 +08:00
dependabot[bot]
916e647a75
chore(deps-dev): bump happy-dom from 20.11.1 to 21.0.4 in /frontend (#7391)
* chore(deps-dev): bump happy-dom from 20.11.1 to 21.0.4 in /frontend

Bumps [happy-dom](https://github.com/capricorn86/happy-dom) from 20.11.1 to 21.0.4.
- [Release notes](https://github.com/capricorn86/happy-dom/releases)
- [Commits](https://github.com/capricorn86/happy-dom/compare/v20.11.1...v21.0.4)

---
updated-dependencies:
- dependency-name: happy-dom
  dependency-version: 21.0.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* test(frontend): isolate FlipDisplay animations in DOM suites

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 10:45:01 +08:00
hataa
35d0dcd6f2
feat(plugins): add sectioned long-form report writing example (#7371)
* feat(plugins): add sectioned long-form report writing example

A standalone example extension whose save_section model tool stores a
万字-scale report section by section (SQLite, per-user and per-thread)
and assembles every section into one markdown document on a final
section_index=999 call. The assembled document is returned as a tool
artifact with only a short status line re-entering the model context, so
the full report never consumes the context budget.

Assembly drops a section's leading heading when it restates the section
title (models habitually open each section with '# <title>' while the
assembler already emits '## <title>'), which otherwise doubles every
heading in the document. The outline section passes through unwrapped
and keeps its own heading as the document title.

* fix(frontend): register report sections in extension catalog

* docs(report-sections): correct the test command path

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 08:50:56 +08:00
Xuehao Xu
f6a5a64c9b
feat(knowledge): add optional ingestion and processing extension (#7293)
* feat(knowledge): add optional ingestion and processing extension

* fix(knowledge): address catalog and ingestion review regressions

* fix(knowledge): add sanitized provider diagnostics and Chinese docs

* fix(knowledge): align capabilities and repair catalog e2e assertions
2026-10-24 08:19:02 +08:00
dependabot[bot]
2efe854980
chore(deps): bump next from 16.3.8 to 16.4.1 in /frontend (#7384)
Bumps [next](https://github.com/vercel/next.js) from 16.3.8 to 16.4.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.3.8...v16.4.1)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.4.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-24 08:15:40 +08:00
Tsai Yuan
0530d0c7c6
fix(frontend): resolve relative message links to artifacts (#7356)
* fix(frontend): resolve relative links in messages to thread artifacts

An assistant message that links a file it wrote with a relative
destination, e.g. [Report](report.md), rendered a plain relative href.
The browser resolved it against the chat route to
/workspace/chats/report.md, so clicking it opened a "thread" whose id is
the file name and the Gateway rejected it (422). Only /mnt/ links were
rewritten, and the clarification and present_files message text did not
get the thread-aware link renderer at all.

- A relative link (no scheme, no leading /, no .. segment) that
  suffix-matches exactly one thread artifact now opens that artifact in
  a new tab, using the rule message images already use. When zero or
  several artifacts match it renders as text instead of a link to a
  broken chat URL. Links outside the message list are unchanged.
- The artifact list reaches the link renderer through a context:
  Streamdown memoizes its root without comparing `components`, so a
  renderer built before the list arrives would keep the stale list.
- /mnt/ artifact links get the normal link styling (they rendered as
  unstyled text before).
- A reopened thread rebuilds its values from the POST /history head,
  which carries messages but not `artifacts`. The message list and the
  artifact panel now add the /mnt/ paths from present_files calls in the
  messages, so links resolve and the panel lists the files after reload.

Co-Authored-By: Claude Code <noreply@anthropic.com>

* fix(frontend): address artifact link review

---------

Co-authored-by: huangzz <110116@nd.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-10-23 22:50:52 +08:00
Tsai Yuan
03c95f5278
fix(subagents): report model output length caps (#7355)
* fix(subagents): report model_length_capped when a subagent hits the output limit

ModelLengthFinishReasonMiddleware only ran on the lead chain. A subagent
whose response was cut at the provider output limit (finish_reason=length,
stop_reason=max_tokens, MAX_TOKENS, Responses max_output_tokens) reached
the lead as a clean `completed`, and a tool call truncated at that
boundary still executed with partial arguments.

Register the same middleware on the subagent chain, in the lead's
relative position (after configured extensions, before Safety). It now
also records each detected cap per context run_id and exposes
`consume_stop_reason`, so the executor's existing duck-typed guard
collection reports `stop_reason=model_length_capped`. Add the value to
the subagent status contract (Python enum, contract fixture, frontend
whitelist) with the label "model output length limit", and update the
subagent docs.

Tests cover the middleware channel, chain placement, the contract, and
an end-to-end executor run with a fake model.

Co-Authored-By: Claude Code <noreply@anthropic.com>

* test(tasks): cover cap reasons on completed and failed runs

Extend the existing shared-contract round-trip matrix to preserve both terminal statuses alongside every supported stop reason, including model_length_capped and loop_capped.

---------

Co-authored-by: huangzz <110116@nd.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 21:42:11 +08:00
Huang-404-Q
4e20cbb4d6
fix(uploads): treat a missing message as an empty one (#7342)
The unsuccessful-upload branch called result.message.trim() directly. A
gateway or proxy that omits message turns a handled upload failure into a
TypeError while handling it, losing the clean "Upload failed" path.

Coerce a non-string message to an empty string before trimming.

The regression test sends success:false without a message and expects
"Upload failed"; it throws a TypeError on main.

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 20:03:17 +08:00
Lachlan Allen
7a32a5e177
feat(exa): support a configurable Exa-compatible base URL (#7306)
Exa web_search and web_fetch accept a per-tool base_url, falling back to
EXA_BASE_URL and then the SDK default. Overrides are validated like the
Serper base URL. web_fetch screens a configured backend with the shared
delegated-backend guard.

Co-authored-by: lachy3433 <lachlanallen.co@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:51:36 +08:00
EastonJiang
0d3a562c5f
fix(frontend): bound Live Browser WebSocket handshakes (#7301)
* fix(frontend): bound Live Browser WebSocket handshakes

* test: register browser-view scoped guidance

* fix(frontend): explain Browser Live connection failures

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 15:39:05 +08:00
TYY-2005
cc013c9680
fix(frontend): classify thinking-only token steps correctly (#7175)
* fix(frontend): classify thinking-only token steps correctly

* docs(frontend): explain thinking-only token step classification

---------

Co-authored-by: TYY-2005 <334029581+TYY-2005@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 11:24:26 +08:00
Xuehao Xu
29ba806fc6
feat(knowledge): add independent Wiki and entity relation retrieval tools (#7171)
* feat(knowledge): add independent Wiki and entity relation retrieval tools

* fix(knowledge): address review diagnostics and CI catalog coverage

* fix(knowledge): deduplicate graph evidence and update catalog e2e

* fix(knowledge): clarify bounded evidence and pin browser CI runners

* Update e2e-tests to run on ubuntu-latest

* Update workflow to use ubuntu-latest

* test(ci): stop pinning maintainer-controlled browser runner images

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-22 16:54:58 +08:00
Cordis798
17f2bea275
fix(api): preserve Request inputs in authenticated fetch (#7254)
* fix(api): preserve Request inputs in authenticated fetch

* docs(frontend): keep fetch guidance within the size budget

* test(api): address fetcher review feedback

---------

Co-authored-by: Cordis798 <284918518+Cordis798@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-22 16:05:43 +08:00
lihongyuan99
2ad2132336
fix(citations): pair inline code within a paragraph, not across blank lines (#5876)
* fix(citations): pair inline code within a paragraph, not across blank lines

maskInlineCode paired backticks over the whole document, so a stray backtick in
one paragraph and another in a later one formed a span CommonMark never renders.
The citation between them was masked out of the Sources list, and the stolen
opener simultaneously un-masked a real code span further down.

Scope the pairing to blank-line-delimited paragraphs, keeping the captured
separator so string length and occurrence indices still align with the original
markdown.

* fix(citations): end the paragraph on a CRLF blank line too

core/messages/utils.ts recognises a blank line as \r?\n[ \t]*\r?\n; the citations
scanner now uses the same shape, so CRLF markdown gets the same pairing limit
instead of falling back to whole-document pairing.

* fix(citations): mask fences in containers and end inline spans at block bounds

Follow-up to the paragraph-scoped inline pairing review on #5876.

Indented fences inside a list item, and blockquote-prefixed fences, were only
blanked by accident before: the whole-document backtick pairing happened to
close them. Scoping the pairing to a paragraph removed that safety net and let
sample citations surface as sources, so the fence matcher now accepts container
prefixes and is marker-aware.

Inline spans also end at the other block boundaries CommonMark uses - ATX
headings, thematic breaks / setext underlines, interrupting list items and a
blockquote's empty continuation line - not only at blank lines.

* fix(citations): anchor fence detection and end fences with their container

An unanchored container-prefix match turns any line that merely contains a
three-backtick run into a fence opener, which masks everything after it and
drops live citations. Anchor the match to the line start.

A fence also has to end with the container it was opened in: a blockquote loses
its fence at the first line without a `>` marker, and an indented fence at the
first non-blank line that is less indented than its opener. Without that, the
citation following a closed blockquote is masked away, which is the same failure
direction as the original bug.

* fix(citations): track the container a fence was opened in

Separate a fence marker's own indentation from the indentation its container
gives it, require whitespace after a list marker before treating it as a
container, count blockquote depth instead of testing it as a boolean, and check
a closer's indentation against the container before ending the block.

Cover the four shapes from the review on #5876 with tests, and check the scanner
against remark-parse over twenty fence shapes.

* fix(citations): cap an opener's indentation at three columns past its container

A marker four columns past the container's content column is an indented code
block, so it must not open a fence; otherwise the citation below it is blanked
to end of input instead of surfacing as a source.

* fix(citations): match fence openers and closers to what their tails allow

A closing fence is bare, and a backtick fence's info string carries no
backtick. So ` ```text ` inside a ``` block is literal content rather
than a closer, and ` ```md `x` ` is a paragraph line rather than an
opener. Missing either check sends the scanner the wrong way: the first
lets a citation after the fake closer surface as a phantom source, the
second blanks a citation the reader can actually click.

* fix(citations): measure fence columns in columns, and only close on a closer

A tab after a list marker advances to the next four-column stop, so sizing a
list item by the character length of its marker put the content column two
columns too far left and kept a dedented citation masked inside the fence.

A closing fence now comes from its own pattern instead of the opener's: the
opener also accepts list markers, which are plain content once a block is open,
so `- ``` ` inside a fence closed it early and surfaced the citation under it.

The optional space after a block quote marker is padding, not indentation, so
measuring a quoted marker from the raw prefix cost the fence one column of its
three-column budget and rejected a valid closer.

* test(citations): pin the list-nested closer shape as its own case

* fix(citations): keep a blockquote fence escape inside an indented code run

Losing the `>` closes a quoted fence, but the escaping line is not free text:
four columns of indentation at the top level starts an indented code block, so
its citations are still code. Without that, the paragraph-scoped inline pairing
this scanner replaced leaves them visible as phantom sources.

* fix(citations): read blank lines and surviving containers around quote escapes

* fix(citations): count a quote that opens behind a list marker

BLOCKQUOTE_PREFIX_RE matches from the start of a line, so a fence opened as
"- > ```md" recorded quoteDepth 0 even though it sits inside a quote. The
escape test then never fired and the fence masked to end of input, blanking
citations that remark-parse renders as live links: 35 of the 924 swept
container combinations were worse than main, all in that family.

Read the opener's own container prefix instead, and park the list stack the
quote hides in so a dedented line is measured against the item that survives
it.

* fix(citations): keep literal quote lines and multi-marker fences contained

A fence that was not opened in a block quote has no quote container a later
line can leave, so a `>` inside it is literal code: stripping it made an
indented content line read as column zero and escape the fence. A line can also
open several list items at once, and the fence belongs to the deepest of them,
so recording only the first marker put `- - - ```md` four columns past its
container and rejected a valid opener.

* fix(citations): scan the container prefix in one pass

Reading the containers a fence marker may sit behind was a single regex whose
alternation could take a space either as a step of its own or as the padding of
the next marker, so it described the same prefix exponentially many ways. A line
carrying no fence therefore made the engine enumerate every decomposition: the
deep-quote shapes tests/e2e/user-message-plain-text.spec.ts renders cost a second
at 26 markers, roughly double for each marker added, and never return for the
three thousand markers those messages carry, which is why the e2e job timed out
waiting for a message that had already rendered on main.

The scan now walks each position once. Which shapes open a fence is unchanged:
the 32-shape and 35-shape differentials against remark-parse, the 924-combination
sweep and this file's 50 existing cases all give the same answers as before.

* fix(citations): keep the list a finished quote lived in, and let paragraph text block a non-1 ordered fence

maskFencedCodeBlocks cleared the item stack on every blockquote-depth change, so a
fence opened after a quote ended measured against the document root and hid a
citation the reader had already left the item for. It also pushed any ordered
marker as a container, so a `2. ~~~md` line that CommonMark keeps inside the
preceding paragraph opened a fence and masked a rendered citation.

* fix(citations): test the paragraph boundary on the CR-normalized line

A CRLF document keeps its "\r" on every line after the split on "\n", and
THEMATIC_BREAK_RE is anchored at the end of the line, so a setext underline
"---\r" was not recognised as a leaf block. The paragraph it had already ended
stayed live, the following "2. ```md" was read as paragraph text, its fence
never opened, and the real closer was then re-read as a top-level opener that
masked the rendered citation to EOF.

Use the same CR-normalized line the position classification already uses, and
pin the LF/CRLF pair plus the cross-depth quote shape against the parser tree.

* fix(citations): keep list items and quote markers the reader keeps

Three container rules in the fence scanner disagreed with remark-parse:

- A dedented line of paragraph text is a lazy continuation, so it does not
  leave the list item. Popping the item there recorded the next fence at
  column zero and hid a rendered citation to end of input.
- Padding after a list marker tops out at four columns: the fifth belongs to
  an indented code block inside an item whose content starts one column past
  the marker, not to a column-six container.
- A `>` deeper than the quote markers a quoted fence already consumed is
  literal code. Stripping it made the content look dedented past the fence's
  column and ended the fence one line early, surfacing a citation the reader
  shows as code.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-22 16:05:03 +08:00
ShallAbby
c11ba84708
fix(frontend): keep collapsed sidebar navigation visible on mobile (#7198)
* fix(frontend): keep collapsed sidebar navigation visible on mobile

* fix(frontend): address mobile sidebar review feedback

* fix(ci): keep frontend agent guidance within size budget

* fix(frontend): align mobile sidebar focus and trigger state

* fix(frontend): keep Agents header actions within mobile viewport

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-22 15:13:01 +08:00
Totoro
0baa99ca90
feat(models): add scoped model controls to Web chats (#7249)
* test: stabilize reporter evidence and guard missing chat nodes

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

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

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

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-22 08:08:23 +08:00
Lucien
ff07f87cc3
fix(browser): preserve Live keyboard modifiers (#7213)
* fix(browser): preserve Live keyboard modifiers

* test(browser): cover Meta modifier on named keys

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 20:57:56 +08:00
Willem Jiang
4b7f2454fe
test(e2e): stabilize virtualized pagination and compaction checks (#7244)
* test(e2e): stabilize virtualized pagination and compaction checks

* test: register the scoped E2E guidance file
2026-10-21 18:58:53 +08:00
Willem Jiang
d4012c4b24
ci(e2e): split browser tests into four parallel shards (#7241)
* ci(e2e): split browser tests into four parallel shards

* docs: keep E2E guidance within the module size budget
2026-10-21 17:03:50 +08:00
Willem Jiang
e790e7e6ba
ci: upgrade Playwright to 1.62.1 for Ubuntu 26.04 (#7219) 2026-10-21 09:13:55 +08:00
Stella-xixi
5cef1a11ef
fix(checkpoints): run conservative retention after completion (#6051)
* fix(checkpoints): wire conservative retention after runs

* fix(checkpoints): fence retention before checkpoint reads

* fix(checkpoints): address retention review feedback

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

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

---------

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

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

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

* chore: remove redundant SVG sources from Goal fix

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

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-20 16:55:13 +08:00
Jason Draper
f2c2490d26
chore(frontend): drop deprecated hast and @types/gsap packages (#7108) 2026-10-20 11:30:44 +08:00
Lucien
1ae50826b1
fix(frontend): isolate workspace query caches by authenticated owner (#7146)
* fix(frontend): isolate query caches by authenticated owner

* fix(frontend): share auth owner across isolation boundaries

* docs(frontend): keep agent guidance within size budget

---------

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

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

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 10:24:16 +08:00
TYY-2005
36ee986d55
fix(frontend): restore thinking-only steps after reconnect (#7144)
* fix(frontend): restore thinking-only steps after reconnect

* docs(frontend): explain reconnect answer boundary

---------

Co-authored-by: TYY-2005 <TYY-2005@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 09:57:28 +08:00
EastonJiang
b8bb728368
fix(frontend): stop chat sends after unsuccessful upload responses (#7122)
Co-authored-by: Jaxon1216 <224378083+Jaxon1216@users.noreply.github.com>
2026-10-19 23:10:33 +08:00
WuWenbo
294de7cefd
fix(models): preserve keys after reverted connection edits (#6813) 2026-10-19 16:29:46 +08:00
Qiu
a1f23b294a
fix(frontend): preserve memory mutations against stale reads (#6959)
* fix(frontend): preserve memory mutations against stale reads

* test: register memory guidance in scoped inventory

---------

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

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

* fix(frontend): fence superseded agent save callbacks

---------

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

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

* fix: address client context review comments

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

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

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

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

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

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

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

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

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

* fix(runtime): bound numeric client context preferences

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: Blair Leng <25675774+BlairLeng@users.noreply.github.com>
Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-19 10:41:18 +08:00
TYY-2005
5f9a521d60
fix(frontend): stop stalled subtask pagination without losing steps (#6729)
* fix(frontend): stop stalled subtask pagination without losing steps

* docs(frontend): note the non-advancing page stop in the subtask backfill

The AGENTS.md backfill sentence only said the loop pages until a short page. It also stops on a full page that fails to advance the after_seq cursor, so record that stop condition as well.

The file sits at 49,149 of its 49,152-byte hard limit, so this is the minimal wording change that fits.

---------

Co-authored-by: TYY-2005 <334029581+TYY-2005@users.noreply.github.com>
2026-10-19 07:32:43 +08:00