refactor(agents): move middleware package to app/agents/shared (slice 5c)

Relocate the entire new_chat/middleware/ package to the shared kernel as one
cohesive unit (it is live shared infrastructure: the multi-agent stack wraps
nearly every middleware via multi_agent_chat/middleware/main_agent/*, and
anonymous_agent consumes it too). Flip 69 live importers across both the
package-path and submodule-path forms.

Shims left for the frozen single-agent stack: a package __init__ re-export plus
submodule shims for permission, skills_backends, and scoped_model_fallback
(the three imported via submodule path by chat_deepagent/subagents).

Cycle break: importing shared.middleware previously reached back into
new_chat.tools at module load, which dragged in new_chat.__init__ ->
chat_deepagent -> the middleware shim -> half-initialized shared.middleware.
Made action_log's ToolDefinition import TYPE_CHECKING-only and
tool_call_repair's INVALID_TOOL_NAME import function-local. These tools-package
back-edges fully resolve in slice 6.

Asset note: skills_backends._default_builtin_root now walks to
app/agents/new_chat/skills/builtin (the skills/ tree migrates in slice 7).
This commit is contained in:
CREDO23 2026-06-04 13:00:41 +02:00
parent 6f488d9564
commit 227983a104
98 changed files with 1131 additions and 999 deletions

View file

@ -11,7 +11,7 @@ from langchain_core.messages import ToolMessage
from langchain_core.tools import tool
from app.agents.shared.feature_flags import AgentFeatureFlags
from app.agents.new_chat.middleware.action_log import ActionLogMiddleware
from app.agents.shared.middleware.action_log import ActionLogMiddleware
from app.agents.new_chat.tools.registry import ToolDefinition
@ -58,7 +58,7 @@ def _disabled_flags() -> AgentFeatureFlags:
def patch_get_flags():
def _patch(flags: AgentFeatureFlags):
return patch(
"app.agents.new_chat.middleware.action_log.get_flags",
"app.agents.shared.middleware.action_log.get_flags",
return_value=flags,
)
@ -360,7 +360,7 @@ class TestActionLogDispatch:
patch_get_flags(_enabled_flags()),
patch("app.db.shielded_async_session", side_effect=lambda: factory()),
patch(
"app.agents.new_chat.middleware.action_log.adispatch_custom_event",
"app.agents.shared.middleware.action_log.adispatch_custom_event",
dispatch_mock,
),
):
@ -395,7 +395,7 @@ class TestActionLogDispatch:
patch_get_flags(_enabled_flags()),
patch("app.db.shielded_async_session", side_effect=_exploding_session),
patch(
"app.agents.new_chat.middleware.action_log.adispatch_custom_event",
"app.agents.shared.middleware.action_log.adispatch_custom_event",
dispatch_mock,
),
):

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import pytest
from app.agents.shared.errors import BusyError
from app.agents.new_chat.middleware.busy_mutex import (
from app.agents.shared.middleware.busy_mutex import (
BusyMutexMiddleware,
end_turn,
get_cancel_event,

View file

@ -10,7 +10,7 @@ from langchain_core.messages import (
ToolMessage,
)
from app.agents.new_chat.middleware.compaction import (
from app.agents.shared.middleware.compaction import (
PROTECTED_SYSTEM_PREFIXES,
_is_protected_system_message,
_sanitize_message_content,
@ -72,7 +72,7 @@ class TestPartitionMessages:
# SurfSenseCompactionMiddleware without a real model, but the
# override path needs ``_lc_helper`` to delegate to. We mock
# that with a simple slicing partitioner equivalent to the real one.
from app.agents.new_chat.middleware.compaction import (
from app.agents.shared.middleware.compaction import (
SurfSenseCompactionMiddleware,
)

View file

@ -7,7 +7,7 @@ from typing import Any
import pytest
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
from app.agents.new_chat.middleware.context_editing import (
from app.agents.shared.middleware.context_editing import (
SpillToBackendEdit,
_build_spill_placeholder,
)

View file

@ -6,7 +6,7 @@ import pytest
from langchain_core.messages import AIMessage
from langchain_core.tools import StructuredTool
from app.agents.new_chat.middleware.dedup_tool_calls import (
from app.agents.shared.middleware.dedup_tool_calls import (
DedupHITLToolCallsMiddleware,
)
@ -137,7 +137,7 @@ def test_full_args_dedup_keeps_distinct_calls_sharing_a_field() -> None:
With :func:`dedup_key_full_args` only fully identical arg dicts dedup.
"""
from app.agents.new_chat.middleware.dedup_tool_calls import dedup_key_full_args
from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args
tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args)
mw = DedupHITLToolCallsMiddleware(agent_tools=[tool])
@ -179,7 +179,7 @@ def test_full_args_dedup_keeps_distinct_calls_sharing_a_field() -> None:
def test_full_args_dedup_drops_only_exact_duplicates() -> None:
from app.agents.new_chat.middleware.dedup_tool_calls import dedup_key_full_args
from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args
tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args)
mw = DedupHITLToolCallsMiddleware(agent_tools=[tool])

View file

@ -10,7 +10,7 @@ from __future__ import annotations
import pytest
from app.agents.new_chat.middleware.permission import PermissionMiddleware
from app.agents.shared.middleware.permission import PermissionMiddleware
from app.agents.shared.permissions import (
Rule,
Ruleset,

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import pytest
from langchain_core.messages import AIMessage
from app.agents.new_chat.middleware.doom_loop import DoomLoopMiddleware, _signature
from app.agents.shared.middleware.doom_loop import DoomLoopMiddleware, _signature
pytestmark = pytest.mark.unit

View file

@ -15,7 +15,7 @@ from unittest.mock import MagicMock
import pytest
from langchain_core.messages import HumanMessage, SystemMessage
from app.agents.new_chat.middleware.flatten_system import (
from app.agents.shared.middleware.flatten_system import (
FlattenSystemMessageMiddleware,
_flatten_text_blocks,
_flattened_request,

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import pytest
from langchain_core.messages import AIMessage, HumanMessage
from app.agents.new_chat.middleware.noop_injection import (
from app.agents.shared.middleware.noop_injection import (
NOOP_TOOL_NAME,
NoopInjectionMiddleware,
_last_ai_has_tool_calls,

View file

@ -8,7 +8,7 @@ from unittest.mock import MagicMock
import pytest
from langchain_core.messages import AIMessage, ToolMessage
from app.agents.new_chat.middleware.otel_span import (
from app.agents.shared.middleware.otel_span import (
OtelSpanMiddleware,
_annotate_model_response,
_annotate_tool_result,
@ -206,13 +206,13 @@ class TestMiddlewareIntegration:
duration_calls: list[dict[str, Any]] = []
token_calls: list[dict[str, Any]] = []
monkeypatch.setattr(
"app.agents.new_chat.middleware.otel_span.ot_metrics.record_model_call_duration",
"app.agents.shared.middleware.otel_span.ot_metrics.record_model_call_duration",
lambda duration_ms, **attrs: duration_calls.append(
{"duration_ms": duration_ms, **attrs}
),
)
monkeypatch.setattr(
"app.agents.new_chat.middleware.otel_span.ot_metrics.record_model_token_usage",
"app.agents.shared.middleware.otel_span.ot_metrics.record_model_token_usage",
lambda **attrs: token_calls.append(attrs),
)
@ -257,11 +257,11 @@ class TestMiddlewareIntegration:
errors: list[str] = []
monkeypatch.setattr(
"app.agents.new_chat.middleware.otel_span.ot_metrics.record_tool_call_error",
"app.agents.shared.middleware.otel_span.ot_metrics.record_tool_call_error",
lambda *, tool_name: errors.append(tool_name),
)
monkeypatch.setattr(
"app.agents.new_chat.middleware.otel_span.ot_metrics.record_tool_call_duration",
"app.agents.shared.middleware.otel_span.ot_metrics.record_tool_call_duration",
lambda *args, **kwargs: None,
)

View file

@ -6,7 +6,7 @@ import pytest
from langchain_core.messages import AIMessage, ToolMessage
from app.agents.shared.errors import CorrectedError, RejectedError
from app.agents.new_chat.middleware.permission import (
from app.agents.shared.middleware.permission import (
PermissionMiddleware,
_normalize_permission_decision,
)

View file

@ -4,7 +4,7 @@ from __future__ import annotations
import pytest
from app.agents.new_chat.middleware.retry_after import (
from app.agents.shared.middleware.retry_after import (
RetryAfterMiddleware,
_extract_retry_after_seconds,
_is_non_retryable,

View file

@ -21,8 +21,8 @@ from unittest.mock import AsyncMock
import pytest
from app.agents.shared.filesystem_selection import FilesystemMode
from app.agents.new_chat.middleware.filesystem import SurfSenseFilesystemMiddleware
from app.agents.new_chat.middleware.kb_postgres_backend import KBPostgresBackend
from app.agents.shared.middleware.filesystem import SurfSenseFilesystemMiddleware
from app.agents.shared.middleware.kb_postgres_backend import KBPostgresBackend
pytestmark = pytest.mark.unit

View file

@ -7,7 +7,7 @@ from pathlib import Path
import pytest
from app.agents.new_chat.middleware.skills_backends import (
from app.agents.shared.middleware.skills_backends import (
SKILLS_BUILTIN_PREFIX,
SKILLS_SPACE_PREFIX,
BuiltinSkillsBackend,

View file

@ -4,7 +4,7 @@ from __future__ import annotations
from langchain_core.tools import tool
from app.agents.new_chat.middleware.permission import PermissionMiddleware
from app.agents.shared.middleware.permission import PermissionMiddleware
from app.agents.new_chat.subagents import (
build_connector_negotiator_subagent,
build_explore_subagent,
@ -140,7 +140,7 @@ class TestExploreSubagent:
def test_includes_dedup_and_patch_middleware(self) -> None:
from deepagents.middleware.patch_tool_calls import PatchToolCallsMiddleware
from app.agents.new_chat.middleware import DedupHITLToolCallsMiddleware
from app.agents.shared.middleware import DedupHITLToolCallsMiddleware
spec = build_explore_subagent(tools=ALL_TOOLS)
types = {type(m) for m in spec["middleware"]} # type: ignore[index]

View file

@ -5,7 +5,7 @@ from __future__ import annotations
import pytest
from langchain_core.messages import AIMessage
from app.agents.new_chat.middleware.tool_call_repair import (
from app.agents.shared.middleware.tool_call_repair import (
ToolCallNameRepairMiddleware,
)
from app.agents.new_chat.tools.invalid_tool import INVALID_TOOL_NAME