19 Commits

Author SHA1 Message Date
Loveca
067c9736b6
feat(goal): add /goal from-chat for IM channels (#8200)
* feat(goal): add /goal from-chat for IM channels

First slice of #7050: add an explicit IM command that composes the active goal
from the recent conversation instead of taking the text literally.
Natural-language intent detection remains out of scope.

Gateway:
- POST /api/threads/{thread_id}/goal/from-chat synthesizes the objective
  from the thread summary and recent agreement turns (visible user/assistant
  text, clarification questions, and card answers), preserving agreed
  constraints and the stopping condition.
- Reject a busy thread before the model call; the save reuses the existing
  checkpoint-write reservation so a run that starts mid-synthesis still wins.
- Refuse to synthesize while a human-input request is unanswered, and return
  structured no_context/unclear/synthesis_failed outcomes without echoing
  model-generated reasons.

IM:
- /goal from-chat [hint] shows the saved objective, then starts exactly one
  run through the ordinary chat path; a longer read timeout covers synthesis.
- Map busy/pending/unclear/no-context/timeout outcomes to localized replies;
  a saved goal whose run cannot start is reported honestly.
- The TUI explains that from-chat is not supported yet; the web input box
  still treats it literally (documented follow-up).

Docs: README, CHANGELOG, IM channel guide, TUI guide, AGENTS, and the
acceptance plan/execution record. Tests: new route, synthesis and manager
suites, plus parser and TUI coverage.

* fix(goal): keep the from-chat confirmation visible on every channel

The saved-objective reply was published as a non-final progress update.
QQ and DingTalk card mode drop non-final outbounds before a stream exists,
and Feishu/Telegram fold the update into the run's stream, so the run
output overwrote the objective. Publish it as the turn's final message.

Redact PII on whole lines and the summary before the context budget can
split an identifier; the composed prompt is still redacted once more as
defense in depth, matching the goal evaluator's direct call.

Also correct the gateway/IM docs (from-chat never creates a checkpoint,
the busy pre-check is worker-local, and synthesis uses the deployment
default model), fix the route test clock patch, and add regression tests
for the final-outbound and truncation-boundary cases.

* fix(goal): align from-chat with the goal-route admission gates

Reject a from-chat save while a tool approval is pending, matching
PUT/DELETE /goal, and suppress subagent-batch continuations queued under
the previous goal after the save. Both invariants landed upstream while
this branch was in review; the new route now shares them.

Add route tests for both behaviors.

* docs(changelog): remove the from-chat entry to avoid conflicts

The maintainer asked to drop the changelog entry because the section conflicts with upstream changes. The feature remains documented in the goals guide and README.

* fix(goal): close three review gaps in /goal from-chat

- Enforce model-use authorization for the deployment default model before
  the synthesis call, matching the suggestions route, so a denied role
  never sends conversation context to the model.
- Save with expected_checkpoint_id pinned to the head the synthesis read;
  a write that landed during the model call is a 409 goal_changed instead
  of silently clobbering the newer goal.
- Re-check the conversation binding before starting the run: a /new during
  synthesis keeps the goal in the original thread and reports that the
  task did not start instead of running it in the switched conversation.

* fix(goal): preserve from-chat handoff and run status

Pin the saved thread through run preparation and reject a changed binding.
Read configuration and model authorization off the event loop, and report
rejected starts separately from requests whose response was lost.

Cover the handoff races and streaming/wait admission outcomes through the
real channel manager and SDK transport, plus file-backed config under the
strict blocking-I/O detector.

Co-authored-by: Loveca <44801706+Loveca@users.noreply.github.com>

* fix(goal): preserve reasoning tags in synthesis JSON

Use the existing JSON-aware reasoning stripper so literal tag text in
objective and reason values survives parsing. Cover complete and unclosed
tags while still removing model reasoning before the JSON payload.

Co-authored-by: Loveca <44801706+Loveca@users.noreply.github.com>

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Totoro <msy626836554@gmail.com>
2026-11-13 14:22:58 +08:00
yi-coding
94447d12b6
fix(memory): preserve summaries when extraction returns invalid types (#8192)
Co-authored-by: xiaoyi <xyi74976-del@users.noreply.github.com>
2026-11-13 14:13:47 +08:00
Wistom Zhang
2ff88fbb29
feat(memory): opt-in reserved categories for DeerMem hybrid-v1 eviction (#8194)
* feat(memory): support category capacity reservation in DeerMem hybrid-v1 eviction

In long-running personal assistant interactions, core user profile facts
(such as health allergies, coding style, or identity constraints) can suffer
from capacity starvation as high-confidence conversational context accumulates.

This change introduces opt-in category capacity reservation for DeerMem:
- In DeerMemConfig and select_facts_for_capacity, support eviction_reserved_categories
  (defaulting to ['correction'] for historical compatibility) and
  eviction_decay_exempt_categories (defaulting to empty list).
- Wire MemoryUpdater to pass the configured categories to select_facts_for_capacity.
- Add deterministic offline unit tests in test_memory_personal_assistant_protection.py
  verifying root-cause reproduction, bidirectional lifecycle (clean explicit removal
  and contradiction replacement), and strict backward compatibility under defaults.

* fix(memory): preserve correction priority in reserved capacity

Co-authored-by: Wistom Zhang <77424093+Yakyou7@users.noreply.github.com>

---------

Co-authored-by: Totoro <msy626836554@gmail.com>
Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-11-13 11:09:41 +08:00
Loveca
bce5dc3327
fix(feishu): receive post attachments from the top-level files array (#8190)
* fix(feishu): keep attachments sent with post messages

* fix(feishu): keep user-typed file placeholders and malformed files arrays from losing attachments

* test(feishu): pin mixed inline attachment behavior and consume the post file count

* fix(feishu): parse post commands before adding attachment placeholders

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
Co-authored-by: Totoro <msy626836554@gmail.com>
2026-11-12 22:09:20 +08:00
Wang guanyu
b16e197618
fix(models): close Codex async HTTP requests on cancellation (#8053)
* fix(models): close Codex async HTTP requests on cancellation

* test(models): isolate loopback cancellation checks from proxies

* docs: record Codex cancellation fix in changelog

* docs: translate Codex cancellation changelog entry

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-11-12 21:55:08 +08:00
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
Evan
b4410f64d4
fix(agents): keep the delegation ledger exact across HITL approval and reused call ids (#8128)
* fix(agents): count each delegation once when a provider reuses its call id

Tool-call ids are unique per response, not per conversation. Some providers
number every response's calls from call_0, so two task calls in one run can
share an id.

The delegation ledger used (run_id, tool_call_id) as a delegation's identity:

- extract_delegations built one id -> entry map over the whole run, so a
  later call_0 replaced the earlier delegation's description and took its
  result while it was still running.
- merge_delegations replaced the same (run_id, id), keeping one entry.
- SubagentLimitMiddleware counted distinct ids, so the per-run cap
  (subagents.max_total_per_run) never advanced past the first call_0 and a
  run could launch any number of subagents one response at a time.

Each entry now also records dispatch_message_id, the AIMessage that issued the
call, and results pair through pair_tool_call_results, which answers a call
only from its own response. The reducer, DurableContext's change filter and
the cap use the extended identity. An entry without a dispatch id still
updates its run's entry for that call id, and a dispatch-scoped entry adopts a
ledger entry written before the field existed, so runs in flight across the
upgrade are not counted twice. The steer path names the dispatching AIMessage
from the tool request's state, so its delegation update and the graph's own
capture stay one entry.

* fix(agents): close an earlier run's dangling dispatch by its own response

_close_delegations_left_by_earlier_runs decided "answered" by
(run_id, tool_call_id). When a stopped run had dispatched call_0 twice,
the first reply kept the second, dangling entry in_progress, so the
lead kept hearing "already delegated" for work nothing would finish.

Entries that carry dispatch_message_id are now decided by pairing that
response's calls through pair_tool_call_results. Legacy entries and
dispatches no longer in history keep the id-based inference.

Also pin that two runs reusing call_0 stay two ledger entries, and
cross-reference the three delegation identity helpers.

* fix(agents): settle an approval-gated task delegation in the ledger

With `task` in `hitl_approval.tools`, the paused run records the dispatch
as in_progress and the resume run executes it. The resume run opens with
no HumanMessage, so its capture window starts after the pre-existing
messages and never contains the dispatching AIMessage: the result was
dropped and the entry stayed in_progress with no result_brief, forever.

DurableContext now pairs results the resumed window receives with the
last pre-existing dispatch and settles the issuing run's entry, keeping
its run_id instead of re-tagging it. HITL denials of `task` carry
`cancelled` subagent status, so a rejected or denied delegation settles
too.

* fix(agents): address review on the delegation ledger

- Steer path: hold each wait's dispatch AIMessage locally and pass it to
  the observer, so a later response reusing the call id while the first
  call is still observed cannot take over its args or dispatch id.
- New-turn close: a dispatch whose reply carries subagent status settles
  from that reply instead of staying in_progress, covering a crash
  between the tools checkpoint and the next capture.
- Reuse thread_state.delegation_key instead of a duplicate identity
  helper in DurableContext.
- CHANGELOG: reference #8128.

* docs(changelog): drop the #8128 entry per review
2026-11-12 19:46:58 +08:00
N
06634003a5
fix(subagents): apply tool policies to middleware-declared tools (#8136)
* fix(subagents): apply tool policies to middleware-declared tools

* fix(subagents): address tool-policy review feedback

Normalize declaration conversion errors with middleware context, document
generated discovery exemptions, and add the Unreleased changelog entry.
Audit final explicit and middleware tool sets before graph creation, with
identity-based exemptions for generated discovery helpers and regressions.

* fix(subagents): reuse tool name resolution and clarify policy errors
2026-11-12 19:12:02 +08:00
paer
2dc68b242f
fix(subagents): prevent concurrent file-backed creates from overwriting definitions (#8132)
* docs(subagents): describe create-only file publication

* fix(subagents): prevent concurrent create overwrites

* test(subagents): cover independent create races

* docs(persistence): record create-only publication

* docs(config): explain managed subagent file claims

* docs(persistence): retain create-only publication rule

* docs(subagents): retain shared file create semantics

* fix(gateway): probe managed subagent hard links on startup

* test(gateway): cover managed subagent volume probes

* test(gateway): keep volume probes off the event loop

* docs(config): describe startup hard-link warnings

* docs(changelog): reference managed subagent fix #8132

* docs(changelog): keep the fix entry clear of append conflicts
2026-11-12 14:39:25 +08:00
ZephyrWang
b80191fad3
fix(memory): reject duplicate fact IDs during import (#8151)
* fix(memory): reject duplicate fact IDs during import

* fix(memory): explain duplicate IDs in import response

* docs(changelog): link duplicate import fix PR

* fix(memory): keep duplicate validation portable

---------

Co-authored-by: AochenShen99 <sac1063467113@hotmail.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-12 11:42:55 +08:00
2SinceJuly
6eb4833598
fix(client): scope memory management to the named agent's bucket (#8146)
* fix(client): scope memory management to the named agent's bucket

A named DeerFlowClient stored and read its memory in that agent's bucket:
MemoryMiddleware and the memory tools both pass agent_name. The management
methods on the client did not, so create_memory_fact wrote the fact into the
user's default bucket while the agent's injected context came from its own
bucket — the fact was persisted but never recalled by the same client.

Pass the client's agent_name through get_memory, export_memory,
import_memory, reload_memory, clear_memory and the single-fact CRUD calls.
An unnamed client keeps the default bucket, and a backend that ignores the
scoping keyword keeps raising its own unsupported-operation error.

Fixes #8142

* fix(client): refuse named memory calls a backend cannot scope

DeerFlowClient forwarded the agent name to every memory management call, but
a backend that declares no agent-scoped storage ignores the keyword and
answers normally. A named client then reported success while the call read,
cleared or exported the user's default bucket, the same silent
wrong-bucket behaviour #8142 is about. Check the capability flag before
forwarding the scope and raise an unsupported-operation error, as the
Gateway already does with 501.

Resolve the scope in reload_memory before the get_memory fallback so an
unsupported scope is not mistaken for a backend without reload support and
retried as a user-wide read.

* chore: drop the CHANGELOG entry

The changelog is generated at release time, and the hand-written entry points
at #8146 without a link definition, which breaks
tests/test_changelog_reference_links.py. Removing it keeps the file released-
generated, per the reviewer's request.

* docs(memory): explain scoped noop client behavior

---------

Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-12 09:21:28 +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
SuKoYa07
fc7c7e012c
fix(frontend): restore handed-off artifacts from thread history (#8080)
* fix(frontend): restore handed-off artifacts from thread history

* fix(frontend): reject duplicate artifact handoff receipts

* docs(frontend): fit artifact guidance within budget

* fix(frontend): accept consistent retried artifact handoffs

---------

Co-authored-by: lovesky514514-cmd <273698517+lovesky514514-cmd@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-11 21:00:33 +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
zuiqiangxijiejin
c7bc8bab67
fix(docker): handle inline YAML comments in sandbox mode detection (#8103)
* fix(docker): honor inline comments on the sandbox section header

* fix(docker): match the sandbox provider value exactly

* test(docker): ignore nested provisioner URLs

* docs(docker): explain exact sandbox provider matching
2026-11-11 20:11:53 +08:00
Heo Hao
80cb6404b6
fix(browserless): reject timeouts httpx cannot use (#8025)
* fix(browserless): reject timeouts httpx cannot use

`_coerce_timeout` forwarded any int, float or numeric string to
`httpx.AsyncClient(timeout=...)` unchanged, so a configured `timeout_s: 0`
or a negative value failed every fetch and capture with `ConnectTimeout`
against a healthy backend, `nan` raised `ValueError: deadline must not be
NaN`, and `inf` left the request without a deadline so it never completed.
Each of those silently disabled the provider.

Fall back to the documented 30s default with a warning when the number is
not a usable deadline. This matches `crawl4ai._coerce_timeout` after #7481;
`jina_ai._coerce_timeout` still forwards non-positive values.

* fix(browserless): address timeout review and format tests

Explain how HTTPX handles NaN and positive infinity. Document that
Browserless accepts positive finite timeouts without an upper limit,
and pin that behavior with a 1e308 test case.

Crawl4AI and Jina timeout validation are tracked in #7481 and #7823.
Remove the Browserless docstring's claim that Crawl4AI shares its
coercion behavior. Format the test file for the backend lint check.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-11-11 19:43:53 +08:00
Wistom Zhang
218c64180e
feat(summarization): align built-in compaction prompt to preserve user requirements and approval conditions (#8114) (#8114)
In long-running agent workflows with repeated context compaction, empirical gateway ablations across 132 real sessions revealed that standard boundary compliance remains robust, while semantic drift can occur when model-generated summaries rephrase literal approval phrases and action conditions.

This change converges PR #8114 into a clean built-in summary prompt enhancement:
- Replaces regex extraction, tag elevation, and heuristic revocation with an extended built-in DeerFlow summary prompt (_DEERFLOW_SUMMARY_PROMPT).
- Instructs the summarizer to faithfully record active user requirements, literal approval phrases, permitted actions, and later explicit revisions without paraphrasing.
- Treats summary text strictly as historical data, fully preserving the security boundary and control/data plane separation of DurableContextMiddleware.
- Unifies automatic and manual compaction factory paths to select the built-in template when unconfigured, while preserving explicit summary_prompt precedence.
- Adds comprehensive sync/async unit tests covering built-in and custom templates.

Co-authored-by: Totoro <Totoro-qaq@users.noreply.github.com>
Co-authored-by: Totoro <msy626836554@gmail.com>
2026-11-11 19:36:32 +08:00
ZephyrWang
d9b2cb4bcb
fix(memory): require facts in replacement imports (#8131)
* fix(memory): require facts in replacement imports

* docs(memory): document import facts requirement

* docs(changelog): link memory import fix
2026-11-11 17:35:17 +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