187 Commits

Author SHA1 Message Date
Eilen Shin
81de0761ed
feat: improve file attachments and add Office artifact previews (#8009)
* feat(frontend): improve file attachment recovery and artifact presentation

* feat(extensions): add Office artifact previews with search and navigation

* fix: preserve public viewers and HTTP Office previews

* fix: address file preview and upload review findings

* fix: use typed upload errors and linear bookmark allocation

* fix(office-preview): isolate parsing and bound preview resources

* fix(ci): install Office preview fixture dependencies for E2E

* fix(ci): provision matching Playwright browsers before E2E tests

* fix(ci): isolate Python libraries and include Office documentation

* test(frontend): wait for download status to finish rendering
2026-11-12 20:30:17 +08:00
Wenchao An
9699f04cdc
feat(projects): add visible project memory with extraction controls (#8117)
* feat(projects): add visible project memory with extraction controls

* fix(projects): avoid replaying committed memory extractions

* chore(ci): retrigger project memory checks

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-12 09:09:44 +08:00
Wenchao An
3d826c6d95
feat(approvals): add Ask, Auto and Full tool permission modes (#8033)
* feat(hitl): add human-in-the-loop approval for high-risk tool calls

* fix(hitl): address approval review findings

* docs(hitl): keep agent guidance under size limit

* fix(hitl): guard approval pause recovery

* docs(hitl): shrink inherited guidance

* fix(hitl): harden approval admission and recovery

* fix(approvals): address review safety and reconciliation gaps

* ci: retry approval checks after runner timeout

---------

Co-authored-by: wangzeren <1004695029@qq.com>
2026-11-11 20:59:05 +08:00
Xinmin Zeng
2a97ec3e58
docs: turn README into an index and move reference detail to docs/guide (#8113)
* docs: turn README into an index and move reference detail to docs/guide

The README had grown to 393 KB because every change appended its notes there. The text moves verbatim into 18 topic guides under docs/guide/; only headings are re-leveled and relative links re-pointed. The README keeps the introduction, a quick start and a guide index. Contributor test notes move to CONTRIBUTING.md.

* docs: send user docs to topic guides and budget the root README

The rule 'update README.md for user-facing changes' is what grew the README. Guidance now points to docs/guide/, and check_agent_guidance.py fails when the root README exceeds 16 KiB (AG003).

* chore: remove unreferenced screenshots and finished plan notes

pr-build/ holds two PR screenshots (3 MB) from #1986. plans/ holds the plan for the merged #4049. docs/SKILL_NAME_CONFLICT_FIX.md is a February change note. Nothing references them.

* docs: route always-loaded AGENTS.md files to scoped guides

* docs(agents): cut scoped guides to scope, verify, traps, decisions

Each AGENTS.md keeps four kinds of content: the scope of the directory, the
verification commands, traps a local test run does not show, and decisions the
code cannot state. Values, field lists, function inventories, test names, issue
numbers and history are deleted, because the code, the tests and git state them.

Search and fetch providers share one community guide. Four guides with no
remaining content are removed. The size check now allows 6 KiB per file (hard
limit 8 KiB) and 24 KiB per chain (hard limit 32 KiB).

* style: format frontend guide table and guidance test imports

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-11 13:26:17 +08:00
Wenchao An
6fe87b65fa
feat(extensions): show package-owned usage in extension details (#8031)
* feat(extensions): show package-owned usage in extension details

* test(extensions): align bookmark checks with detail UI

* fix(extensions): reconcile package documentation and locale fallbacks

* fix(extensions): align discovery locale fallback

* fix(extensions): isolate malformed optional documentation

* docs: keep backend guidance within inherited budget
2026-11-10 18:41:27 +08:00
jianyx1
2598940283
fix(frontend): make chat stream server errors actionable (#8041)
* fix(frontend): make chat stream server errors actionable

* fix(frontend): log chat stream server errors

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-09 20:42:29 +08:00
Wenchao An
97746ad725
fix(frontend): open file chooser from attachment button (#8018) 2026-11-09 09:57:11 +08:00
Nan Gao
3067b5a5bd
fix(ui): tidy the extension detail layout and narrow-screen overflow (#7936)
* fix(capabilities): tidy the extension detail layout and narrow-screen overflow

The extension detail view led with a page-level reload button and sat the
back button beside it 3px off: the two flowed inline and aligned by
baseline, which the back button takes from its icon. It also kept a
directory search that filters nothing there, a heading close to the page
title, and status copy that read like body text. On narrow screens the tab
strip widened the whole page, leaving a horizontal scrollbar.

Lead with the back button and move reload to the opposite end, drop the
search from the detail view, and give the detail an icon, package name and
a status notice holding the installation guide. On the directory, reload
now sits beside the hint that explains it. The tab strip scrolls inside
itself and keeps the active tab in view, and reload collapses to an icon
below `sm` with its accessible name intact.

No copy changes.

* fix(capabilities): realign the tab strip on resize without moving the page

Review follow-up. The active tab was only realigned when the tab changed, so
a rotation or window resize could leave it clipped. Realign whenever the strip
resizes, and leave manual swipes alone.

Scroll only the strip. scrollIntoView would also move the page, pulling it back
up when a resize arrives after the user has scrolled past the strip.

Also hide the strip's scrollbar in browsers without scrollbar-width support,
and find the page scroller in the E2E test by computed style rather than by a
class name.
2026-11-06 14:00:04 +08:00
Totoro
593ba49458
feat(subagents): continue requested batch work and schedule explicit prerequisites (#7839)
* feat(subagents): continue native batches and honor item dependencies

* docs: link native batch follow-up changelog entries

* test(migrations): pin isolated history-seed upgrade to 0050

* fix(subagents): preserve follow-up scope, blockers and fair scheduling

* test(memory): freeze capacity evaluation time

* fix(subagents): guard follow-up admission and retain run controls

* fix(subagents): enforce runs:cancel on tool cancellation of follow-up batches

* style(tests): format test_batch_followup_host with ruff

* fix(subagents): drop the admission checkpoint check and keep SDK cancel compatible

The checkpoint check inside run admission waited on the checkpoint saver's
lock while holding the SQLite write lock, which could make another chat's
checkpoint write fail. Remove it; dispatch still checks the conversation
before admission.

A follow-up waiting for an unanswered question now keeps its claim until the
lease expires instead of rereading the conversation every poll. Cancel passes
the caller's run authority only to submitters that take it, so existing SDK
submitters keep the two-argument call. Document that retrying a prerequisite
by hand does not requeue its blocked items, and list 0052 as the migration head.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-05 15:10:21 +08:00
Wenchao An
2145c9ab1f
feat(models): discover provider catalogs and search chat models (#7871)
* feat(models): discover provider catalogs and search chat models

* docs(models): scope catalog guidance to owning directories

* test(guidance): register model catalog scoped instructions

* test(memory): pin capacity scenarios to a stable clock

* fix(models): guard catalog imports and align picker interactions

* test(memory): share the fixed clock with updater capacity tests

* fix(models): clear provider filters removed by catalog refresh

* refactor(models): share connection fields across model forms

* fix(models): keep active provider filters clearable

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-05 06:43:01 +08:00
easT-T
3dd9e8841f
fix(frontend): preserve live subtask steps during history backfill (#7864)
* fix(frontend): preserve live subtask steps during history backfill

* test(deermem): keep updater eviction fixtures fresh

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-04 20:16:09 +08:00
easT-T
3b627af8e7
fix(frontend): release rejected run-input attempts before retrying (#7790) 2026-11-02 15:31:05 +08:00
minibeartwo
7c3d2551b6
feat(frontend): add a token usage dashboard for the console APIs (#7636)
* feat(frontend): add token usage dashboard over console APIs

* fix(frontend): address token usage dashboard review feedback

* fix(frontend): route usage runs to their custom agent chats

* docs(frontend): keep agent guidance within the module budget
2026-11-02 15:29:46 +08:00
王一帆
d0ec8dcc5d
feat(knowledge): add optional ingestion and status center (#7518)
* feat(knowledge): add optional ingestion and status center

* docs(knowledge): record upstream CI budget fix and regression results

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-02 10:42:21 +08:00
Totoro
c48447a4ae
fix(runtime): present outputs before a loop wrap-up and explain step-limit stops (#7588)
* fix(runtime): present outputs before a loop wrap-up and explain step-limit stops

Loop-detection warnings now ask the agent to present the files it already
created in /mnt/user-data/outputs before its final answer, when
present_files is available.

A run that exhausts recursion_limit now ends with stop_reason
recursion_capped and a plain message that lists outputs it created but
did not present. An active goal stands down without an evaluator call.
The /wait error envelope keeps the GraphRecursionError name, and IM
channels reply with the step-limit message instead of a generic failure.

* docs(changelog): note run-end delivery and step-limit stops (#7588)

* test(runtime): stop pinning LangGraph's step count in the step-limit test

The produced paths and the message already follow the observed model
calls, so the exact count only pinned LangGraph's per-step accounting.
Also note on the step-limit goal branch that its caller passes no
evaluator factory, so it has to stay ahead of the evaluator section.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-27 10:34:34 +08:00
Totoro
ab8cd4e10f
fix(channels): keep IM binding codes on screen and report goal state in replies (#7591)
* fix(channels): keep IM binding codes on screen and report goal state in replies

Connecting Slack, Discord, Feishu, DingTalk, WeCom, Buzz or QQ now opens a
dialog that keeps the /connect command on screen with a copy button, its
expiry time and the binding status, instead of a toast that disappears.

The bots' /connect replies and the IM /goal replies now use the owner's
web UI language (else channel_connections.notification_locale), and /goal
reports the goal's state as the web goal bar does: in progress, why it
stopped, or that it was met.

* docs(changelog): note the IM binding dialog and localized IM replies (#7591)

* test(channels): check IM reply tables against every notification locale

The connect-reply check compared a fixed en-US/zh-CN pair, and the /goal
reply table had none. Both now match NOTIFICATION_LOCALES with the same
keys in each, like the existing _GOAL_REASON_TEXT check, so a new locale
fails here instead of raising KeyError at reply time.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-27 09:46:58 +08:00
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
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
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
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
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
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
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
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
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
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
e790e7e6ba
ci: upgrade Playwright to 1.62.1 for Ubuntu 26.04 (#7219) 2026-10-21 09:13:55 +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
LingYi
49adb0edec
fix(frontend): ignore message edit shortcuts during IME composition (#6768)
Co-authored-by: LingYi-Liang <270316896+LingYi-Liang@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 07:11:53 +08:00
Xuehao Xu
c056ee556a
feat(knowledge): add WeKnora read-only extension (phase 3) (#6950)
* feat(knowledge): add WeKnora read-only extension (phase 3)

* fix(weknora): complete extension catalog and harden response validation

* test(extensions): cover WeKnora discovery in browser catalog

* docs(weknora): explain extension and MCP use cases

* docs(weknora): correct guide language and sync Chinese overview

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-18 10:28:30 +08:00
Xuehao Xu
169002a4c9
feat(workflows): add Phase 2 approvals, immutable artifacts and configuration evidence (#6920)
* feat(workflows): add versioned approvals and run configuration evidence

* docs: trim repeated setup guidance to respect inherited size limit

* docs: leave room in inherited agent guidance

* test(channels): align Buzz capability expectation with upstream registry

* fix(workflows): fence file consumers and preserve review access

* fix(runtime): reuse assembly evidence key at run admission
2026-10-17 09:41:15 +08:00
Lucien
34316550df
fix(agents): preserve reasoning defaults when the model catalog is unavailable (#6907)
* fix(frontend): preserve untouched custom-agent reasoning defaults

* docs: keep frontend agent guidance within size budget

* test(agents): cover reasoning edits across catalog transitions

* fix(agents): expose stale defaults and validate model switches

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-16 17:22:21 +08:00
王一帆
6bc1020d34
feat(knowledge): decouple scope and read-only catalogs (phase 2) (#6904)
* feat(knowledge): decouple scope and read-only catalogs

* docs(knowledge): keep frontend guidance within repository budget

* fix(knowledge): align execution identity and harden provider boundaries

* docs: keep knowledge guidance within CI size budgets

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-15 15:10:39 +08:00
Totoro
c994826d06
feat(channels): generalize group notifications and add opt-in task/goal IM notices (#6903)
* feat(channels): generalize IM group targets and add task/goal run notifications

- Generalize proactive group notification capability (proactive_group_notifications), binding validation, and scheduled task group selection across Feishu, QQ, and WeCom while keeping WeChat personal-only.
- Add QQ group active notification delivery via POST /v2/groups/{group_openid}/messages (msg_type=2) and WeCom group active notification delivery via WS Client.sendMessage(chatid, body) with bot_id:group:chatid workspace isolation.
- Generalize notification outbox sources via migration 0043_runtime_im_notices (source_kind, source_key, run_id, nullable task_id/task_run_id) with backward-compatible SQLite/PostgreSQL upgrades.
- Add opt-in ordinary task & goal run IM notifications (run_completed, run_failed, human_input_requested) on the shared outbox, delivery worker, renderer, and settings UI.

* fix(notifications): address review feedback and group/runtime notification edge cases

* test(migrations): update CURRENT_HEAD to 0043_runtime_im_notices across migration chain tests

* fix(channels): preserve group binding names and legacy authorization

Carry owner-chosen group names through existing connect state, keep private bindings out of legacy group fallback, and restore completion metadata retries. Cover real binding and worker finalization paths and synchronize notification docs.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-15 10:30:55 +08:00
Lucien
8008a6195f
fix(artifacts): authenticate isolated HTML preview resources (#6701)
* fix(artifacts): authenticate isolated HTML preview resources

* docs(artifacts): retain precise path guidance within size budget

* fix: preserve HTML preview auth and rendering semantics

* fix(artifacts): quote rewritten CSS URLs

* docs: keep merged artifact guidance within size limit

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-15 10:15:44 +08:00
Xuehao Xu
76102fab0b
feat(extensions): add opt-in script workflow MVP (#6858)
* feat(extensions): add opt-in script workflow MVP

* fix(workflows): clarify timeout handling and stabilize request identity

* fix(ci): register workflow catalog and trim inherited guidance

* fix(workflows): retain launch ownership and resolve injected input anchors
2026-10-14 22:42:17 +08:00
LingYi
96f930f5be
fix(frontend): align one-time DST conversion with the scheduler (#6756)
Co-authored-by: LingYi-Liang <270316896+LingYi-Liang@users.noreply.github.com>
2026-10-14 22:17:51 +08:00
Totoro
517a8309a2
feat(extensions): allow ModelTool plugins to return structured artifacts (#6861)
* test(plugins): capture opt-in artifact RED (18 failed, 13 passed)

* feat(plugins): add typed opt-in artifacts (31 ToolNode tests pass)

* test(extensions): require API 0.2.6 (version RED verified)

* build(extensions): pin API 0.2.6 (installed wheel guards GREEN)

* test(examples): verify plugin artifacts through checkpoint restart

* test(extensions): reproduce artifact key coercion (8 RED cases)

* fix(extensions): reject non-string artifact keys (185 tests GREEN)

* fix(frontend): list bundled retrieval example in extension catalog

* test(mcp): reconcile recovered notification at stored due time

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-14 17:24:32 +08:00
Totoro
cb90f5dca4
feat(channels): control IM notifications and add WeChat/QQ delivery (#6850)
* feat(channels): control IM notifications and add WeChat/QQ delivery

* fix(scheduler): skip notification preference locks without recipients

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-14 11:14:48 +08:00
haotianqi666
4210854a9f
fix(frontend): resolve local images in Markdown artifact previews (#6827)
* fix(frontend): resolve local images in Markdown artifact previews

* fix(frontend): key artifact images by resolver context

---------

Co-authored-by: haotianqi666 <250384991+haotianqi666@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-14 08:59:53 +08:00
Lucien
c5fd453c37
fix(artifacts): preserve account-scoped drafts across navigation (#6700)
* fix(artifacts): preserve account-scoped drafts across navigation

* docs(artifacts): keep draft lifecycle guidance within its budget

* fix(artifacts): retain drafts through unknown auth and isolate typing updates

* fix(artifacts): preserve drafts when visiting public showcases

* fix(artifacts): preserve edits after pending save
2026-10-13 15:44:20 +08:00
Eilen Shin
e98179a9d3
feat(models): register managed models with provider presets (#6757)
* feat(models): register managed models with provider presets

* docs: add PR 6757 UI comparison screenshots

* test: fix model management CI checks

* docs: remove PR 6757 screenshots

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-13 08:14:23 +08:00
Totoro
5ae95e6a34
feat: add Feishu group notifications and fix mention caret placement (#6762)
* feat(feishu): deliver scheduled-task notifications to bound users

* feat: add Feishu group notifications and fix mention caret placement

* fix(frontend): preserve group targets while capabilities load

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-13 07:17:56 +08:00
haotianqi666
338f8819a4
fix(frontend): preserve schedule when switching to custom cron (#6749)
Co-authored-by: haotianqi666 <250384991+haotianqi666@users.noreply.github.com>
2026-10-12 20:04:48 +08:00
Hyeonsang Cho
eb7fc28010
fix(frontend): keep the thread list offset past hidden side chats (#6702)
* fix(frontend): keep the thread list offset past hidden side chats

Primary thread lists hide sidecar threads on the client, so a page of 50
visible chats can consume more server rows. fetchInfiniteThreadsPage kept
that server offset in a Symbol property on the page array, but TanStack
Query's structural sharing (replaceEqualDeep) rebuilds a changed array
without symbol keys, and the list cache writers (rename, unread, pin,
delete, upsert) rebuild pages with map/filter. After the first refetch or
cache write, getInfiniteThreadsNextPageParam fell back to the sum of the
visible page lengths, so the next page started at 50 instead of 53 and
re-fetched rows already loaded. The same fallback treated a full page
that lost a row to a local delete as the end of the list.

Each cached page is now { threads, hiddenCount, hasMore }. The next offset
is the sum of visible and hidden rows over the loaded pages, so local
deletes and inserts still move it with the server list, and the end of the
list comes from the server response rather than the page length.
useInfiniteThreads projects the pages back to thread arrays with select,
so list components are unchanged.

* test(frontend): pin the sidebar's next-page offset past side chats in E2E

Three side chats in the first 50 server rows push the sidebar's second
page to offset 53. After a rename through the sidebar menu, scrolling to
the sentinel must request offset 53, not 50. On main this test fails with
the request at offset 50.

* docs(changelog): reference #6702

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-12 17:35:27 +08:00
Hyeonsang Cho
91a88e08e6
fix(frontend): show a date-only run_at at midnight in the task's zone (#6692)
* fix(frontend): show a date-only run_at at midnight in the task's zone

A chat-created one-time task can store run_at as a bare date
("2026-12-07"). The server reads it with datetime.fromisoformat as
midnight in the task's timezone. onceRunAtInstant instead returned it
unchanged: its offset check matched the trailing "-07" as a UTC offset,
and its local-time pattern needed a time anyway. new Date() then read it
as UTC midnight, so the schedule line, chat card and edit form showed
08:00 in Asia/Shanghai and the previous evening west of UTC.

A bare date is now matched first and converted as midnight in the task's
zone, the same way a time-bearing local run_at is.

* docs(changelog): reference #6692

* test(frontend): pin the clock in the date-only run_at E2E

The detail page formats the schedule line against the real clock: it
drops the year inside the current year and switches to relative labels
in the final week. The exact-text assertion therefore failed throughout
2027 ("Once, Dec 7 00:00 ...") and again as "Once, Tue 00:00 ..." in the
week before the run. Fix "now" at 2026-10-01, the same instant the unit
test uses, so the assertion stays deterministic.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-11 20:34:07 +08:00
Yeager0204
782fed9135
feat(skills): show YAML load errors for owned custom skills (#6623)
* feat(skills): show YAML load errors for owned custom skills

* docs: capture skill diagnostics with real gateway validation

* fix(skills): clarify YAML diagnostic scope

* docs: remove PR evidence directory

---------

Co-authored-by: kwsYeager <331533331+kwsYegar@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-11 12:52:48 +08:00
yang rui
a333ac3189
feat(search): add Webz news search provider and setup integration (#5880)
* feat(search): add Webz news search provider and setup integration

* fix(webz): honor caller result limits and clarify page counts

* fix(frontend): render Webz search source links

* fix(frontend): skip malformed search source entries
2026-10-10 18:49:59 +08:00
Totoro
0fe6e63a68
feat(goal): show why a chat goal stopped and when it was met, and redact the goal evaluator and continuation input (#6556)
* feat(goal): record the met goal and keep goal state on the history head

- Add a goal_outcome LastValue channel (GoalOutcomeState). Only the
  guarded clear of a satisfied ordinary goal writes it, in the same
  checkpoint as the clear; the reply anchor is the latest visible AI
  message with text, the reply the visible signature keys on. A goal
  stored without created_at still clears and gets a record.
- write_thread_goal removes the record on every other goal write and
  lists it in channel_versions, new_versions and metadata writes when
  it changes. Threads that never had a record keep their checkpoint
  shape. The sync-only SqliteSaver and CachedHistorySaver, which take
  the #6448 sync fallback, store and drop it the same way.
- goal_outcome is server-owned: POST /state and non-internal run input
  that carry it get 400. A POST /state goal write, or a goal in
  external run input, clears it.
- A branch head written onto its replay base resets the channels the
  head lacks, so a goal or record that write_thread_goal removed does
  not come back. Branching from the met turn keeps the record without
  the met goal.
- The POST /history head now carries an active goal and an achieved
  goal_outcome, also before any message exists, so the post-run
  history refetch keeps them. Older entries stay title/thread_data.
- contracts/thread_goal_contract.json pins the chat stand-down and
  check-failure codes, the record shape and status, the head keys and
  the host-written reasons.

* feat(frontend): show why a goal stopped and when it was met

- The goal bar derives its state from the active goal, the goal_outcome
  record and the messages: set, in progress, continuing, stopped (limit,
  auto-continue off, no progress, token budget, missing evidence, failed
  run, unknown code), waiting for you, waiting, couldn't check the goal,
  paused and goal met. An 8/8 stand-down now reads "Stopped" with the
  next step instead of "Continuing 8/8", and a count-0 stand-down no
  longer looks like a fresh goal.
- "Goal met" shows while its anchor reply is the latest visible message
  (a visible human message, or a visible AI message with text, moves the
  chat on), so a reload agrees with the live view. An active goal wins
  over the record, and this tab's /goal set or clear hides it.
- useActiveGoal drops the optimistic goal when the record of that same
  goal arrives, which fixes the bar that stayed after a goal met on the
  first run (the clear frame has no goal key), also for a goal stored
  without created_at. Server goal values pass through isGoalState; a
  /goal status read uses the backend's own active check, so the bar, the
  toast and the edit lock agree with the server.
- Row 2 gives the reason and what to do next; Details holds the full
  goal, the checker's own note (never host-written text, a check failure
  or a paused goal's older check), the assumption note and an unknown
  code, and starts collapsed in each new state. The bar is a labelled
  section with a live region that announces state changes only (another
  goal's same state counts) and re-reads its text after a language switch.
- The edit pencil on the latest turn is shown disabled with "Editing is
  off while a goal is set" instead of hidden; the message toolbar shows
  on focus-within, so keyboard users can reach it.
- A /goal set or clear rejected with 409 keeps the draft and says a run
  is still going; other failures keep the server's message and the draft.
  A /goal request replaced by a newer one leaves the composer alone, so
  a double submit no longer empties it before the second one fails.
- Copy lives in inputBox.goalBar (en-US, zh-CN); a contract test reads
  contracts/thread_goal_contract.json, and the raw-identifier guard now
  catches stand-down codes and goal field names.

* test(frontend): cover the goal bar on both chat pages

- tests/e2e/goal-status.spec.ts runs every case on the chat page and the
  agent chat page, since each wires the goal bar and the edit lock itself:
  a goal stopped at 8/8, one waiting for details and one waiting on an
  open question card show their reason and next step, also after a
  reload; the locked edit pencil gets keyboard focus, shows its toolbar
  and tooltip, does not open the editor, and hides while a file uploads;
  Goal met after one automatic follow-up (In progress, Continuing 1/8,
  then "Auto-continued once") survives a reload and hides after the next
  visible message; a goal met on the first run of a new chat replaces the
  set bar and frees the pencil, and a new /goal replaces Goal met while
  its own run goes on; a /goal set or clear refused with 409 keeps the
  draft for the retry and shows its own busy toast.
- The mock POST /history head carries only the contract's
  history_head_keys, with goal only while active and goal_outcome only
  when achieved; thread state and run values omit an unset goal instead
  of sending goal: null. PUT and DELETE /goal drop the met record,
  setGoalWritesBusy returns the Gateway's 409, and a repeat create of a
  requested thread id returns the thread as is, like the Gateway.
- The raw-identifier guard also catches checker blockers and the met
  record's field names.
- frontend/src/AGENTS.md: the GoalStatus sentence now names goal,
  goal_outcome and useActiveGoal, reworded in place.

* test(frontend): keep the draft through a failed upload and a busy /goal

#6412 fires onSent only once a send is dispatched, so a failed upload
keeps the text, files and stored draft. A /goal the Gateway refuses with
409 never reaches sendMessage, and the composer rejects it so PromptInput
keeps the text. Drive both through the real sendMessage in one composer:
a failed upload keeps "summarize it" stored, a busy /goal keeps its text
and stored draft with no run, and the retry saves the goal and starts
one run with the objective, not the "/goal" text. After that run the
composer is empty and no stored draft comes back.

* fix(frontend): drop the stop condition's closing period before the pause clause

A stored stop condition often ends with a period, so the schedule card
read "…remain)., pauses itself" (and "…。,满足后自动暂停" in Chinese).
Trim the condition's closing sentence punctuation before the clause that
follows it. The event line, where the condition ends the sentence, is
unchanged.

* fix(frontend): point "evidence missing" at the agent, not a user reply

"Evidence missing" asked the user to reply with what's left, which is
the push a goal is meant to save. It now asks the agent to show the
result or explain what's missing, and offers /goal clear only for a goal that
no longer applies: clearing drops the met record, so work that is right
would never show "Goal met".

zh-CN follows. Copy only: no key, state or mapping changes, and the
scheduled page's reason labels are untouched.

tests/unit/components/workspace/goal-status-next-steps.test.ts checks
the row in en-US and zh-CN, and that no stop row offers /goal clear
without that condition. goal-status.dom.test.tsx and
goal-stream.dom.test.tsx now expect the new copy.

* fix(frontend): say "Couldn't check the goal" once in the goal bar

After a goal check that could not run, the bar's second line borrowed
the scheduled page's whole reason, so it repeated the chip: "Couldn't
check the goal · Couldn't check the goal; the run itself may be fine.
It's checked again after your next message." Chinese had the same
repeat.

The line now gives only why the check failed ("The run itself may be
fine", "No final reply was saved" or "The chat changed during the
check"), then when it is checked again. The screen-reader announcement
follows. The chip and the scheduled page's reason labels are unchanged.

tests/unit/components/workspace/goal-status-unchecked.test.ts checks
every check-failure code in en-US and zh-CN: the label appears once,
and the line still says why the check failed.

* feat(goal): redact the evaluator input when pii_redaction is enabled

The goal evaluator calls its model directly, outside
PiiRedactionMiddleware, so it sent raw user lines, tool arguments, tool
results and Human Input Card answers even with pii_redaction enabled.

With pii_redaction enabled:
- the messages in the evidence window are redacted whole before the
  evidence caps can cut an identifier in two, as TitleMiddleware does.
  This reuses redact_queued_messages, which already covers content,
  call arguments and card answers. Earlier messages are left alone: the
  evaluator never sees them, and the pass is synchronous work on the
  event loop (0.84 s for an 800-message, 4 MB thread, 0.06 s for its
  window);
- the assembled input, the goal objective included, is redacted again.

A redaction error fails the check with evaluator_failed; raw text is
never sent instead. When redaction is off, the input is byte-identical.
Thread state keeps the raw text. README.md, config.example.yaml and the
middleware docstring list the evaluator with the other redacted inputs.

tests/test_goal_pii.py puts an address where the evidence caps cut a
tool argument, a tool result and a card answer, and checks the
objective, the unchanged input when redaction is off, that only the
evidence window reaches the redactor and gives the same evidence as
redacting the whole thread, that a redaction error stands down with
evaluator_failed before the model is called, and the worker path.

* fix(goal): redact the hidden continuation when pii_redaction is enabled

The hidden /goal continuation repeats the goal objective and the
evaluator's reason and evidence summary. PiiRedactionMiddleware skips
it, since it is a framework message and not a user message, so with
pii_redaction enabled each continuation turn sent them raw to the
agent's model, and the message stayed in the thread for later model
calls. The durable-context block and the evaluator input already redact
the objective.

With pii_redaction enabled, make_goal_continuation_message now redacts
the objective, the reason and the evidence summary with redact_text
before the message is built. The thread keeps the redacted message,
which the UI hides; the goal state keeps the raw objective.

The worker builds the message before it counts the continuation. A
redaction error fails the check the way an evaluator redaction error
does: run_failed with stand_down_reason evaluator_failed, the goal stays
active, nothing is sent and the continuation budget is not spent. Only
the exception type is stored. With redaction off, the message is
byte-identical.

README.md, config.example.yaml and the middleware docstring list the
continuation with the other redacted inputs.

tests/test_goal_continuation_pii.py checks the redacted objective,
reason and evidence summary against the tokens redact_text gives, the
unchanged message with redaction off or no config, the worker path
under the run config, and a redaction error.

* docs(changelog): link the goal outcome entries to #6556

Fill the four ([#PR]) placeholders in CHANGELOG.md and CHANGELOG_zh.md with this PR's number and add the matching link references.

* fix(frontend): keep an active goal stored without timestamps visible

POST /threads/{id}/state and run input can store an active goal without
created_at or updated_at. The backend still evaluates it and rejects an
edit with 409 (is_active_goal checks only status "active"), but
isGoalState required both timestamps, so history and live values
dropped it: both chat pages hid the goal bar and unlocked the last-turn
pencil, and only a /goal status read, which used a looser predicate,
brought them back.

isGoalState now accepts any record with a string objective and status
"active", and normalizeGoalStatusRead uses it, so thread values, live
updates and status reads share one rule. Malformed or inactive values
are still dropped.

Tests cover a sparse active goal from the history head and from a live
update before the post-run refetch (bar shown, hasGoal true), and a
malformed goal that stays hidden.

* test(goal): check the outcome write metadata the same way on every saver

PostgresSaver stores checkpoint metadata through
get_serializable_checkpoint_metadata, which drops "writes", so the
[postgres] case of test_goal_outcome_round_trips_through_the_sync_fallback
failed with KeyError: 'writes' when CI ran it against a live database.

The test now records the metadata write_thread_goal passes to put and
asserts the clear's and the next goal's "writes" there, for every saver.
The stored channel values are checked as before, and on sqlite and
cached-sqlite the stored "writes" must still equal what was passed.

* docs(agents): keep goal_outcome out of the agents guidance chain

The middlewares AGENTS.md chain is already over the 98,304-byte AG002
hard limit on main, and the PR-mode check fails any chain a PR touches,
so adding goal_outcome to the ThreadState field list in
deerflow/agents/AGENTS.md failed the agent-guidance job. goal_outcome
stays documented in runtime/AGENTS.md and backend/app/gateway/AGENTS.md,
which are outside that chain.

* refactor(goal): read the evaluator's evidence and redaction from one window

format_visible_conversation recomputed the window start that the redaction
pass takes from _evidence_window. Both now use _evidence_window, so a later
change to the cap or to what counts as visible moves both.

The window test now fills the window with tool-only replies and tool results.
Before, it caught a cap change in one copy but not a change to what counts
as visible.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-10 00:20:45 +08:00