Files
2026-07-13 13:12:33 +08:00

2079 lines
69 KiB
Python

from __future__ import annotations
import asyncio
import builtins
from pathlib import Path
from threading import Event
from types import SimpleNamespace
from typing import Any
import pytest
from opensquilla.engine.types import AgentConfig, DoneEvent
from opensquilla.gateway.boot import (
_configured_agent_ids,
_gateway_home,
_register_dream_crons,
_task_runtime_turn_hard_deadline_s,
_warn_workspace_state_mismatch,
build_flush_service,
build_services,
build_task_runtime_run_kwargs,
dispatch_task_runtime_turn,
emit_skill_filter_banner,
validate_squilla_router_runtime,
)
from opensquilla.gateway.config import (
AgentEntryConfig,
GatewayConfig,
effective_agent_stream_idle_timeout_seconds,
effective_webui_stream_idle_grace_seconds,
)
from opensquilla.gateway.diagnostics import DiagnosticsState
from opensquilla.gateway.routing import build_cli_route_envelope, build_cron_route_envelope
from opensquilla.onboarding.mutations import upsert_channel
from opensquilla.provider import Message
from opensquilla.scheduler.types import CronJob, JobStatus
from opensquilla.session.compaction import CompactionConfig
from opensquilla.session.manager import SessionManager
from opensquilla.session.models import SessionIntent
from opensquilla.session.storage import SessionStorage
from opensquilla.tools.registry import ToolRegistry
from opensquilla.tools.types import CallerKind, ToolContext, ToolSpec
def test_gateway_boot_bridges_compaction_notifications_to_session_stream() -> None:
source = Path("src/opensquilla/gateway/boot.py").read_text(encoding="utf-8")
assert "add_compaction_listener" in source
assert '"session.event.compaction"' in source
assert "_compaction_listener_remove" in source
def test_task_runtime_default_hard_deadline_is_unbounded() -> None:
config = GatewayConfig()
deadline = _task_runtime_turn_hard_deadline_s(config)
assert deadline is None
def test_task_runtime_hard_deadline_honors_explicit_config() -> None:
config = GatewayConfig()
config.task_runtime.turn_hard_deadline_s = 12.5
assert _task_runtime_turn_hard_deadline_s(config) == 12.5
def test_gateway_server_close_releases_pid_lock_when_shutdown_step_fails() -> None:
from opensquilla.gateway import boot
released: list[str] = []
class FakePidLock:
def release(self) -> None:
released.append("released")
class FailingChannelManager:
async def stop_all(self) -> None:
raise RuntimeError("channel stop failed")
server = boot.GatewayServer(
app=SimpleNamespace(),
config=GatewayConfig(),
_channel_manager=FailingChannelManager(),
_pid_lock=FakePidLock(),
)
async def run_case() -> None:
with pytest.raises(RuntimeError, match="channel stop failed"):
await server.close()
assert released == ["released"]
assert server._pid_lock is None
asyncio.run(run_case())
def test_start_gateway_server_releases_pid_lock_when_build_services_fails(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
from opensquilla.gateway import boot
events: list[str] = []
async def fail_build_services(**_kwargs: Any) -> Any:
events.append("build_services")
raise RuntimeError("service construction failed")
monkeypatch.setattr(boot, "build_services", fail_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire",
lambda self: events.append("acquire"),
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.release",
lambda self: events.append("release"),
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
)
async def run_case() -> None:
with pytest.raises(RuntimeError, match="service construction failed"):
await boot.start_gateway_server(config=config, run=False)
assert events == ["acquire", "build_services", "release"]
asyncio.run(run_case())
def test_start_gateway_server_logs_install_telemetry_without_structlog_event_collision(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
from opensquilla.gateway import boot
debug_logs: list[tuple[str, dict[str, Any]]] = []
class FakeLog:
def debug(self, event: str, **kwargs: Any) -> None:
debug_logs.append((event, kwargs))
def info(self, _event: str, **_kwargs: Any) -> None:
return None
def warning(self, _event: str, **_kwargs: Any) -> None:
return None
class FakeTurnRunner:
def __init__(self, **_kwargs: Any) -> None:
return None
def set_session_lock_provider(self, _provider: Any) -> None:
return None
async def fake_build_services(**kwargs: Any) -> Any:
config = kwargs["config"]
async def close() -> None:
return None
return SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=object(),
skill_loader=object(),
usage_tracker=object(),
config=config,
memory_sync_managers={},
model_catalog=None,
memory_retrievers={},
turn_capture_services={},
flush_service=None,
cron_scheduler=None,
task_runtime=None,
agent_registry=None,
memory_managers={},
memory_stores={},
_turn_runner_ref=[],
close=close,
)
def fake_collect_install_telemetry(*, config: GatewayConfig) -> Any:
return SimpleNamespace(
skipped_reason=None,
event="install",
sent=True,
uploaded=False,
endpoint_configured=True,
)
monkeypatch.setattr(boot, "log", FakeLog())
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
monkeypatch.setattr(boot, "build_services", fake_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(
"opensquilla.observability.install_telemetry.collect_install_telemetry",
fake_collect_install_telemetry,
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire",
lambda self: None,
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.release",
lambda self: None,
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
)
async def run_case() -> None:
server = await boot.start_gateway_server(config=config, run=False)
try:
telemetry_logs = [
kwargs for event, kwargs in debug_logs if event == "gateway.install_telemetry"
]
assert len(telemetry_logs) == 1
assert telemetry_logs[0]["telemetry_event"] == "install"
assert "event" not in telemetry_logs[0]
assert "gateway.install_telemetry_skipped" not in {
event for event, _kwargs in debug_logs
}
finally:
await server.close()
asyncio.run(run_case())
def test_build_task_runtime_run_kwargs_forwards_fresh_user_session() -> None:
run = SimpleNamespace(
agent_id="main",
attachments=[],
input_provenance=None,
run_kind="session_turn",
no_memory_capture=False,
fresh_user_session=True,
ingress_pipeline_steps=(),
semantic_message=None,
)
kwargs = build_task_runtime_run_kwargs(run, tool_context=object(), model="model")
assert kwargs["fresh_user_session"] is True
def test_build_task_runtime_run_kwargs_forwards_bound_user_message_id() -> None:
# The persisted user message id must reach TurnRunner.run so history binds to
# the exact prompt this turn answers (queued sends must not duplicate or leak).
run = SimpleNamespace(
agent_id="main",
attachments=[],
input_provenance=None,
run_kind="session_turn",
no_memory_capture=False,
fresh_user_session=False,
ingress_pipeline_steps=(),
semantic_message=None,
persisted_user_message_id="msg-123",
)
kwargs = build_task_runtime_run_kwargs(run, tool_context=object(), model="model")
assert kwargs["bound_user_message_id"] == "msg-123"
def test_build_task_runtime_run_kwargs_omits_bound_id_when_absent() -> None:
# Legacy callers/mocks without the field keep the positional-trim fallback:
# the kwarg must be omitted, not forwarded as None.
run = SimpleNamespace(
agent_id="main",
attachments=[],
input_provenance=None,
run_kind="session_turn",
no_memory_capture=False,
fresh_user_session=False,
ingress_pipeline_steps=(),
semantic_message=None,
persisted_user_message_id=None,
)
kwargs = build_task_runtime_run_kwargs(run, tool_context=object(), model="model")
assert "bound_user_message_id" not in kwargs
def test_gateway_stream_timeout_config_defaults_remain_serializable() -> None:
config = GatewayConfig()
assert config.agent_stream_idle_timeout_seconds == 600.0
assert config.webui_stream_idle_grace_seconds == 630.0
assert config.webui_stream_idle_grace_seconds > config.agent_stream_idle_timeout_seconds
# A fresh install without an OpenRouter credential cannot run the static-B5
# ensemble, so the effective hang-detection budgets stay at the defaults.
assert effective_agent_stream_idle_timeout_seconds(config) == 600.0
assert effective_webui_stream_idle_grace_seconds(config) == 630.0
def test_gateway_stream_timeout_defaults_stay_single_router_when_openrouter_key_present(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENROUTER_API_KEY", "sk-or-synthetic")
config = GatewayConfig()
assert effective_agent_stream_idle_timeout_seconds(config) == 600.0
assert effective_webui_stream_idle_grace_seconds(config) == 630.0
def test_gateway_stream_timeouts_keep_legacy_effective_values_when_static_disabled() -> None:
config = GatewayConfig(
llm_ensemble={
"enabled": True,
"selection_mode": "router_dynamic",
}
)
assert config.agent_stream_idle_timeout_seconds == 600.0
assert config.webui_stream_idle_grace_seconds == 630.0
assert effective_agent_stream_idle_timeout_seconds(config) == 600.0
assert effective_webui_stream_idle_grace_seconds(config) == 630.0
def test_static_openrouter_b5_effective_stream_timeouts_extend_webui_budget() -> None:
config = GatewayConfig(
llm={"provider": "openrouter", "api_key": "sk-or-synthetic"},
llm_ensemble={
"enabled": True,
"selection_mode": "static_openrouter_b5",
},
)
assert config.agent_stream_idle_timeout_seconds == 600.0
assert config.webui_stream_idle_grace_seconds == 630.0
assert effective_agent_stream_idle_timeout_seconds(config) == 1200.0
assert effective_webui_stream_idle_grace_seconds(config) == 1260.0
def test_static_openrouter_b5_keyless_install_keeps_default_stream_timeouts(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.delenv("OPENROUTER_API_KEY", raising=False)
config = GatewayConfig(
llm={"provider": "groq", "api_key": "sk-groq-synthetic"},
llm_ensemble={
"enabled": True,
"selection_mode": "static_openrouter_b5",
},
)
assert effective_agent_stream_idle_timeout_seconds(config) == 600.0
assert effective_webui_stream_idle_grace_seconds(config) == 630.0
def test_static_openrouter_b5_webui_grace_stays_above_custom_stream_idle() -> None:
config = GatewayConfig(
agent_stream_idle_timeout_seconds=2000.0,
webui_stream_idle_grace_seconds=630.0,
llm={"provider": "openrouter", "api_key": "sk-or-synthetic"},
llm_ensemble={
"enabled": True,
"selection_mode": "static_openrouter_b5",
},
)
assert effective_agent_stream_idle_timeout_seconds(config) == 2000.0
assert effective_webui_stream_idle_grace_seconds(config) == 2060.0
def test_compaction_time_budget_defaults_allow_long_chain_work() -> None:
gateway_config = GatewayConfig()
agent_config = AgentConfig()
compaction_config = CompactionConfig()
assert gateway_config.memory.flush_timeout_seconds == 15.0
assert gateway_config.memory.flush_background_timeout_seconds == 120.0
assert gateway_config.compaction.timeout_seconds == 90.0
assert agent_config.flush_timeout_seconds == 15.0
assert agent_config.flush_background_timeout_seconds == 120.0
assert compaction_config.timeout_seconds == 90.0
def test_gateway_home_uses_configured_state_parent(tmp_path: Path) -> None:
config = GatewayConfig(
state_dir=str(tmp_path / "instance" / "state"),
workspace_dir=str(tmp_path / "instance" / "workspace"),
)
assert _gateway_home(config) == tmp_path / "instance"
def test_gateway_home_falls_back_to_config_path_parent(tmp_path: Path) -> None:
config = GatewayConfig(
state_dir=None,
config_path=str(tmp_path / "service" / "config.toml"),
workspace_dir=str(tmp_path / "service" / "workspace"),
)
assert _gateway_home(config) == tmp_path / "service"
@pytest.mark.asyncio
async def test_boot_sandbox_setup_runs_by_default(
monkeypatch: pytest.MonkeyPatch,
) -> None:
from opensquilla.gateway import boot
from opensquilla.sandbox.setup_state import SandboxSetupState, SetupResult
calls: list[str] = []
config = GatewayConfig(
sandbox={
"run_mode": "trusted",
"sandbox": True,
"security_grading": True,
"network_default": "proxy_allowlist",
},
)
async def fake_ensure(setup_config: GatewayConfig) -> SetupResult:
calls.append("setup")
assert setup_config is config
return SetupResult(
state=SandboxSetupState.READY,
platform="auto",
message="Sandbox setup is ready.",
requires_admin=False,
detail="proxy_allowlist=ready",
)
monkeypatch.setattr(
"opensquilla.sandbox.setup_runtime.ensure_sandbox_setup_auto",
fake_ensure,
)
result = await boot._ensure_sandbox_setup_on_boot(config)
assert result is not None
assert result.state is SandboxSetupState.READY
assert calls == ["setup"]
@pytest.mark.asyncio
async def test_boot_sandbox_setup_can_be_disabled(
monkeypatch: pytest.MonkeyPatch,
) -> None:
from opensquilla.gateway import boot
async def fail_if_called(config: GatewayConfig) -> object:
raise AssertionError("sandbox.auto_setup=false must not trigger setup")
monkeypatch.setattr(
"opensquilla.sandbox.setup_runtime.ensure_sandbox_setup_auto",
fail_if_called,
)
result = await boot._ensure_sandbox_setup_on_boot(
GatewayConfig(
sandbox={
"auto_setup": False,
"run_mode": "trusted",
"sandbox": True,
"security_grading": True,
},
)
)
assert result is None
@pytest.mark.asyncio
async def test_boot_sandbox_setup_runs_for_full_host_access_by_default(
monkeypatch: pytest.MonkeyPatch,
) -> None:
from opensquilla.gateway import boot
from opensquilla.sandbox.setup_state import SandboxSetupState, SetupResult
calls: list[str] = []
config = GatewayConfig(
sandbox={
"run_mode": "full",
"sandbox": False,
"security_grading": False,
},
)
async def fake_ensure(setup_config: GatewayConfig) -> SetupResult:
calls.append("setup")
assert setup_config is config
return SetupResult(
state=SandboxSetupState.READY,
platform="auto",
message="Sandbox setup is ready.",
requires_admin=False,
)
monkeypatch.setattr(
"opensquilla.sandbox.setup_runtime.ensure_sandbox_setup_auto",
fake_ensure,
)
result = await boot._ensure_sandbox_setup_on_boot(config)
assert result is not None
assert result.state is SandboxSetupState.READY
assert calls == ["setup"]
@pytest.mark.asyncio
async def test_build_services_schedules_sandbox_setup_after_runtime(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
from opensquilla.gateway import boot
events: list[str] = []
scheduled: list[Any] = []
async def fake_setup(config: GatewayConfig) -> None:
events.append("setup")
def fake_configure_runtime(*args: Any, **kwargs: Any) -> Any:
events.append("runtime")
return SimpleNamespace(effective=SimpleNamespace(as_dict=lambda: {}))
def fake_create_background_task(coro: Any) -> Any:
scheduled.append(coro)
close = getattr(coro, "close", None)
if callable(close):
close()
return SimpleNamespace()
monkeypatch.setattr(boot, "_ensure_sandbox_setup_on_boot", fake_setup)
monkeypatch.setattr(boot, "create_background_task", fake_create_background_task)
monkeypatch.setattr("opensquilla.sandbox.integration.configure_runtime", fake_configure_runtime)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
mcp={"enabled": False},
memory={"flush_enabled": False},
sandbox={
"run_mode": "trusted",
"sandbox": True,
"security_grading": True,
"network_default": "proxy_allowlist",
},
)
services = await build_services(
config=config,
session_db_path=str(tmp_path / "sessions.sqlite"),
seed_agent_workspaces=False,
)
try:
assert events == ["runtime"]
assert len(scheduled) == 1
finally:
await services.close()
class _FakeDreamScheduler:
def __init__(self, jobs: list[CronJob] | None = None) -> None:
self.jobs = jobs or []
self.added: list[dict[str, Any]] = []
self.paused: list[str] = []
async def list_jobs(self) -> list[CronJob]:
return self.jobs
async def add_job(self, **kwargs: Any) -> None:
self.added.append(kwargs)
async def pause_job(self, job_id: str) -> None:
self.paused.append(job_id)
for job in self.jobs:
if job.id == job_id:
job.status = JobStatus.PAUSED
def test_build_turn_runner_from_services_wires_memory_services(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, Any] = {}
class FakeTurnRunner:
def __init__(self, **kwargs: Any) -> None:
captured.update(kwargs)
from opensquilla.gateway import boot
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
services = SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=object(),
skill_loader=object(),
usage_tracker=object(),
config=GatewayConfig(),
memory_sync_managers={"main": object()},
memory_retrievers={"main": object()},
turn_capture_services={"main": object()},
flush_service=object(),
model_catalog=object(),
)
runner = boot.build_turn_runner_from_services(services)
assert isinstance(runner, FakeTurnRunner)
assert captured["memory_sync_managers"] is services.memory_sync_managers
assert captured["memory_retrievers"] is services.memory_retrievers
assert captured["turn_capture_services"] is services.turn_capture_services
assert captured["session_flush_service"] is services.flush_service
assert captured["model_catalog"] is services.model_catalog
def test_build_turn_runner_from_services_wires_diagnostics_state(
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, Any] = {}
class FakeTurnRunner:
def __init__(self, **kwargs: Any) -> None:
captured.update(kwargs)
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
services = SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=object(),
skill_loader=object(),
usage_tracker=object(),
config=GatewayConfig(),
)
state = DiagnosticsState.from_config(GatewayConfig())
from opensquilla.gateway import boot
runner = boot.build_turn_runner_from_services(services, diagnostics_state=state)
assert isinstance(runner, FakeTurnRunner)
assert captured["diagnostics_state"] is state
@pytest.mark.asyncio
async def test_start_gateway_server_shares_diagnostics_state_between_app_and_turn_runner(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured_runner: dict[str, Any] = {}
class FakeTurnRunner:
def __init__(self, **kwargs: Any) -> None:
captured_runner.update(kwargs)
def set_session_lock_provider(self, provider: Any) -> None:
captured_runner["session_lock_provider"] = provider
async def fake_build_services(**kwargs: Any) -> Any:
config = kwargs["config"]
async def close() -> None:
return None
return SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=object(),
skill_loader=object(),
usage_tracker=object(),
config=config,
memory_sync_managers={},
model_catalog=None,
memory_retrievers={},
turn_capture_services={},
flush_service=None,
cron_scheduler=None,
task_runtime=None,
agent_registry=None,
memory_managers={},
memory_stores={},
_turn_runner_ref=[],
close=close,
)
from opensquilla.gateway import boot
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
monkeypatch.setattr(boot, "build_services", fake_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire",
lambda self: None,
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.release",
lambda self: None,
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
diagnostics_enabled=True,
)
server = await boot.start_gateway_server(config=config, run=False)
try:
state = server.app.state.diagnostics_state
assert isinstance(state, DiagnosticsState)
assert captured_runner["diagnostics_state"] is state
state.set_runtime(enabled=True, raw=True)
assert captured_runner["diagnostics_state"].raw_turn_call_enabled() is True
finally:
await server.close()
@pytest.mark.asyncio
async def test_start_gateway_server_creates_default_subscription_manager(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured_bridge: dict[str, Any] = {}
class FakeTurnRunner:
def __init__(self, **_kwargs: Any) -> None:
pass
def set_session_lock_provider(self, _provider: Any) -> None:
pass
class FakeEventBridge:
def __init__(self, *, subscription_manager: Any, connection_registry: Any) -> None:
captured_bridge["subscription_manager"] = subscription_manager
captured_bridge["connection_registry"] = connection_registry
async def emit(self, *_args: Any, **_kwargs: Any) -> None:
return None
async def fake_build_services(**kwargs: Any) -> Any:
config = kwargs["config"]
async def close() -> None:
return None
return SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=object(),
skill_loader=object(),
usage_tracker=object(),
config=config,
memory_sync_managers={},
model_catalog=None,
memory_retrievers={},
turn_capture_services={},
flush_service=None,
cron_scheduler=None,
task_runtime=None,
agent_registry=None,
memory_managers={},
memory_stores={},
_turn_runner_ref=[],
close=close,
)
from opensquilla.gateway import boot
from opensquilla.gateway.websocket import SubscriptionManager
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
monkeypatch.setattr("opensquilla.gateway.event_bridge.EventBridge", FakeEventBridge)
monkeypatch.setattr(boot, "build_services", fake_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire",
lambda self: None,
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.release",
lambda self: None,
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
)
server = await boot.start_gateway_server(config=config, run=False)
try:
assert isinstance(captured_bridge["subscription_manager"], SubscriptionManager)
finally:
await server.close()
@pytest.mark.asyncio
async def test_start_gateway_server_schedules_router_preload_after_channels(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
events: list[str] = []
class FakeTurnRunner:
def __init__(self, **_kwargs: Any) -> None:
pass
def set_session_lock_provider(self, _provider: Any) -> None:
pass
class FakeChannelManager:
async def start_all(self) -> dict[str, bool]:
events.append("channels.start_all")
return {"feishu": True}
def start_errors(self) -> dict[str, dict[str, str]]:
return {}
async def stop_all(self) -> None:
return None
class FakeServer:
def __init__(self, _config: Any) -> None:
self.should_exit = False
async def serve(self) -> None:
return None
async def fake_build_services(**kwargs: Any) -> Any:
config = kwargs["config"]
async def close() -> None:
return None
return SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=object(),
skill_loader=object(),
usage_tracker=object(),
config=config,
memory_sync_managers={},
model_catalog=None,
memory_retrievers={},
turn_capture_services={},
flush_service=None,
cron_scheduler=None,
task_runtime=None,
agent_registry=None,
memory_managers={},
memory_stores={},
_turn_runner_ref=[],
close=close,
)
def fake_create_background_task(coro: Any) -> Any:
code = getattr(coro, "cr_code", None)
name = getattr(code, "co_name", "")
if name == "preload_squilla_router_runtime":
events.append("router.preload.scheduled")
elif name == "serve":
events.append("server.serve.scheduled")
close = getattr(coro, "close", None)
if callable(close):
close()
return __import__("asyncio").create_task(__import__("asyncio").sleep(0))
from opensquilla.gateway import boot
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
monkeypatch.setattr(boot, "build_services", fake_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(boot, "create_background_task", fake_create_background_task)
monkeypatch.setattr(boot.uvicorn, "Server", FakeServer)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire",
lambda self: None,
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
)
config.squilla_router.enabled = True
server = await boot.start_gateway_server(
config=config,
channel_manager=FakeChannelManager(),
run=True,
)
try:
assert events.index("channels.start_all") < events.index("router.preload.scheduled")
finally:
await server.close()
def test_start_gateway_server_passes_tls_files_to_uvicorn(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured_config: dict[str, Any] = {}
class FakeTurnRunner:
def __init__(self, **_kwargs: Any) -> None:
pass
def set_session_lock_provider(self, _provider: Any) -> None:
pass
class FakeUvicornConfig:
def __init__(self, **kwargs: Any) -> None:
captured_config.update(kwargs)
class FakeServer:
def __init__(self, _config: Any) -> None:
self.should_exit = False
async def serve(self) -> None:
return None
async def fake_build_services(**kwargs: Any) -> Any:
config = kwargs["config"]
async def close() -> None:
return None
return SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=object(),
skill_loader=object(),
usage_tracker=object(),
config=config,
memory_sync_managers={},
model_catalog=None,
memory_retrievers={},
turn_capture_services={},
flush_service=None,
cron_scheduler=None,
task_runtime=None,
agent_registry=None,
memory_managers={},
memory_stores={},
_turn_runner_ref=[],
close=close,
)
def fake_create_background_task(coro: Any) -> Any:
close = getattr(coro, "close", None)
if callable(close):
close()
return asyncio.create_task(asyncio.sleep(0))
from opensquilla.gateway import boot
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
monkeypatch.setattr(boot, "build_services", fake_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(boot, "create_background_task", fake_create_background_task)
monkeypatch.setattr(boot.uvicorn, "Config", FakeUvicornConfig)
monkeypatch.setattr(boot.uvicorn, "Server", FakeServer)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire",
lambda self: None,
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.release",
lambda self: None,
)
keyfile = str(tmp_path / "gateway.key")
certfile = str(tmp_path / "gateway.crt")
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
tls={"keyfile": keyfile, "certfile": certfile},
)
async def run_case() -> None:
server = await boot.start_gateway_server(config=config, run=True)
try:
assert captured_config["ssl_keyfile"] == keyfile
assert captured_config["ssl_certfile"] == certfile
finally:
await server.close()
asyncio.run(run_case())
@pytest.mark.asyncio
async def test_start_gateway_server_wires_cron_failure_dispatcher(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Driver-level guard for the production cron failure-destination wire.
When ``svc.cron_scheduler`` exists, boot must register
``DeliveryChain.dispatch_failure_alert`` as the global failure dispatcher
in ``scheduler.jobs`` so failed cron runs reach the configured FD at
runtime. Without this wire the dispatch plumbing is dead in production
even though unit tests cover the hook directly.
"""
captured: dict[str, Any] = {}
class FakeTurnRunner:
def __init__(self, **_kw: Any) -> None: ...
def set_session_lock_provider(self, _provider: Any) -> None: ...
class FakeCronScheduler:
def __init__(self) -> None:
self.registered: dict[str, Any] = {}
def register_handler(self, key: str, fn: Any) -> None:
self.registered[key] = fn
async def list_jobs(self) -> list:
return []
cron_sched = FakeCronScheduler()
async def fake_build_services(**kwargs: Any) -> Any:
async def close() -> None:
return None
return SimpleNamespace(
provider_selector=object(),
tool_registry=object(),
session_manager=None,
skill_loader=object(),
usage_tracker=object(),
config=kwargs["config"],
memory_sync_managers={},
model_catalog=None,
memory_retrievers={},
turn_capture_services={},
flush_service=None,
cron_scheduler=cron_sched,
task_runtime=None,
agent_registry=None,
memory_managers={},
memory_stores={},
_turn_runner_ref=[],
close=close,
)
from opensquilla.gateway import boot
from opensquilla.scheduler import jobs as scheduler_jobs
def _record_dispatcher(fn: Any) -> None:
captured["dispatcher"] = fn
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
monkeypatch.setattr(boot, "build_services", fake_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(scheduler_jobs, "set_failure_dispatcher", _record_dispatcher)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire", lambda self: None
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.release", lambda self: None
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
)
server = await boot.start_gateway_server(config=config, run=False)
try:
assert callable(captured.get("dispatcher")), (
"set_failure_dispatcher was not called during boot — the cron "
"failure-destination wire is missing from gateway/boot.py"
)
# The wire must register DeliveryChain.dispatch_failure_alert
# (a bound method), not some unrelated callable.
assert (
getattr(captured["dispatcher"], "__name__", "")
== "dispatch_failure_alert"
)
# Handler factories ran, confirming the wire ran inside the cron-init
# branch (not just by coincidence).
assert set(cron_sched.registered) >= {
"agent_run",
"static_message",
"system_event",
}
finally:
await server.close()
@pytest.mark.asyncio
async def test_start_gateway_server_wires_meta_skill_auto_propose_routes(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Boot must connect the three auto-propose surfaces, not just define them.
The cron handler factory, runtime bridge, and dream post-hook each have
isolated unit coverage. This guards the production integration point where
the previous implementation left those pieces unreachable.
"""
from opensquilla.gateway import boot
from opensquilla.gateway.auto_propose_bridge import get_runtime, reset_runtime_for_test
from opensquilla.scheduler import auto_propose_handler as auto_handler_mod
from opensquilla.scheduler import dream_handler as dream_handler_mod
from opensquilla.skills.creator import proposer as proposer_mod
from opensquilla.skills.creator import runtime_e2e as runtime_e2e_mod
reset_runtime_for_test()
captured: dict[str, Any] = {}
runtime_contexts: list[dict[str, Any]] = []
installed_runtime_contexts: list[dict[str, Any]] = []
installed_smoke_contexts: list[dict[str, Any]] = []
reset_tokens: list[str] = []
smoke_reset_tokens: list[str] = []
class FakeProviderSelector:
def __init__(self) -> None:
self.model = "primary-model"
def clone(self) -> FakeProviderSelector:
captured["provider_selector_cloned"] = True
return self
def override_model(self, model: str) -> None:
captured["provider_override_model"] = model
self.model = model
def resolve(self) -> Any:
return SimpleNamespace(model=self.model)
class FakeTurnRunner:
def __init__(self, **_kw: Any) -> None: ...
def set_session_lock_provider(self, _provider: Any) -> None: ...
class FakeCronScheduler:
def __init__(self) -> None:
self.registered: dict[str, Any] = {}
self.added: list[dict[str, Any]] = []
self.paused: list[str] = []
def register_handler(self, key: str, fn: Any) -> None:
self.registered[key] = fn
async def list_jobs(self) -> list:
return []
async def add_job(self, **kwargs: Any) -> Any:
self.added.append(kwargs)
return SimpleNamespace(id=kwargs.get("name", "job"))
async def pause_job(self, job_id: str) -> None:
self.paused.append(job_id)
cron_sched = FakeCronScheduler()
async def fake_build_services(**kwargs: Any) -> Any:
async def close() -> None:
return None
return SimpleNamespace(
provider_selector=FakeProviderSelector(),
tool_registry=ToolRegistry(),
session_manager=None,
skill_loader=object(),
usage_tracker=object(),
config=kwargs["config"],
memory_sync_managers={},
model_catalog=None,
memory_retrievers={},
turn_capture_services={},
flush_service=None,
cron_scheduler=cron_sched,
task_runtime=None,
agent_registry=None,
memory_managers={},
memory_stores={},
_turn_runner_ref=[],
close=close,
)
def fake_make_auto_propose_handler(**kwargs: Any) -> Any:
captured["auto_handler_kwargs"] = kwargs
async def _handler(_job: Any) -> Any:
return SimpleNamespace(summary="auto_propose fake", delivery_status="delivered")
return _handler
def fake_make_memory_dream_handler(*args: Any, **kwargs: Any) -> Any:
captured["dream_handler_kwargs"] = kwargs
return "dream-handler"
def fake_make_runtime_e2e_context(**kwargs: Any) -> dict[str, Any]:
runtime_contexts.append(kwargs)
return {"runner": object(), "judge": object(), "baseline_model": kwargs["baseline_model"]}
def fake_set_runtime_e2e_context(ctx: dict[str, Any]) -> str:
installed_runtime_contexts.append(ctx)
return "runtime-token"
def fake_reset_runtime_e2e_context(token: str) -> None:
reset_tokens.append(token)
def fake_set_smoke_fixture_context(ctx: dict[str, Any]) -> str:
installed_smoke_contexts.append(ctx)
return "smoke-token"
def fake_reset_smoke_fixture_context(token: str) -> None:
smoke_reset_tokens.append(token)
monkeypatch.setattr("opensquilla.engine.runtime.TurnRunner", FakeTurnRunner)
monkeypatch.setattr(boot, "build_services", fake_build_services)
monkeypatch.setattr(boot, "_setup_file_logging", lambda config: None)
monkeypatch.setattr(boot, "emit_skill_filter_banner", lambda config: None)
monkeypatch.setattr(
auto_handler_mod,
"make_auto_propose_handler",
fake_make_auto_propose_handler,
)
monkeypatch.setattr(
dream_handler_mod,
"make_memory_dream_handler",
fake_make_memory_dream_handler,
)
monkeypatch.setattr(
runtime_e2e_mod, "make_runtime_e2e_context", fake_make_runtime_e2e_context
)
monkeypatch.setattr(
proposer_mod, "set_runtime_e2e_context", fake_set_runtime_e2e_context
)
monkeypatch.setattr(
proposer_mod, "reset_runtime_e2e_context", fake_reset_runtime_e2e_context
)
monkeypatch.setattr(
proposer_mod, "set_smoke_fixture_context", fake_set_smoke_fixture_context
)
monkeypatch.setattr(
proposer_mod, "reset_smoke_fixture_context", fake_reset_smoke_fixture_context
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.acquire", lambda self: None
)
monkeypatch.setattr(
"opensquilla.gateway.pidlock.GatewayPidLock.release", lambda self: None
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
memory={"dream": {"enabled": True}},
meta_skill={
"auto_propose": {
"enabled": True,
"on_dream_complete": True,
"auto_enable": True,
},
},
squilla_router={
"tiers": {
"c3": {
"model": "frontier-t3-model",
"thinking_level": "high",
},
},
},
)
server = await boot.start_gateway_server(config=config, run=False)
try:
assert "auto_propose" in cron_sched.registered
assert captured["auto_handler_kwargs"]["config"] is config.meta_skill.auto_propose
assert any(job["handler_key"] == "auto_propose" for job in cron_sched.added)
assert callable(captured["dream_handler_kwargs"].get("post_dream_hook"))
orch = captured["auto_handler_kwargs"]["build_orchestrator"]("main")
assert captured["provider_selector_cloned"] is True
assert captured["provider_override_model"] == "frontier-t3-model"
assert runtime_contexts
assert runtime_contexts[-1]["skill_loader"] is server._services.skill_loader
base_config = runtime_contexts[-1]["base_config"]
assert base_config.model_id == "frontier-t3-model"
assert base_config.metadata["routed_tier"] == "c3"
assert base_config.metadata["thinking_level"] == "high"
assert runtime_contexts[-1]["baseline_model"] == "frontier-t3-model"
with pytest.raises(RuntimeError):
await orch._tool_invoker("meta_skill_runtime_e2e_run", {"skill_md": "x"})
assert installed_runtime_contexts[-1] is not None
assert installed_smoke_contexts[-1]["llm_chat"] is not None
assert reset_tokens[-1] == "runtime-token"
assert smoke_reset_tokens[-1] == "smoke-token"
rt = get_runtime()
assert rt is not None
assert rt.config is config.meta_skill.auto_propose
assert rt.home == tmp_path
finally:
await server.close()
reset_runtime_for_test()
def test_build_flush_service_respects_memory_flush_enabled_config() -> None:
service = build_flush_service(
tool_registry=ToolRegistry(),
provider_selector=SimpleNamespace(resolve=lambda: object()),
config=GatewayConfig(memory={"flush_enabled": False}),
)
assert service is None
def test_build_flush_service_uses_configured_background_memory_timeout() -> None:
service = build_flush_service(
tool_registry=ToolRegistry(),
provider_selector=SimpleNamespace(resolve=lambda: object()),
config=GatewayConfig(
memory={
"flush_enabled": True,
"flush_timeout_seconds": 0.25,
"flush_background_timeout_seconds": 42.0,
}
),
)
assert service is not None
assert service._default_timeout == 42.0
@pytest.mark.asyncio
async def test_build_flush_service_archive_workspace_falls_back_to_main_workspace(
tmp_path: Path,
) -> None:
registry = ToolRegistry()
main_workspace = tmp_path / "main-workspace"
matching_memory_dir = tmp_path / "matching-memory"
service = build_flush_service(
tool_registry=registry,
provider_selector=SimpleNamespace(resolve=lambda: None),
config=GatewayConfig(memory={"flush_enabled": True}),
memory_managers={
"side": SimpleNamespace(workspace_dir=None, memory_dir=matching_memory_dir),
"main": SimpleNamespace(
workspace_dir=main_workspace,
memory_dir=tmp_path / "main-memory",
),
},
)
receipt = await service.execute(
[Message(role="user", content="temporary transcript")],
"agent:side:webchat:s1",
agent_id="side",
)
assert receipt.mode == "raw"
assert (main_workspace / receipt.flushed_paths[0]).exists()
assert not (matching_memory_dir / receipt.flushed_paths[0]).exists()
@pytest.mark.asyncio
async def test_build_flush_service_wires_durable_receipt_writer(tmp_path: Path) -> None:
storage = await SessionStorage.open(str(tmp_path / "sessions.sqlite"))
session_manager = SessionManager(storage)
registry = ToolRegistry()
async def memory_save(path: str, content: str, mode: str) -> str:
assert mode == "append"
assert content.startswith("# Raw flush")
return f"Saved to {path} (0 chunks indexed)."
registry.register(
ToolSpec(
name="memory_save",
description="Save memory",
parameters={
"path": {"type": "string"},
"content": {"type": "string"},
"mode": {"type": "string"},
},
required=["path", "content", "mode"],
),
memory_save,
)
try:
session_key = "agent:main:webchat:s1"
session = await session_manager.create(session_key)
service = build_flush_service(
tool_registry=registry,
provider_selector=SimpleNamespace(resolve=lambda: None),
config=GatewayConfig(memory={"flush_enabled": True}),
session_manager=session_manager,
memory_managers={"main": SimpleNamespace(workspace_dir=tmp_path)},
)
receipt = await service.execute(
[Message(role="user", content="temporary transcript")],
session_key,
agent_id="main",
)
rows = await storage.list_memory_durable_receipts(session_key=session_key)
assert receipt.result_status == "ok_archive_only"
assert len(rows) == 2
assert rows[0].scope == "preimage"
repair_row = rows[1]
assert repair_row.session_id == session.session_id
assert repair_row.scope == "repair"
assert repair_row.status == "repair_pending"
assert repair_row.reason == "ok_archive_only"
assert repair_row.target_path == receipt.flushed_paths[0]
assert repair_row.source_path == f"session:{session_key}:flush:1-1"
assert repair_row.content_hash == receipt.content_hash
assert repair_row.turn_id == "flush:1-1"
assert repair_row.idempotency_key.startswith(
f"flush-receipt:repair:{session_key}:{session.session_id}:flush:1-1:"
)
finally:
await storage.close()
@pytest.mark.asyncio
async def test_build_flush_service_receipt_uses_session_id_captured_before_rotation(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
storage = await SessionStorage.open(str(tmp_path / "sessions.sqlite"))
session_manager = SessionManager(storage)
registry = ToolRegistry()
archive_started = Event()
allow_archive = Event()
from opensquilla.memory import session_flush as session_flush_module
real_archive_writer = session_flush_module.write_raw_fallback_archive
def archive_writer(*args: Any, **kwargs: Any) -> Any:
archive_started.set()
assert allow_archive.wait(timeout=2.0)
return real_archive_writer(*args, **kwargs)
monkeypatch.setattr(
session_flush_module,
"write_raw_fallback_archive",
archive_writer,
)
try:
session_key = "agent:main:webchat:s1"
original = await session_manager.create(session_key)
service = build_flush_service(
tool_registry=registry,
provider_selector=SimpleNamespace(resolve=lambda: None),
config=GatewayConfig(memory={"flush_enabled": True}),
session_manager=session_manager,
memory_managers={"main": SimpleNamespace(workspace_dir=tmp_path)},
)
task = asyncio.create_task(
service.execute(
[Message(role="user", content="temporary transcript")],
session_key,
agent_id="main",
)
)
await asyncio.wait_for(asyncio.to_thread(archive_started.wait), timeout=2.0)
rotated, did_rotate = await session_manager.apply_intent(
session_key,
SessionIntent.RESET_SAME_KEY,
)
allow_archive.set()
receipt = await task
rows = await storage.list_memory_durable_receipts(session_key=session_key)
assert did_rotate
assert rotated.session_id != original.session_id
assert receipt.session_id == original.session_id
assert len(rows) == 2
assert {row.scope for row in rows} == {"preimage", "repair"}
for row in rows:
assert row.session_id == original.session_id
assert row.session_id != rotated.session_id
finally:
await storage.close()
@pytest.mark.asyncio
async def test_build_flush_service_receipts_distinguish_same_window_different_content(
tmp_path: Path,
) -> None:
storage = await SessionStorage.open(str(tmp_path / "sessions.sqlite"))
session_manager = SessionManager(storage)
registry = ToolRegistry()
async def memory_save(path: str, content: str, mode: str) -> str:
return f"Saved to {path} (0 chunks indexed)."
registry.register(
ToolSpec(
name="memory_save",
description="Save memory",
parameters={
"path": {"type": "string"},
"content": {"type": "string"},
"mode": {"type": "string"},
},
required=["path", "content", "mode"],
),
memory_save,
)
try:
session_key = "agent:main:webchat:s1"
await session_manager.create(session_key)
service = build_flush_service(
tool_registry=registry,
provider_selector=SimpleNamespace(resolve=lambda: None),
config=GatewayConfig(memory={"flush_enabled": True}),
session_manager=session_manager,
memory_managers={"main": SimpleNamespace(workspace_dir=tmp_path)},
)
first = await service.execute(
[Message(role="user", content="first content")],
session_key,
agent_id="main",
)
second = await service.execute(
[Message(role="user", content="second content")],
session_key,
agent_id="main",
)
rows = await storage.list_memory_durable_receipts(session_key=session_key)
assert first.content_hash != second.content_hash
repair_rows = [row for row in rows if row.scope == "repair"]
assert len(repair_rows) == 2
assert len({row.content_hash for row in repair_rows}) == 2
assert len({row.idempotency_key for row in repair_rows}) == 2
finally:
await storage.close()
@pytest.mark.asyncio
async def test_build_flush_service_archive_failed_without_checkpoint_is_checkpoint_failed(
tmp_path: Path,
) -> None:
storage = await SessionStorage.open(str(tmp_path / "sessions.sqlite"))
session_manager = SessionManager(storage)
registry = ToolRegistry()
async def memory_save(path: str, content: str, mode: str) -> str:
raise RuntimeError("disk full")
registry.register(
ToolSpec(
name="memory_save",
description="Save memory",
parameters={
"path": {"type": "string"},
"content": {"type": "string"},
"mode": {"type": "string"},
},
required=["path", "content", "mode"],
),
memory_save,
)
try:
session_key = "agent:main:webchat:s1"
session = await session_manager.create(session_key)
service = build_flush_service(
tool_registry=registry,
provider_selector=SimpleNamespace(resolve=lambda: None),
config=GatewayConfig(memory={"flush_enabled": True}),
session_manager=session_manager,
)
receipt = await service.execute(
[Message(role="user", content="temporary transcript")],
session_key,
agent_id="main",
)
rows = await storage.list_memory_durable_receipts(session_key=session_key)
assert receipt.result_status == "archive_failed"
assert len(rows) == 1
assert rows[0].session_id == session.session_id
assert rows[0].scope == "checkpoint"
assert rows[0].status == "checkpoint_failed"
assert rows[0].reason == "archive_failed"
assert rows[0].content_hash == receipt.content_hash
finally:
await storage.close()
@pytest.mark.asyncio
async def test_build_services_registers_session_search_tool(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
def fail_background_sandbox_setup(coro: Any) -> None:
close = getattr(coro, "close", None)
if callable(close):
close()
raise AssertionError("unit tests must not schedule real sandbox setup")
monkeypatch.setattr(
"opensquilla.gateway.boot.create_background_task",
fail_background_sandbox_setup,
)
monkeypatch.setattr(
"opensquilla.sandbox.integration.configure_runtime",
lambda *args, **kwargs: SimpleNamespace(
effective=SimpleNamespace(as_dict=lambda: {})
),
)
captured_memory_kwargs: dict[str, Any] = {}
async def fake_build_memory_managers(*_args: Any, **_kwargs: Any) -> dict[str, Any]:
captured_memory_kwargs.update(_kwargs)
return {}
monkeypatch.setattr(
"opensquilla.memory.manager.build_memory_managers",
fake_build_memory_managers,
)
registry = ToolRegistry()
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
control_ui={"enabled": False},
channels={"channels": []},
mcp={"enabled": False},
memory={"flush_enabled": False},
sandbox={"auto_setup": False},
)
services = await build_services(
config=config,
tool_registry=registry,
session_db_path=str(tmp_path / "sessions.sqlite"),
)
try:
session_search = registry.get("session_search")
assert session_search is not None
assert "Full-text search across persisted session transcripts" in (
session_search.spec.description
)
assert "defaults to curated memory source files" in (
session_search.spec.description
)
assert "use source=sessions or source=all" in session_search.spec.description
owner_names = {
tool["name"]
for tool in await registry.list_tools(
caller_kind=CallerKind.AGENT,
is_owner=True,
)
}
channel_names = {
tool.name
for tool in registry.to_tool_definitions(
ToolContext(is_owner=False, caller_kind=CallerKind.CHANNEL)
)
}
assert "session_search" in owner_names
assert "session_search" not in channel_names
await services.session_manager.create("agent:main:main")
await services.session_manager.append_message(
"agent:main:main",
"user",
"needle transcript detail",
)
output = await session_search.handler(query="needle", limit=5)
assert "needle" in output
assert "agent:main:main" in output
assert captured_memory_kwargs["session_storage"] is services.session_manager.storage
finally:
await services.close()
def test_router_boot_validation_does_not_load_heavy_runtime(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
bundle_dir = tmp_path / "v4_bundle"
(bundle_dir / "runtime_src").mkdir(parents=True)
(bundle_dir / "router.runtime.yaml").write_text("v4: {}\n", encoding="utf-8")
config = GatewayConfig()
config.squilla_router.v4_bundle_dir = str(bundle_dir)
config.squilla_router.require_router_runtime = True
real_import = builtins.__import__
def guarded_import(name: str, *args: Any, **kwargs: Any) -> Any:
if name == "opensquilla.squilla_router.v4_phase3":
raise AssertionError("boot validation must not load V4Phase3Strategy")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", guarded_import)
validate_squilla_router_runtime(config)
def test_router_boot_validation_still_fails_when_required_bundle_missing(tmp_path: Path) -> None:
config = GatewayConfig()
config.squilla_router.v4_bundle_dir = str(tmp_path / "missing")
config.squilla_router.require_router_runtime = True
with pytest.raises(RuntimeError, match="missing V4 bundle files"):
validate_squilla_router_runtime(config)
def test_skill_filter_banner_accepts_tokenizers_without_transformers(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
caplog: pytest.LogCaptureFixture,
) -> None:
from opensquilla.memory.embedding import LocalEmbeddingProvider
def fake_find_spec(name: str):
if name in {"onnxruntime", "tokenizers"}:
return object()
if name == "transformers":
return None
raise AssertionError(name)
monkeypatch.setattr("importlib.util.find_spec", fake_find_spec)
monkeypatch.setattr(
LocalEmbeddingProvider,
"_bundled_onnx_dir",
classmethod(lambda cls, model_name: tmp_path),
)
emit_skill_filter_banner(
SimpleNamespace(filter_enabled=True, filter_strategy="semantic", filter_embedding_model="")
)
assert "ONNX embedding backend not available" not in caplog.text
@pytest.mark.asyncio
async def test_build_services_fails_fast_for_explicit_remote_memory_without_key(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setattr(
"opensquilla.sandbox.integration.configure_runtime",
lambda *args, **kwargs: SimpleNamespace(
effective=SimpleNamespace(as_dict=lambda: {})
),
)
config = GatewayConfig(
state_dir=str(tmp_path / "state"),
workspace_dir=str(tmp_path / "workspace"),
memory={"embedding": {"provider": "openai"}},
sandbox={"auto_setup": False},
)
with pytest.raises(ValueError, match="memory.embedding.remote.api_key"):
await build_services(config=config)
def test_configured_agent_ids_include_enabled_registry_agents_and_channels() -> None:
result = upsert_channel(
GatewayConfig(
agents=[
AgentEntryConfig(id="ops"),
AgentEntryConfig(id="disabled", enabled=False),
]
),
entry_payload={
"type": "slack",
"name": "work",
"token": "x",
"signing_secret": "ss",
"agent_id": "channel",
},
)
assert _configured_agent_ids(result.config) == ["channel", "main", "ops"]
def test_workspace_state_mismatch_emits_warning(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
warnings: list[dict[str, Any]] = []
monkeypatch.setenv("OPENSQUILLA_STATE_DIR", str(tmp_path / "gateway-3"))
monkeypatch.setenv(
"OPENSQUILLA_GATEWAY_CONFIG_PATH",
str(tmp_path / "gateway-3" / "config.toml"),
)
monkeypatch.setattr(
"opensquilla.gateway.boot.log.warning",
lambda event, **kwargs: warnings.append({"event": event, **kwargs}),
)
config = GatewayConfig(
state_dir=str(tmp_path / "gateway-3" / "state"),
workspace_dir=str(tmp_path / "gateway-1" / "workspace"),
config_path=str(tmp_path / "gateway-3" / "config.toml"),
)
_warn_workspace_state_mismatch(config)
assert warnings
assert warnings[0]["event"] == "build_services.workspace_state_mismatch"
assert "OPENSQUILLA_STATE_DIR" in warnings[0]["expected_roots"]
def test_dream_defaults_are_fail_closed() -> None:
config = GatewayConfig()
assert config.memory.dream.enabled is False
assert config.memory.dream.preview_mode is True
assert config.memory.dream.auto_schedule is False
def test_memory_mode_fingerprint_keeps_dream_auto_schedule_visible() -> None:
config = GatewayConfig(memory={"dream": {"enabled": True}})
assert config.memory.dream.enabled is True
assert config.memory.dream.preview_mode is True
assert config.memory.dream.auto_schedule is False
assert config.memory_mode_fingerprint()["dream_auto_schedule"] == "false"
@pytest.mark.asyncio
async def test_dream_boot_does_not_register_when_auto_schedule_is_off() -> None:
scheduler = _FakeDreamScheduler()
config = GatewayConfig(memory={"dream": {"enabled": True}})
await _register_dream_crons(
scheduler=scheduler,
memory_config=config.memory,
agent_ids=["main"],
)
assert scheduler.added == []
@pytest.mark.asyncio
async def test_dream_boot_pauses_existing_jobs_when_auto_schedule_is_off() -> None:
existing = CronJob(id="dream-main", name="memory_dream:main", status=JobStatus.PENDING)
scheduler = _FakeDreamScheduler([existing])
config = GatewayConfig(memory={"dream": {"enabled": True}})
await _register_dream_crons(
scheduler=scheduler,
memory_config=config.memory,
agent_ids=["main"],
)
assert scheduler.paused == ["dream-main"]
assert existing.status == JobStatus.PAUSED
assert scheduler.added == []
@pytest.mark.asyncio
async def test_dream_boot_pauses_existing_jobs_when_disabled(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_MEMORY_DREAM_DISABLED", "1")
existing = CronJob(id="dream-main", name="memory_dream:main", status=JobStatus.PENDING)
scheduler = _FakeDreamScheduler([existing])
config = GatewayConfig(
memory={"dream": {"enabled": True, "auto_schedule": True}},
)
await _register_dream_crons(
scheduler=scheduler,
memory_config=config.memory,
agent_ids=["main"],
)
assert scheduler.paused == ["dream-main"]
assert existing.status == JobStatus.PAUSED
assert scheduler.added == []
@pytest.mark.asyncio
async def test_task_runtime_turn_uses_agent_registry_model_when_session_has_no_model() -> None:
class RecordingTurnRunner:
def __init__(self) -> None:
self.calls: list[dict[str, Any]] = []
async def run(self, message: str, session_key: str, **kwargs: Any):
self.calls.append(kwargs)
yield DoneEvent()
class SessionManager:
async def get_session(self, session_key: str) -> Any:
return SimpleNamespace(model=None)
events: list[tuple[str, str, dict[str, Any]]] = []
async def emit(session_key: str, event_name: str, payload: dict[str, Any]) -> None:
events.append((session_key, event_name, payload))
config = GatewayConfig(
agents=[AgentEntryConfig(id="ops", model="agent/default")],
agent_stream_heartbeat_interval_seconds=0.0,
agent_stream_idle_timeout_seconds=1.0,
)
run = SimpleNamespace(
agent_id="ops",
task_id="task-1",
session_key="agent:ops:task-runtime",
message="hello",
envelope=build_cli_route_envelope(
session_key="agent:ops:task-runtime",
agent_id="ops",
),
attachments=[],
input_provenance={},
run_kind="interactive",
no_memory_capture=False,
ingress_pipeline_steps=[],
semantic_message=None,
stream_event_sink=None,
)
runner = RecordingTurnRunner()
await dispatch_task_runtime_turn(
run,
config=config,
session_manager=SessionManager(),
turn_runner=runner,
event_emitter=emit,
)
assert runner.calls[0]["model"] == "agent/default"
@pytest.mark.asyncio
async def test_task_runtime_turn_applies_cron_job_tool_policy() -> None:
class RecordingTurnRunner:
def __init__(self) -> None:
self.calls: list[dict[str, Any]] = []
async def run(self, message: str, session_key: str, **kwargs: Any):
self.calls.append(kwargs)
yield DoneEvent()
events: list[tuple[str, str, dict[str, Any]]] = []
async def emit(session_key: str, event_name: str, payload: dict[str, Any]) -> None:
events.append((session_key, event_name, payload))
job = CronJob(
id="cron-policy",
name="Policy",
payload={"kind": "agent_turn", "agent_id": "ops"},
tool_policy={
"profile": "minimal",
"also_allow": ["memory_search", "exec_command"],
"deny": ["web_fetch"],
},
)
run = SimpleNamespace(
agent_id="ops",
task_id="task-1",
session_key="cron:cron-policy:run:1",
message="hello",
envelope=build_cron_route_envelope(
job,
session_key="cron:cron-policy:run:1",
agent_id="ops",
),
attachments=[],
input_provenance={},
run_kind="cron_turn",
no_memory_capture=False,
ingress_pipeline_steps=[],
semantic_message=None,
stream_event_sink=None,
)
runner = RecordingTurnRunner()
await dispatch_task_runtime_turn(
run,
config=GatewayConfig(),
session_manager=None,
turn_runner=runner,
event_emitter=emit,
)
tool_context = runner.calls[0]["tool_context"]
assert tool_context.allowed_tools == {"session_status"}
assert "exec_command" in tool_context.denied_tools
assert "web_fetch" in tool_context.denied_tools
@pytest.mark.asyncio
async def test_task_runtime_turn_uses_owner_boundary_for_owner_cron_job() -> None:
class RecordingTurnRunner:
def __init__(self) -> None:
self.calls: list[dict[str, Any]] = []
async def run(self, message: str, session_key: str, **kwargs: Any):
self.calls.append(kwargs)
yield DoneEvent()
async def emit(session_key: str, event_name: str, payload: dict[str, Any]) -> None:
return None
job = CronJob(
id="cron-owner",
name="Owner",
payload={"kind": "agent_turn", "agent_id": "ops"},
creator_is_owner=True,
tool_policy={
"profile": "minimal",
"also_allow": ["memory_search", "exec_command"],
"deny": ["web_fetch"],
},
)
run = SimpleNamespace(
agent_id="ops",
task_id="task-1",
session_key="cron:cron-owner:run:1",
message="hello",
envelope=build_cron_route_envelope(
job,
session_key="cron:cron-owner:run:1",
agent_id="ops",
),
attachments=[],
input_provenance={},
run_kind="cron_turn",
no_memory_capture=False,
ingress_pipeline_steps=[],
semantic_message=None,
stream_event_sink=None,
)
runner = RecordingTurnRunner()
await dispatch_task_runtime_turn(
run,
config=GatewayConfig(),
session_manager=None,
turn_runner=runner,
event_emitter=emit,
)
tool_context = runner.calls[0]["tool_context"]
assert tool_context.is_owner is True
assert tool_context.allowed_tools is None
assert tool_context.tool_policy == job.tool_policy
assert "exec_command" not in tool_context.denied_tools