diff --git a/loopx/capabilities/native_chat/model_settings.py b/loopx/capabilities/native_chat/model_settings.py index b8133ca7f4..7d0f27946e 100644 --- a/loopx/capabilities/native_chat/model_settings.py +++ b/loopx/capabilities/native_chat/model_settings.py @@ -40,7 +40,7 @@ def session_model_settings_supported(session: dict[str, Any]) -> bool: configuration owner. A model choice must never trigger their thread replay. Imports are local because the Session store also uses these normalizers. """ - from ...chat_coordination import PROJECT_CONTEXT_VERSION + from ...chat.coordination import PROJECT_CONTEXT_VERSION from ...chat_manager import is_manager_channel from ...chat_store import CHAT_SESSION_MODE_MANAGED diff --git a/loopx/chat_coordination.py b/loopx/chat/coordination.py similarity index 92% rename from loopx/chat_coordination.py rename to loopx/chat/coordination.py index 62df8f68c6..3bb992fb0b 100644 --- a/loopx/chat_coordination.py +++ b/loopx/chat/coordination.py @@ -40,8 +40,8 @@ def apply_context_handoff( return response if scope["kind"] == "unavailable": raise ValueError("context handoff requires a scoped conversation") - from .capabilities.manager_context.execution import handoff_response - from .chat_manager_context import manager_authorization_scope_is_current + from ..capabilities.manager_context.execution import handoff_response + from ..chat_manager_context import manager_authorization_scope_is_current expected_scope_id = context.get("authorization_scope_id") if scope["kind"] == "external_audience" and expected_scope_id is None: @@ -70,14 +70,14 @@ def apply_context_handoff( def prepare_turn_context(controller, adapter, session, turn_id, event_sink, *, scope, preserve_original_thread=False): """Prepare the same evidence/handoff path for each supported conversation.""" - from .chat_runtime import CodexAppServerAdapter, CodexChatAgentError - from .chat_manager import (MANAGER_AGENT_GOAL_ID, MANAGER_AGENT_OBJECTIVE, + from ..chat_runtime import CodexAppServerAdapter, CodexChatAgentError + from ..chat_manager import (MANAGER_AGENT_GOAL_ID, MANAGER_AGENT_OBJECTIVE, manager_workspace) - from .capabilities.manager_runtime import manager_runtime_session_fields + from ..capabilities.manager_runtime import manager_runtime_session_fields session_id = session["session_id"] runtime_root = getattr(controller, "coordination_runtime_root", controller.store.root.parent) - from .chat_manager_context import collect_manager_turn_context + from ..chat_manager_context import collect_manager_turn_context observation_only = getattr(adapter, "observation_only", False) is True event_sink("agent.phase", {"phase": "manager_context", "label": "正在读取当前 Goal 的工作与协作" if scope["kind"] == "owner_goal" else "正在读取授权范围内的 Goal 状态"}) context = collect_manager_turn_context( @@ -90,7 +90,7 @@ def prepare_turn_context(controller, adapter, session, turn_id, event_sink, *, s remote_evidence=not isinstance(adapter, CodexAppServerAdapter) and not observation_only, ) if scope.get("bound_steward") is True and isinstance(context.get("bound_steward"), dict): - from .capabilities.native_chat.external_conversations import ChatExternalConversations + from ..capabilities.native_chat.external_conversations import ChatExternalConversations context["bound_steward"]["executions"] = ChatExternalConversations(controller).commission_evidence(session) controller.store.append_event(session_id, turn_id, kind="manager.context", payload=context) if observation_only: @@ -146,12 +146,12 @@ def prepare_turn_context(controller, adapter, session, turn_id, event_sink, *, s ) with controller.lock: controller.adapters[session_id] = adapter - from .capabilities.manager_context import authority + from ..capabilities.manager_context import authority context["context_delegation"] = authority( runtime_root, controller.registry_path, session, controller.store.load_turn(session_id, turn_id) or {}, ) - from .capabilities.manager_context.execution import catalog + from ..capabilities.manager_context.execution import catalog execution_catalog = catalog( runtime_root, controller.registry_path, session, controller.store.load_turn(session_id, turn_id) or {}, @@ -159,8 +159,8 @@ def prepare_turn_context(controller, adapter, session, turn_id, event_sink, *, s if execution_catalog["bindings"] or not execution_catalog["available"]: context["context_execution"] = execution_catalog if isinstance(adapter, CodexAppServerAdapter): - from .capabilities.manager_context.inspection import ManagerInspection, manager_index - from .chat_manager_context import manager_authorization_scope_is_current + from ..capabilities.manager_context.inspection import ManagerInspection, manager_index + from ..chat_manager_context import manager_authorization_scope_is_current expected_scope_id = context.get("authorization_scope_id") def scope_valid() -> bool: if scope["kind"] == "owner_portfolio": @@ -199,7 +199,7 @@ def scope_valid() -> bool: adapter.session.read_tool_handler = inspection.read context["evidence_sources"] = inspection.sources() context = manager_index(context) - from .capabilities.native_chat.external_conversations import ChatExternalConversations + from ..capabilities.native_chat.external_conversations import ChatExternalConversations recovery = ChatExternalConversations(controller).recovery_context( session, controller.store.load_turn(session_id, turn_id) or {}) if scope.get("bound_steward") else None if recovery is not None: diff --git a/loopx/chat_endpoint_catalog.py b/loopx/chat/endpoint_catalog.py similarity index 96% rename from loopx/chat_endpoint_catalog.py rename to loopx/chat/endpoint_catalog.py index ef52c1fd03..bc0f13d975 100644 --- a/loopx/chat_endpoint_catalog.py +++ b/loopx/chat/endpoint_catalog.py @@ -17,13 +17,13 @@ from pathlib import Path from typing import Any -from .chat_dsh import STEWARD_SEGMENT_ENV -from .control_plane.operator_provider import operator_provider_environ -from .control_plane.turn_driver.host_binding import ( +from ..chat_dsh import STEWARD_SEGMENT_ENV +from ..control_plane.operator_provider import operator_provider_environ +from ..control_plane.turn_driver.host_binding import ( MANAGED_TURN_HOST, managed_executor_binding, ) -from .kiro_cli_goal_mode import ( +from ..kiro_cli_goal_mode import ( KIRO_CLI_CHAT_ADAPTER_KIND, KIRO_CLI_CHAT_AGENT_ID, KIRO_CLI_CHAT_DISPLAY_NAME, @@ -88,7 +88,7 @@ def _environment_readiness(variable: str) -> dict[str, Any]: def _antigravity_readiness() -> dict[str, Any]: """Live local probe for Antigravity's LanguageServerService.""" - from .capabilities.native_chat.antigravity_adapter import probe_antigravity_endpoint + from ..capabilities.native_chat.antigravity_adapter import probe_antigravity_endpoint available, executable, detail = probe_antigravity_endpoint() if available: diff --git a/loopx/chat_runtime.py b/loopx/chat_runtime.py index bc3db995f4..cec57507e3 100644 --- a/loopx/chat_runtime.py +++ b/loopx/chat_runtime.py @@ -20,7 +20,7 @@ manager_answer_readback, manager_session_model_allocation, ) -from .chat_coordination import PROJECT_COORDINATION_GUIDANCE, PROJECT_CONTEXT_VERSION, apply_context_handoff +from .chat.coordination import PROJECT_COORDINATION_GUIDANCE, PROJECT_CONTEXT_VERSION, apply_context_handoff from .capabilities.native_chat import codex_context, project_context as project_context_policy from .capabilities.native_chat.codex_adapter import CodexAppServerAdapter, start_attachment_turn from .capabilities.native_chat.machine_defaults import codex_profile_home, load_codex_chat_defaults @@ -46,7 +46,7 @@ validate_goal_chat, ) from .chat_dsh import DshChatAdapter -from .chat_endpoint_catalog import builtin_chat_endpoints +from .chat.endpoint_catalog import builtin_chat_endpoints from .chat_endpoints import AgentEndpointRegistry from .control_plane.turn_driver.host_binding import MANAGED_TURN_HOST from .control_plane.turn_driver.execution_profile import managed_execution_profile @@ -1744,7 +1744,7 @@ def event_sink(kind: str, payload: dict[str, Any]) -> None: if session.get("project_context") is not None: self.project_contexts.session_context(session) project_context_policy.validate_project_executor_scope(str(session["agent_id"]), session["project_context"], self.capabilities) - from .chat_coordination import prepare_turn_context + from .chat.coordination import prepare_turn_context current_turn = self.store.load_turn(session_id, turn_id) or {} if current_turn.get("origin") == "peer" or "collaboration_request" in current_turn: from .capabilities.native_chat.conversation_bindings import OriginalConversationRequests diff --git a/tests/architecture/top_level_module_budget.json b/tests/architecture/top_level_module_budget.json index ac1a498748..342ba84390 100644 --- a/tests/architecture/top_level_module_budget.json +++ b/tests/architecture/top_level_module_budget.json @@ -1,6 +1,6 @@ { "schema": "loopx_top_level_module_budget_v0", "baseline_commit": "a4483aaece5bc74ec3f969fd067229487022401b", - "max_top_level_modules": 130, + "max_top_level_modules": 128, "allowlist": ["__init__.py", "entrypoint.py", "cli.py"] } diff --git a/tests/control_plane/test_peer_source_store.py b/tests/control_plane/test_peer_source_store.py index 11582f8a26..f9571130f8 100644 --- a/tests/control_plane/test_peer_source_store.py +++ b/tests/control_plane/test_peer_source_store.py @@ -13,7 +13,7 @@ from canonical_authority_fixture import isolate_sqlite_runtime, promoted_create_fixture from loopx.capabilities.manager_context import POLICY_SCHEMA, _hash, _root, _write, register_ingress -from loopx.chat_coordination import apply_context_handoff +from loopx.chat.coordination import apply_context_handoff from loopx.chat_store import ChatSessionStore from loopx.control_plane.collaboration.peers import read_inbox, request from loopx.capabilities.manager_context.roundtrip import ( diff --git a/tests/test_chat_agent_model_http.py b/tests/test_chat_agent_model_http.py index b92ffe8da7..fe28206b79 100644 --- a/tests/test_chat_agent_model_http.py +++ b/tests/test_chat_agent_model_http.py @@ -30,7 +30,7 @@ def current_session_settings(tmp_path, monkeypatch): session = store.create_session(goal_id="fixture", agent_id="codex", adapter_kind="codex_app_server", upstream_thread_id="original-thread", agent_model_id="first", agent_reasoning_effort="low", requested_agent_model_id="first", requested_agent_reasoning_effort="low", upstream_mode="chat") - from loopx.chat_coordination import PROJECT_CONTEXT_VERSION + from loopx.chat.coordination import PROJECT_CONTEXT_VERSION session = store.update_session(session["session_id"], coordination_context_version=PROJECT_CONTEXT_VERSION) server = chat_server.ChatHTTPServer(("127.0.0.1", 0), chat_server.ChatRequestHandler) server.verbose = False diff --git a/tests/test_chat_conversation_bindings.py b/tests/test_chat_conversation_bindings.py index c43667324f..318d7f51b4 100644 --- a/tests/test_chat_conversation_bindings.py +++ b/tests/test_chat_conversation_bindings.py @@ -1434,7 +1434,7 @@ def test_read_only_bound_project_rejects_write_scoped_executor_before_session_cr project_grant="workspace_read", ) monkeypatch.setattr( - "loopx.chat_endpoint_catalog.shutil.which", + "loopx.chat.endpoint_catalog.shutil.which", lambda executable: executable if executable == "kiro-cli" else None, ) starts = [] @@ -1478,7 +1478,7 @@ def close_session(self): project_grant="workspace_read", ) monkeypatch.setattr( - "loopx.chat_endpoint_catalog.shutil.which", + "loopx.chat.endpoint_catalog.shutil.which", lambda executable: executable if executable in {"kiro-cli", runtime.codex_bin} else None, diff --git a/tests/test_chat_endpoint_readiness.py b/tests/test_chat_endpoint_readiness.py index 5e24d4d50f..a7fd83302b 100644 --- a/tests/test_chat_endpoint_readiness.py +++ b/tests/test_chat_endpoint_readiness.py @@ -5,7 +5,7 @@ import sys from pathlib import Path -from loopx.chat_endpoint_catalog import builtin_chat_endpoints +from loopx.chat.endpoint_catalog import builtin_chat_endpoints def _rows(monkeypatch, tmp_path: Path, **bins: str) -> dict[str, dict]: diff --git a/tests/test_chat_ordinary_project.py b/tests/test_chat_ordinary_project.py index 316102f0e8..6b8fe7dd2f 100644 --- a/tests/test_chat_ordinary_project.py +++ b/tests/test_chat_ordinary_project.py @@ -208,7 +208,7 @@ def test_writable_project_turns_use_project_skills_without_manager_work_selectio def test_workspace_only_rejects_other_executor_before_session_creation(ordinary, monkeypatch, grant): store, runtime, contexts, request, _, _, _ = ordinary contexts.filesystem_scope, contexts.workspace_grant = "workspace_only", grant - monkeypatch.setattr("loopx.chat_endpoint_catalog.shutil.which", + monkeypatch.setattr("loopx.chat.endpoint_catalog.shutil.which", lambda executable: executable if executable == "kiro-cli" else None) monkeypatch.setattr(runtime, "_start_adapter", lambda **_: pytest.fail("must reject before starting a host")) status, result = request("/api/chat/sessions", { @@ -228,7 +228,7 @@ def close_session(self): pass monkeypatch.setattr( - "loopx.chat_endpoint_catalog.shutil.which", + "loopx.chat.endpoint_catalog.shutil.which", lambda executable: executable if executable == "kiro-cli" else None, ) kiro_capability = next(row for row in runtime.capabilities() if row["agent_id"] == "kiro-cli") diff --git a/tests/test_chat_project_coordination.py b/tests/test_chat_project_coordination.py index 7033112f8a..e642c5e69d 100644 --- a/tests/test_chat_project_coordination.py +++ b/tests/test_chat_project_coordination.py @@ -9,7 +9,7 @@ import pytest -from loopx.chat_coordination import PROJECT_CONTEXT_VERSION +from loopx.chat.coordination import PROJECT_CONTEXT_VERSION from loopx.chat_agent import CodexChatAgentSession, CodexChatAgentError from loopx.chat_manager_context import collect_manager_turn_context from loopx.chat_runtime import ChatRuntimeController, CodexAppServerAdapter diff --git a/tests/test_chat_queue_preparation.py b/tests/test_chat_queue_preparation.py index 3b4e5f7ef1..2e5977ef15 100644 --- a/tests/test_chat_queue_preparation.py +++ b/tests/test_chat_queue_preparation.py @@ -13,7 +13,7 @@ import pytest from loopx.chat_agent import CodexChatAgentError -from loopx.chat_coordination import PROJECT_CONTEXT_VERSION +from loopx.chat.coordination import PROJECT_CONTEXT_VERSION from loopx.chat_runtime import ChatRuntimeController, CodexAppServerAdapter from loopx.chat_server import ChatHTTPServer, ChatRequestHandler from loopx.chat_store import ChatSessionStore @@ -555,7 +555,7 @@ def preparation(*args, **kwargs): @pytest.mark.parametrize("delay_stage", ["context", "provider"]) def test_http_stop_retains_fence_until_late_worker_acknowledges(tmp_path, monkeypatch, delay_stage): """A failed stop retains authority; retry settles only after the worker exits.""" - import loopx.chat_coordination as coordination + import loopx.chat.coordination as coordination from loopx.capabilities import manager_context store, sid, runtime = session_runtime(tmp_path) diff --git a/tests/test_native_steward_turn_host.py b/tests/test_native_steward_turn_host.py index c69b3c8f54..56c9e675af 100644 --- a/tests/test_native_steward_turn_host.py +++ b/tests/test_native_steward_turn_host.py @@ -585,7 +585,7 @@ def gated(adapter, message, sink): def test_inventory_replacement_during_context_read_never_forks_the_original_native_thread(steward, tmp_path, monkeypatch): sid, actor, _ = prepare(steward, "file", tmp_path, monkeypatch) store, runtime, _, _, binding, capture, _ = steward - import loopx.chat_coordination as coordination + import loopx.chat.coordination as coordination prepare_context = coordination.prepare_turn_context def changed(*args, **kwargs):