From 1ab4d610c2b3468f353584a4f68d0ac9c19aa0be Mon Sep 17 00:00:00 2001 From: "lusendong.6789" Date: Mon, 2 Nov 2026 17:00:10 +0800 Subject: [PATCH 1/3] refactor(chat): move internal APIs under chat namespace Signed-off-by: lusendong.6789 Co-authored-by: TRAE CLI --- .../usage_statistics_api.py} | 2 +- .../workspace_directory.py} | 12 ++++++------ loopx/chat_status_api.py | 2 +- loopx/presentation/configuration_api.py | 2 +- loopx/presentation/goal_source_routing.py | 2 +- tests/architecture/top_level_module_budget.json | 2 +- tests/test_chat_internal_module_layout.py | 6 ++++++ 7 files changed, 17 insertions(+), 11 deletions(-) rename loopx/{chat_usage_statistics_api.py => chat/usage_statistics_api.py} (97%) rename loopx/{chat_workspace_directory.py => chat/workspace_directory.py} (81%) diff --git a/loopx/chat_usage_statistics_api.py b/loopx/chat/usage_statistics_api.py similarity index 97% rename from loopx/chat_usage_statistics_api.py rename to loopx/chat/usage_statistics_api.py index 3ceb4d0fd0..242e47afec 100644 --- a/loopx/chat_usage_statistics_api.py +++ b/loopx/chat/usage_statistics_api.py @@ -4,7 +4,7 @@ import subprocess from typing import Any -from .usage_ping import UsageSettingsInputError, control +from ..usage_ping import UsageSettingsInputError, control CHAT_USAGE_STATISTICS_PATH = "/api/chat/usage-statistics" diff --git a/loopx/chat_workspace_directory.py b/loopx/chat/workspace_directory.py similarity index 81% rename from loopx/chat_workspace_directory.py rename to loopx/chat/workspace_directory.py index b6aa741f6d..43f9d6e973 100644 --- a/loopx/chat_workspace_directory.py +++ b/loopx/chat/workspace_directory.py @@ -6,11 +6,11 @@ import hashlib import json -from .chat import redact_local_paths -from .control_plane.goals.activation import goal_activation_state -from .control_plane.status.collection import registry_activation_revision -from .registry import registry_goals -from .control_plane.presentation.goal_metadata import goal_parent_fields +from ..chat import redact_local_paths +from ..control_plane.goals.activation import goal_activation_state +from ..control_plane.status.collection import registry_activation_revision +from ..registry import registry_goals +from ..control_plane.presentation.goal_metadata import goal_parent_fields def workspace_goal_directory( @@ -35,7 +35,7 @@ def workspace_goal_directory( ) links = registry.get("goal_source_links", []) if links: - from .control_plane.status.goal_source import linked_source_goals + from ..control_plane.status.goal_source import linked_source_goals goals.extend(goal for goal in linked_source_goals(registry) if not selected_goal_id or goal["id"] == selected_goal_id) revision = registry_activation_revision(registry) diff --git a/loopx/chat_status_api.py b/loopx/chat_status_api.py index 8bbde6fb9f..f0e978462c 100644 --- a/loopx/chat_status_api.py +++ b/loopx/chat_status_api.py @@ -11,7 +11,7 @@ from .chat import redact_local_paths from .chat_goal_subagent_api import goal_subagent_configuration_enabled -from .chat_workspace_directory import workspace_goal_directory +from .chat.workspace_directory import workspace_goal_directory from .codex_app_thread_activity import ( codex_delivery_expectations, codex_thread_observers, diff --git a/loopx/presentation/configuration_api.py b/loopx/presentation/configuration_api.py index 16fb61f1eb..d01c3b3eb3 100644 --- a/loopx/presentation/configuration_api.py +++ b/loopx/presentation/configuration_api.py @@ -11,7 +11,7 @@ from . import goal_amendment_api as amendment_api from . import request_control_api as request_api from . import goal_storage_api as storage_api -from .. import chat_usage_statistics_api as usage_api +from ..chat import usage_statistics_api as usage_api from .. import chat_goal_configuration_api as goal_api from . import goal_direction_api as direction_api from . import decision_context_api as context_api diff --git a/loopx/presentation/goal_source_routing.py b/loopx/presentation/goal_source_routing.py index 32defe8ed6..42d8a0329f 100644 --- a/loopx/presentation/goal_source_routing.py +++ b/loopx/presentation/goal_source_routing.py @@ -70,7 +70,7 @@ def _todo_detail(self): self._send_error("The original Task is unavailable; retry after reconnecting.", status=503) def _goal_source_links(self): - from ..chat_workspace_directory import workspace_goal_directory + from ..chat.workspace_directory import workspace_goal_directory from ..registry import load_registry from urllib.parse import parse_qs, urlparse import subprocess diff --git a/tests/architecture/top_level_module_budget.json b/tests/architecture/top_level_module_budget.json index 9c63a99dc2..114a6aca20 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": 141, + "max_top_level_modules": 139, "allowlist": ["__init__.py", "entrypoint.py", "cli.py"] } diff --git a/tests/test_chat_internal_module_layout.py b/tests/test_chat_internal_module_layout.py index e558b5d576..c3c1914cc0 100644 --- a/tests/test_chat_internal_module_layout.py +++ b/tests/test_chat_internal_module_layout.py @@ -10,6 +10,8 @@ from loopx.chat.monitor_actions import ChatMonitorActionMixin from loopx.chat.todo_actions import ChatTodoActionMixin from loopx.chat.todo_detail import TodoDetailRequestMixin +from loopx.chat.usage_statistics_api import UsageStatisticsRequestMixin +from loopx.chat.workspace_directory import workspace_goal_directory def test_chat_internal_helpers_load_below_the_public_chat_namespace() -> None: @@ -19,6 +21,8 @@ def test_chat_internal_helpers_load_below_the_public_chat_namespace() -> None: assert ChatMonitorActionMixin.__module__ == "loopx.chat.monitor_actions" assert ChatTodoActionMixin.__module__ == "loopx.chat.todo_actions" assert TodoDetailRequestMixin.__module__ == "loopx.chat.todo_detail" + assert UsageStatisticsRequestMixin.__module__ == "loopx.chat.usage_statistics_api" + assert workspace_goal_directory.__module__ == "loopx.chat.workspace_directory" def test_chat_internal_helpers_have_no_top_level_compatibility_shims() -> None: @@ -27,5 +31,7 @@ def test_chat_internal_helpers_have_no_top_level_compatibility_shims() -> None: "loopx.chat_monitor_actions", "loopx.chat_todo_actions", "loopx.chat_todo_detail", + "loopx.chat_usage_statistics_api", + "loopx.chat_workspace_directory", ): assert importlib.util.find_spec(old_module) is None From 53435870bf75887f09213ff3593727470d8d86ac Mon Sep 17 00:00:00 2001 From: Lusen Dong Date: Mon, 2 Nov 2026 19:11:04 +0800 Subject: [PATCH 2/3] refactor(chat): colocate configuration APIs (#7891) --- .../automation_cadence_api.py} | 4 +-- .../goal_configuration_api.py} | 28 +++++++++---------- .../machine_configuration_api.py} | 10 +++---- loopx/presentation/configuration_api.py | 6 ++-- .../architecture/top_level_module_budget.json | 2 +- .../test_capability_improvement.py | 2 +- .../test_explore_machine_defaults.py | 2 +- .../capabilities/test_explore_turn_context.py | 2 +- .../test_goal_capability_inspection.py | 2 +- ...est_machine_configuration_goal_defaults.py | 8 +++--- .../test_periodic_report_cadence.py | 2 +- .../test_pr_review_configuration.py | 2 +- tests/capabilities/test_progress_review.py | 2 +- .../test_reward_memory_agent_bindings.py | 6 ++-- .../test_subagent_model_configuration.py | 6 ++-- .../control_plane/test_todo_replan_cadence.py | 2 +- tests/test_chat_automation_cadence_api.py | 2 +- tests/test_chat_goal_configuration_api.py | 2 +- tests/test_chat_internal_module_layout.py | 15 ++++++++++ tests/test_chat_machine_configuration_api.py | 3 +- 20 files changed, 62 insertions(+), 46 deletions(-) rename loopx/{chat_automation_cadence_api.py => chat/automation_cadence_api.py} (99%) rename loopx/{chat_goal_configuration_api.py => chat/goal_configuration_api.py} (96%) rename loopx/{chat_machine_configuration_api.py => chat/machine_configuration_api.py} (97%) diff --git a/loopx/chat_automation_cadence_api.py b/loopx/chat/automation_cadence_api.py similarity index 99% rename from loopx/chat_automation_cadence_api.py rename to loopx/chat/automation_cadence_api.py index 062a3b352c..986681b078 100644 --- a/loopx/chat_automation_cadence_api.py +++ b/loopx/chat/automation_cadence_api.py @@ -7,12 +7,12 @@ from typing import Any from urllib.parse import parse_qs, urlparse -from .control_plane.effect_runtime import ( +from ..control_plane.effect_runtime import ( EffectRuntimeConflict, EffectRuntimeRejected, effect_runtime_result, ) -from .control_plane.quota.automation_cadence_readback import ( +from ..control_plane.quota.automation_cadence_readback import ( automation_cadence_readback, ) diff --git a/loopx/chat_goal_configuration_api.py b/loopx/chat/goal_configuration_api.py similarity index 96% rename from loopx/chat_goal_configuration_api.py rename to loopx/chat/goal_configuration_api.py index 92f98d4489..5bb47de3dc 100644 --- a/loopx/chat_goal_configuration_api.py +++ b/loopx/chat/goal_configuration_api.py @@ -5,26 +5,26 @@ from typing import Any, Protocol from urllib.parse import parse_qs, urlparse -from .capabilities.configuration_inspection import ( +from ..capabilities.configuration_inspection import ( GOAL_CONFIGURATION_INSPECTION_SCHEMA, inspect_machine_namespaces, project_goal_configuration as _public_goal_configuration, ) -from .capabilities.multi_subagent import ( +from ..capabilities.multi_subagent import ( apply_codex_subagent_capacity, plan_codex_subagent_capacity, public_codex_host_capacity, ) -from .configuration_transaction import ( +from ..configuration_transaction import ( build_configuration_update_plan, configuration_payload_revision, require_expected_configuration_plan_revision, ) -from .control_plane.goals.configure_goal_service import ( +from ..control_plane.goals.configure_goal_service import ( configure_goal_with_global_sync, read_goal_configuration_with_source_route, ) -from .orchestration import subagent_model_configuration_options +from ..orchestration import subagent_model_configuration_options CHAT_GOAL_CONFIGURATION_PATH = "/api/chat/goal-configuration" CHAT_GOAL_CONFIGURATION_PREVIEW_PATH = f"{CHAT_GOAL_CONFIGURATION_PATH}/preview" @@ -145,7 +145,7 @@ def _change_quality_options(config: Mapping[str, Any]) -> dict[str, Any]: def _progress_review_options(config: Mapping[str, Any]) -> dict[str, Any]: - from .capabilities.progress_review.policy import ( + from ..capabilities.progress_review.policy import ( normalize_progress_review_contract_revision, normalize_progress_review_drift_threshold, normalize_progress_review_mode, @@ -248,8 +248,8 @@ def _goal_capability_options( if capability_id == "goal_capability_organization": return {"capability_improvement_configuration": config} if capability_id == "todo_replan_cadence": - from .capabilities.todo_replan_cadence.machine_defaults import normalize_replan_cadence_configuration - from .control_plane.work_items.stride_policy_codec import normalize_stride_strategy + from ..capabilities.todo_replan_cadence.machine_defaults import normalize_replan_cadence_configuration + from ..control_plane.work_items.stride_policy_codec import normalize_stride_strategy horizon = {"clear_execution_planning_horizon_goal": True} if "planning_horizon_goal" in config: if not isinstance(config["planning_horizon_goal"], bool): @@ -272,7 +272,7 @@ def _goal_capability_options( return {option: cadence["count"], **horizon, **({"execution_stride_strategy": cadence["stride_strategy"]} if "stride_strategy" in cadence else {})} if capability_id == "periodic_report": - from .capabilities.periodic_report.goal_configuration import normalize_configuration + from ..capabilities.periodic_report.goal_configuration import normalize_configuration # Validate the payload before the Session guard reads native sources. return {"periodic_report_configuration": normalize_configuration(config)} if capability_id == "reward_memory": @@ -314,7 +314,7 @@ def _goal_capability_options( if capability_id == "explore_harness": return _explore_harness_options(config) if capability_id == "pull_request_review": - from .capabilities.pr_review_queue.goal_configuration import normalize_configuration + from ..capabilities.pr_review_queue.goal_configuration import normalize_configuration return {"pull_request_review_configuration": normalize_configuration(config)} if capability_id == "change_quality_qualification": return _change_quality_options(config) @@ -445,7 +445,7 @@ def _goal_configuration_readback_matches( capability_id: str, actual: object, desired: object, applied: Mapping[str, Any] ) -> bool: if capability_id == "reward_memory" and isinstance(actual, Mapping) and isinstance(desired, Mapping): - from .capabilities.reward_memory.configuration import reward_memory_configuration_readback_matches + from ..capabilities.reward_memory.configuration import reward_memory_configuration_readback_matches return reward_memory_configuration_readback_matches(actual, desired, applied) return actual == desired @@ -472,8 +472,8 @@ def _goal_configuration_inspect(self) -> None: result, machine_namespaces=self._goal_configuration_machine_namespaces(), ) - from .agent_registry import load_goal_from_registry - from .capabilities.progress_review.context import external_progress_review_context + from ..agent_registry import load_goal_from_registry + from ..capabilities.progress_review.context import external_progress_review_context goal = load_goal_from_registry(self.server.registry_path, goal_ids[0].strip()) observation = external_progress_review_context(goal or {}, getattr(self.server, "runtime_root", None)) if observation is not None: @@ -681,7 +681,7 @@ def _goal_configuration_update(self, *, execute: bool) -> None: options = _goal_capability_options(capability_id, configuration) machine_namespaces = self._goal_configuration_machine_namespaces() from contextlib import nullcontext - from .capabilities.native_chat.steward_reports import session_report_configuration_guard + from ..capabilities.native_chat.steward_reports import session_report_configuration_guard controller = getattr(self.server, "runtime_controller", None) guard = (session_report_configuration_guard(controller, goal_id, configuration) if capability_id == "periodic_report" and controller is not None else nullcontext()) diff --git a/loopx/chat_machine_configuration_api.py b/loopx/chat/machine_configuration_api.py similarity index 97% rename from loopx/chat_machine_configuration_api.py rename to loopx/chat/machine_configuration_api.py index 7d51a5b407..2e00ba36b0 100644 --- a/loopx/chat_machine_configuration_api.py +++ b/loopx/chat/machine_configuration_api.py @@ -4,12 +4,12 @@ from pathlib import Path from typing import Any, Protocol, cast -from .capabilities.configuration_ui import build_capability_configuration_catalog -from .configuration_catalog import build_configuration_capability_descriptors -from .capabilities.machine_configuration.builtins import ( +from ..capabilities.configuration_ui import build_capability_configuration_catalog +from ..configuration_catalog import build_configuration_capability_descriptors +from ..capabilities.machine_configuration.builtins import ( build_builtin_machine_configuration_registry, ) -from .capabilities.machine_configuration.contract import ( +from ..capabilities.machine_configuration.contract import ( MACHINE_CONFIGURATION_SCHEMA, MachineConfigurationRegistry, machine_configuration_revision, @@ -17,7 +17,7 @@ normalize_machine_configuration, remove_machine_configuration_namespace, ) -from .capabilities.machine_configuration.store import ( +from ..capabilities.machine_configuration.store import ( configure_machine_configuration, inspect_machine_configuration, read_stored_machine_configuration, diff --git a/loopx/presentation/configuration_api.py b/loopx/presentation/configuration_api.py index d01c3b3eb3..7faaf63c6e 100644 --- a/loopx/presentation/configuration_api.py +++ b/loopx/presentation/configuration_api.py @@ -12,13 +12,13 @@ from . import request_control_api as request_api from . import goal_storage_api as storage_api from ..chat import usage_statistics_api as usage_api -from .. import chat_goal_configuration_api as goal_api +from ..chat import goal_configuration_api as goal_api from . import goal_direction_api as direction_api from . import decision_context_api as context_api from . import content_media_api as media_api -from .. import chat_machine_configuration_api as machine_api +from ..chat import machine_configuration_api as machine_api from .. import chat_operator_provider_api as operator_api -from .. import chat_automation_cadence_api as cadence_api +from ..chat import automation_cadence_api as cadence_api class ChatConfigurationRequestMixin( diff --git a/tests/architecture/top_level_module_budget.json b/tests/architecture/top_level_module_budget.json index 114a6aca20..2f596b7889 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": 139, + "max_top_level_modules": 136, "allowlist": ["__init__.py", "entrypoint.py", "cli.py"] } diff --git a/tests/capabilities/test_capability_improvement.py b/tests/capabilities/test_capability_improvement.py index b966494c26..8258f3d3f6 100644 --- a/tests/capabilities/test_capability_improvement.py +++ b/tests/capabilities/test_capability_improvement.py @@ -8,7 +8,7 @@ import pytest from loopx.capabilities.goal_capability_organization.goal_configuration import configuration_summary -from loopx.chat_goal_configuration_api import ( +from loopx.chat.goal_configuration_api import ( CHAT_GOAL_CONFIGURATION_PREVIEW_PATH, GoalConfigurationRequestMixin, ) from loopx.configure_goal import configure_goal diff --git a/tests/capabilities/test_explore_machine_defaults.py b/tests/capabilities/test_explore_machine_defaults.py index 479ce188e8..37d359b94c 100644 --- a/tests/capabilities/test_explore_machine_defaults.py +++ b/tests/capabilities/test_explore_machine_defaults.py @@ -24,7 +24,7 @@ from loopx.capabilities.configuration_inspection import inspect_machine_namespaces, project_goal_configuration from loopx.capabilities.machine_configuration.builtins import build_builtin_machine_configuration_registry from loopx.capabilities.machine_configuration.store import configure_machine_configuration -from loopx.chat_goal_configuration_api import _goal_capability_options +from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.configure_goal import configure_goal from loopx.control_plane.coordination.goal_storage_initialization import initialize_goal_storage_target from loopx.history import collect_history diff --git a/tests/capabilities/test_explore_turn_context.py b/tests/capabilities/test_explore_turn_context.py index 311f86be03..9dbd596072 100644 --- a/tests/capabilities/test_explore_turn_context.py +++ b/tests/capabilities/test_explore_turn_context.py @@ -18,7 +18,7 @@ explore_result_log_path, ) from loopx.configure_goal import configure_goal -from loopx.chat_goal_configuration_api import _goal_capability_options +from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.control_plane.effect_runtime import ( effect_runtime_result, EffectRuntimeRejected, diff --git a/tests/capabilities/test_goal_capability_inspection.py b/tests/capabilities/test_goal_capability_inspection.py index 1e5dd164b4..8123d711df 100644 --- a/tests/capabilities/test_goal_capability_inspection.py +++ b/tests/capabilities/test_goal_capability_inspection.py @@ -10,7 +10,7 @@ inspect_goal_capabilities, render_goal_capabilities, ) -from loopx.chat_goal_configuration_api import GoalConfigurationRequestMixin +from loopx.chat.goal_configuration_api import GoalConfigurationRequestMixin from loopx.cli import main diff --git a/tests/capabilities/test_machine_configuration_goal_defaults.py b/tests/capabilities/test_machine_configuration_goal_defaults.py index 2f940ae05d..795a31bf5a 100644 --- a/tests/capabilities/test_machine_configuration_goal_defaults.py +++ b/tests/capabilities/test_machine_configuration_goal_defaults.py @@ -249,7 +249,7 @@ def test_goal_overrides_win_and_clearing_restores_live_machine_defaults( def test_effective_turn_machine_default_migration_and_override_readback(tmp_path: Path) -> None: """An explicit unit switch preserves v0 storage until apply and is reversible.""" - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.capabilities.goal_inspection import inspect_goal_capabilities _repo, registry_path, runtime_root = _fixture(tmp_path) @@ -354,7 +354,7 @@ def test_product_default_and_namespace_removal_use_six_settled_turns(tmp_path, m def test_stride_version_inherits_and_rolls_back(tmp_path: Path) -> None: - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.capabilities.goal_inspection import inspect_goal_capabilities _repo, registry_path, runtime_root = _fixture(tmp_path) @@ -414,7 +414,7 @@ def test_stride_version_inherits_and_rolls_back(tmp_path: Path) -> None: def test_goal_stride_only_uses_live_review_interval(tmp_path: Path) -> None: - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.capabilities.goal_inspection import inspect_goal_capabilities _repo, registry_path, runtime_root = _fixture(tmp_path) @@ -509,7 +509,7 @@ def test_stride_configuration_rejects_unsupported_or_ambiguous_versions(schema_v def test_horizon_goal_defaults_on_with_explicit_overrides_and_clear(tmp_path): - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.capabilities.goal_inspection import inspect_goal_capabilities _repo, registry_path, runtime_root = _fixture(tmp_path) diff --git a/tests/capabilities/test_periodic_report_cadence.py b/tests/capabilities/test_periodic_report_cadence.py index 7fdb06cf9e..b7cf3fea0f 100644 --- a/tests/capabilities/test_periodic_report_cadence.py +++ b/tests/capabilities/test_periodic_report_cadence.py @@ -114,7 +114,7 @@ def test_timezone_conflict_is_not_silently_reinterpreted(): def test_configure_goal_persists_and_clears_schedule(tmp_path): import json from loopx.configure_goal import configure_goal - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.capabilities.periodic_report.machine_defaults import resolve_goal_periodic_report_subscription registry = tmp_path / "registry.json" registry.write_text(json.dumps({"goals": [{"id": "example", "repo": str(tmp_path), diff --git a/tests/capabilities/test_pr_review_configuration.py b/tests/capabilities/test_pr_review_configuration.py index 3f08fa6ad6..f5046933b6 100644 --- a/tests/capabilities/test_pr_review_configuration.py +++ b/tests/capabilities/test_pr_review_configuration.py @@ -8,7 +8,7 @@ from loopx.cli import main from loopx.configure_goal import configure_goal -from loopx.chat_goal_configuration_api import _goal_capability_options +from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.capabilities.pr_review_queue.goal_configuration import ( apply_change, configuration_summary, diff --git a/tests/capabilities/test_progress_review.py b/tests/capabilities/test_progress_review.py index 7f45e2ce97..119af83382 100644 --- a/tests/capabilities/test_progress_review.py +++ b/tests/capabilities/test_progress_review.py @@ -272,7 +272,7 @@ def test_catalog_registers_the_default_off_capability() -> None: import io # noqa: E402 from contextlib import redirect_stdout # noqa: E402 -from loopx.chat_goal_configuration_api import _goal_capability_options # noqa: E402 +from loopx.chat.goal_configuration_api import _goal_capability_options # noqa: E402 from loopx.cli import main as cli_main # noqa: E402 from loopx.configure_goal import configure_goal # noqa: E402 from loopx.configuration_catalog import build_goal_configuration_catalog # noqa: E402 diff --git a/tests/capabilities/test_reward_memory_agent_bindings.py b/tests/capabilities/test_reward_memory_agent_bindings.py index fa4dd27731..6c17d0fca7 100644 --- a/tests/capabilities/test_reward_memory_agent_bindings.py +++ b/tests/capabilities/test_reward_memory_agent_bindings.py @@ -188,7 +188,7 @@ def test_wrong_target_mixed_scope_and_failed_preflight_cannot_commit(independent def test_editor_api_preserves_target_and_rejects_ambiguous_off_requests(): - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options assert _goal_capability_options("reward_memory", {"agent_id": "writer", "enabled": False}) == { "reward_memory_agent_id": "writer", "clear_reward_memory_config": True} enabled = _goal_capability_options("reward_memory", {"agent_id": "writer", "enabled": True, "config_path": ".loopx/config/writer.json"}) @@ -259,7 +259,7 @@ def test_pending_settlement_cannot_follow_a_replacement_binding(independent): def test_editor_readback_accepts_verified_activation_and_keeps_partial_write_fact(independent): - from loopx.chat_goal_configuration_api import GoalConfigurationRequestMixin + from loopx.chat.goal_configuration_api import GoalConfigurationRequestMixin registry, _, _ = independent preview = enable_writer(registry, execute=False) desired = preview["feature_summary"]["reward_memory"] @@ -321,7 +321,7 @@ def test_app_stage_resolves_the_registered_file_when_projection_omits_private_pa def test_committed_binding_with_unavailable_readback_is_partial_not_rollback(independent): - from loopx.chat_goal_configuration_api import GoalConfigurationRequestMixin + from loopx.chat.goal_configuration_api import GoalConfigurationRequestMixin registry, _, _ = independent applied = enable_writer(registry) class Unavailable(GoalConfigurationRequestMixin): diff --git a/tests/control_plane/test_subagent_model_configuration.py b/tests/control_plane/test_subagent_model_configuration.py index efd05add29..69f6045f07 100644 --- a/tests/control_plane/test_subagent_model_configuration.py +++ b/tests/control_plane/test_subagent_model_configuration.py @@ -221,7 +221,7 @@ def test_unconfigured_shape_and_unrecognized_model_availability() -> None: def test_capability_editor_model_roundtrip_and_clear(registry: Path) -> None: - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options for config, expected in [ ( @@ -249,7 +249,7 @@ def test_capability_editor_model_roundtrip_and_clear(registry: Path) -> None: def test_capability_editor_requests_one_click_codex_capacity_alignment() -> None: - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options options = _goal_capability_options( "multi_subagent", @@ -265,7 +265,7 @@ def test_capability_editor_requests_one_click_codex_capacity_alignment() -> None {"model": "example", "reasoning_effort": True}, ]) def test_capability_model_rejects_non_string_inputs(config: dict) -> None: - from loopx.chat_goal_configuration_api import _goal_capability_options + from loopx.chat.goal_configuration_api import _goal_capability_options with pytest.raises(ValueError, match="must be strings"): _goal_capability_options("multi_subagent", {"enabled": False, **config}) diff --git a/tests/control_plane/test_todo_replan_cadence.py b/tests/control_plane/test_todo_replan_cadence.py index bec3e50323..6413a6f91b 100644 --- a/tests/control_plane/test_todo_replan_cadence.py +++ b/tests/control_plane/test_todo_replan_cadence.py @@ -5,7 +5,7 @@ import pytest -from loopx.chat_goal_configuration_api import _goal_capability_options +from loopx.chat.goal_configuration_api import _goal_capability_options from loopx.configure_goal import configure_goal from loopx.control_plane.goals.goal_frontier.outcome_continuity import ( acceptance_gaps_from_todo_completion_checkpoint, diff --git a/tests/test_chat_automation_cadence_api.py b/tests/test_chat_automation_cadence_api.py index 16f397b544..9d4ac2f0ff 100644 --- a/tests/test_chat_automation_cadence_api.py +++ b/tests/test_chat_automation_cadence_api.py @@ -11,7 +11,7 @@ import pytest -from loopx import chat_automation_cadence_api +from loopx.chat import automation_cadence_api as chat_automation_cadence_api from loopx.chat_server import ChatHTTPServer, ChatRequestHandler from loopx.control_plane.effect_runtime import ( EffectRuntimeConflict, diff --git a/tests/test_chat_goal_configuration_api.py b/tests/test_chat_goal_configuration_api.py index 5d3504a11a..3470a06b84 100644 --- a/tests/test_chat_goal_configuration_api.py +++ b/tests/test_chat_goal_configuration_api.py @@ -7,7 +7,7 @@ import pytest -from loopx.chat_goal_configuration_api import ( +from loopx.chat.goal_configuration_api import ( CHAT_GOAL_CONFIGURATION_APPLY_PATH, CHAT_GOAL_CONFIGURATION_PATH, CHAT_GOAL_CONFIGURATION_PREVIEW_PATH, diff --git a/tests/test_chat_internal_module_layout.py b/tests/test_chat_internal_module_layout.py index c3c1914cc0..6a4132ba82 100644 --- a/tests/test_chat_internal_module_layout.py +++ b/tests/test_chat_internal_module_layout.py @@ -6,7 +6,10 @@ from pathlib import Path import loopx.chat as public_chat +from loopx.chat.automation_cadence_api import AutomationCadenceRequestMixin from loopx.chat.event_cache import ChatEventCache +from loopx.chat.goal_configuration_api import GoalConfigurationRequestMixin +from loopx.chat.machine_configuration_api import MachineConfigurationRequestMixin from loopx.chat.monitor_actions import ChatMonitorActionMixin from loopx.chat.todo_actions import ChatTodoActionMixin from loopx.chat.todo_detail import TodoDetailRequestMixin @@ -17,7 +20,16 @@ def test_chat_internal_helpers_load_below_the_public_chat_namespace() -> None: namespace_root = Path(public_chat.__file__).with_suffix("").resolve() assert [Path(entry).resolve() for entry in public_chat.__path__] == [namespace_root] + assert ( + AutomationCadenceRequestMixin.__module__ + == "loopx.chat.automation_cadence_api" + ) assert ChatEventCache.__module__ == "loopx.chat.event_cache" + assert GoalConfigurationRequestMixin.__module__ == "loopx.chat.goal_configuration_api" + assert ( + MachineConfigurationRequestMixin.__module__ + == "loopx.chat.machine_configuration_api" + ) assert ChatMonitorActionMixin.__module__ == "loopx.chat.monitor_actions" assert ChatTodoActionMixin.__module__ == "loopx.chat.todo_actions" assert TodoDetailRequestMixin.__module__ == "loopx.chat.todo_detail" @@ -28,6 +40,9 @@ def test_chat_internal_helpers_load_below_the_public_chat_namespace() -> None: def test_chat_internal_helpers_have_no_top_level_compatibility_shims() -> None: for old_module in ( "loopx.chat_event_cache", + "loopx.chat_automation_cadence_api", + "loopx.chat_goal_configuration_api", + "loopx.chat_machine_configuration_api", "loopx.chat_monitor_actions", "loopx.chat_todo_actions", "loopx.chat_todo_detail", diff --git a/tests/test_chat_machine_configuration_api.py b/tests/test_chat_machine_configuration_api.py index f6948d870e..f8c673bb36 100644 --- a/tests/test_chat_machine_configuration_api.py +++ b/tests/test_chat_machine_configuration_api.py @@ -18,7 +18,7 @@ configure_machine_configuration, read_machine_configuration, ) -from loopx.chat_machine_configuration_api import ( +from loopx.chat.machine_configuration_api import ( CHAT_MACHINE_CONFIGURATION_APPLY_PATH, CHAT_MACHINE_CONFIGURATION_PATH, CHAT_MACHINE_CONFIGURATION_PREVIEW_PATH, @@ -604,6 +604,7 @@ def test_the_steward_executor_namespace_is_editable_and_read_back( "executor_reasoning_effort", ] assert capability["configuration_editor"]["fields"][1]["options"] == [ + "antigravity", "codex", "dsh", ] From 77ebc81b4970fb342fdbbe26efce4ce312ae77f5 Mon Sep 17 00:00:00 2001 From: Lusen Dong Date: Mon, 2 Nov 2026 20:22:17 +0800 Subject: [PATCH 3/3] refactor(chat): move server entry helpers (#7896) --- loopx/{chat_session_open.py => chat/session_open.py} | 2 +- loopx/{chat_ssh_source_api.py => chat/ssh_source_api.py} | 6 +++--- loopx/chat_server.py | 4 ++-- tests/architecture/top_level_module_budget.json | 2 +- tests/test_chat_internal_module_layout.py | 6 ++++++ tests/test_native_steward_turn_host.py | 2 +- tests/test_ssh_tunnel_source.py | 8 ++++---- 7 files changed, 18 insertions(+), 12 deletions(-) rename loopx/{chat_session_open.py => chat/session_open.py} (98%) rename loopx/{chat_ssh_source_api.py => chat/ssh_source_api.py} (94%) diff --git a/loopx/chat_session_open.py b/loopx/chat/session_open.py similarity index 98% rename from loopx/chat_session_open.py rename to loopx/chat/session_open.py index 21ebd12d63..e140d3fb4d 100644 --- a/loopx/chat_session_open.py +++ b/loopx/chat/session_open.py @@ -22,7 +22,7 @@ from pathlib import Path from typing import Any -from .chat_manager import open_manager_session +from ..chat_manager import open_manager_session # The executor a Goal-scoped session runs on when the caller makes no explicit # pick. The steward channel does not use this: it resolves its own default. diff --git a/loopx/chat_ssh_source_api.py b/loopx/chat/ssh_source_api.py similarity index 94% rename from loopx/chat_ssh_source_api.py rename to loopx/chat/ssh_source_api.py index 821fa13a7c..5c74b02300 100644 --- a/loopx/chat_ssh_source_api.py +++ b/loopx/chat/ssh_source_api.py @@ -5,11 +5,11 @@ from collections.abc import Callable from typing import Any -from .control_plane.goals.ssh_lifecycle_transport import ( +from ..control_plane.goals.ssh_lifecycle_transport import ( apply_ssh_goal_lifecycle, ) -from .control_plane.status.ssh_tunnel import ensure_ssh_source -from .status_server import is_loopback_host +from ..control_plane.status.ssh_tunnel import ensure_ssh_source +from ..status_server import is_loopback_host SSH_SOURCE_ENSURE_PATH = "/api/ssh-source/ensure" diff --git a/loopx/chat_server.py b/loopx/chat_server.py index bc9fa940c8..97ef446c53 100644 --- a/loopx/chat_server.py +++ b/loopx/chat_server.py @@ -53,8 +53,8 @@ MANAGER_AGENT_GOAL_ID, MANAGER_AGENT_OBJECTIVE, is_manager_channel, manager_capabilities_projection, manager_workspace, ) -from .chat_session_open import open_chat_session -from .chat_ssh_source_api import SshSourceRequestMixin +from .chat.session_open import open_chat_session +from .chat.ssh_source_api import SshSourceRequestMixin from .chat_store import ChatSessionStore, TERMINAL_TURN_STATES from .capabilities.native_chat.model_settings import normalize_agent_reasoning_effort, normalize_agent_model_id from .extensions.lark.private_conversation_api import PrivateConversationRequestMixin, PRIVATE_CONVERSATIONS_PATH diff --git a/tests/architecture/top_level_module_budget.json b/tests/architecture/top_level_module_budget.json index 2f596b7889..c5ee836e21 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": 136, + "max_top_level_modules": 134, "allowlist": ["__init__.py", "entrypoint.py", "cli.py"] } diff --git a/tests/test_chat_internal_module_layout.py b/tests/test_chat_internal_module_layout.py index 6a4132ba82..ef0478b0a9 100644 --- a/tests/test_chat_internal_module_layout.py +++ b/tests/test_chat_internal_module_layout.py @@ -11,6 +11,8 @@ from loopx.chat.goal_configuration_api import GoalConfigurationRequestMixin from loopx.chat.machine_configuration_api import MachineConfigurationRequestMixin from loopx.chat.monitor_actions import ChatMonitorActionMixin +from loopx.chat.session_open import open_chat_session +from loopx.chat.ssh_source_api import SshSourceRequestMixin from loopx.chat.todo_actions import ChatTodoActionMixin from loopx.chat.todo_detail import TodoDetailRequestMixin from loopx.chat.usage_statistics_api import UsageStatisticsRequestMixin @@ -31,6 +33,8 @@ def test_chat_internal_helpers_load_below_the_public_chat_namespace() -> None: == "loopx.chat.machine_configuration_api" ) assert ChatMonitorActionMixin.__module__ == "loopx.chat.monitor_actions" + assert open_chat_session.__module__ == "loopx.chat.session_open" + assert SshSourceRequestMixin.__module__ == "loopx.chat.ssh_source_api" assert ChatTodoActionMixin.__module__ == "loopx.chat.todo_actions" assert TodoDetailRequestMixin.__module__ == "loopx.chat.todo_detail" assert UsageStatisticsRequestMixin.__module__ == "loopx.chat.usage_statistics_api" @@ -44,6 +48,8 @@ def test_chat_internal_helpers_have_no_top_level_compatibility_shims() -> None: "loopx.chat_goal_configuration_api", "loopx.chat_machine_configuration_api", "loopx.chat_monitor_actions", + "loopx.chat_session_open", + "loopx.chat_ssh_source_api", "loopx.chat_todo_actions", "loopx.chat_todo_detail", "loopx.chat_usage_statistics_api", diff --git a/tests/test_native_steward_turn_host.py b/tests/test_native_steward_turn_host.py index 458036e1b7..c69b3c8f54 100644 --- a/tests/test_native_steward_turn_host.py +++ b/tests/test_native_steward_turn_host.py @@ -59,7 +59,7 @@ def managed_fixture(steward, tmp_path, monkeypatch, provider="file", *, validato def test_registered_agents_have_distinct_durable_sessions_without_adopting_legacy_history(steward, tmp_path, monkeypatch, provider): service, first, actor, _, _, capture = managed_fixture(steward, tmp_path, monkeypatch, provider) store, runtime, *_ = steward - from loopx.chat_session_open import open_chat_session + from loopx.chat.session_open import open_chat_session from loopx.agent_registry import load_goal_from_registry cli(runtime, "configure-goal", "--goal-id", actor["goal_id"], "--registered-agent", actor["agent_id"], "--registered-agent", "peer", "--execute") diff --git a/tests/test_ssh_tunnel_source.py b/tests/test_ssh_tunnel_source.py index f12307fc00..b2b764633e 100644 --- a/tests/test_ssh_tunnel_source.py +++ b/tests/test_ssh_tunnel_source.py @@ -11,7 +11,7 @@ import pytest -from loopx.chat_ssh_source_api import SshSourceRequestMixin +from loopx.chat.ssh_source_api import SshSourceRequestMixin from loopx.control_plane.goals.ssh_lifecycle_transport import ( apply_ssh_goal_lifecycle, ) @@ -135,7 +135,7 @@ def test_ssh_source_request_mixin_delegates_validated_loopback_request() -> None receipt = {"ok": True, "status_url": "http://127.0.0.1:8877/status.json"} with mock.patch( - "loopx.chat_ssh_source_api.ensure_ssh_source", return_value=receipt + "loopx.chat.ssh_source_api.ensure_ssh_source", return_value=receipt ) as ensure: handler._ssh_source_ensure() @@ -149,7 +149,7 @@ def test_ssh_source_request_mixin_delegates_validated_loopback_request() -> None def test_ssh_source_request_mixin_rejects_non_loopback_server() -> None: handler = _SshSourceHandler(host="0.0.0.0") - with mock.patch("loopx.chat_ssh_source_api.ensure_ssh_source") as ensure: + with mock.patch("loopx.chat.ssh_source_api.ensure_ssh_source") as ensure: handler._ssh_source_ensure() ensure.assert_not_called() @@ -337,7 +337,7 @@ def test_ssh_goal_lifecycle_handler_returns_verified_remote_receipt() -> None: "projection_verified": True, } with mock.patch( - "loopx.chat_ssh_source_api.apply_ssh_goal_lifecycle", + "loopx.chat.ssh_source_api.apply_ssh_goal_lifecycle", return_value=receipt, ) as apply: handler._ssh_goal_lifecycle()