deerflow2/backend/packages/harness/deerflow
Nan Gao dcc6f1e678
feat(loop-detection): defer warning injection (#2752)
* fix(loop-detection): defer warn injection to wrap_model_call

The warn branch in LoopDetectionMiddleware injected a HumanMessage
into state from after_model. The tools node had not yet produced
ToolMessage responses to the previous AIMessage(tool_calls=...), so
the new HumanMessage landed *between* the assistant's tool_calls and
their responses. OpenAI/Moonshot reject the next request with
"tool_call_ids did not have response messages" because their
validators require tool_calls to be followed immediately by tool
messages.

Detection now runs in after_model as before, but only enqueues the
warning into a per-thread list. Injection happens in wrap_model_call,
where every prior ToolMessage is already present in request.messages.
The warning is appended at the end as HumanMessage(name="loop_warning")
— pairing intact, AIMessage semantics untouched, no SystemMessage
issues for Anthropic.

Closes #2029, addresses #2255 #2293 #2304 #2511.

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

* fix(channels): remove loop warning display filter

* feat(loop-detection): scope pending warnings by run

* docs(loop-detection): update docs

* test(loop-detection): assert deferred warnings are queued

* fix(loop-detection): cap transient warning state

* docs: update docs

* add async awrap_model_call test coverage

* docs(loop-detection): document transient warnings

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 14:36:07 +08:00
..
agents feat(loop-detection): defer warning injection (#2752) 2026-05-21 14:36:07 +08:00
community feat(sandbox) Adds download file interface in Sandbox (#3038) 2026-05-20 10:16:31 +08:00
config Fix env resolution in MCP config lists (#2556) 2026-05-21 07:27:00 +08:00
guardrails feat(guardrails): add pre-tool-call authorization middleware with pluggable providers (#1240) 2026-03-23 18:07:33 +08:00
mcp fix(harness): wrap async-only config tools for sync client execution (#2878) 2026-05-11 22:14:13 +08:00
models feat: static system prompt with DynamicContextMiddleware for prefix-cache optimization (#2801) 2026-05-09 09:27:02 +08:00
persistence fix(harness)!: hydrate runs from RunStore and persist interrupted status (#2932) 2026-05-18 22:25:02 +08:00
reflection refactor: split backend into harness (deerflow.*) and app (app.*) (#1131) 2026-03-14 22:55:52 +08:00
runtime fix(runtime): make RunManager.cancel() idempotent for already-interrupted runs (#3055) (#3058) 2026-05-20 16:37:36 +08:00
sandbox feat(sandbox) Adds download file interface in Sandbox (#3038) 2026-05-20 10:16:31 +08:00
skills fix(skills): make security scanner JSON parsing robust for LLM output variations (#2987) 2026-05-17 08:59:42 +08:00
subagents fix(subagents): make subagent timeout terminal state atomic (#2583) 2026-05-18 22:19:32 +08:00
tools fix(task-tool): cancel and schedule deferred cleanup on polling safety timeout (#3097) 2026-05-21 07:47:19 +08:00
tracing feat(tracing): add optional Langfuse support (#1717) 2026-04-02 13:06:10 +08:00
uploads fix(uploads): add Windows support for safe symlink-protected uploads (#2794) 2026-05-09 18:21:54 +08:00
utils fix(gateway): return ISO 8601 timestamps from threads endpoints (#2599) 2026-05-02 15:16:16 +08:00
__init__.py refactor: split backend into harness (deerflow.*) and app (app.*) (#1131) 2026-03-14 22:55:52 +08:00
client.py feat: refine token usage display modes (#2329) 2026-05-04 09:56:16 +08:00