Construct provider, registry, session, tool_context for ``sess``. Errors are captured into ``sess.init_error`` rather than raised, so the client gets a clean error message instead of a bare socket close.
(sess: _AgentSession, perm_mode: str | None)
| 1387 | |
| 1388 | |
| 1389 | def _build_runtime(sess: _AgentSession, perm_mode: str | None) -> None: |
| 1390 | """Construct provider, registry, session, tool_context for ``sess``. |
| 1391 | |
| 1392 | Errors are captured into ``sess.init_error`` rather than raised, so the |
| 1393 | client gets a clean error message instead of a bare socket close. |
| 1394 | """ |
| 1395 | try: |
| 1396 | from src.config import get_default_provider, get_provider_config |
| 1397 | from src.permissions.settings_paths import default_setup_paths |
| 1398 | from src.permissions.setup import setup_permissions |
| 1399 | from src.providers import ( |
| 1400 | get_provider_class, |
| 1401 | provider_requires_api_key, |
| 1402 | resolve_api_key, |
| 1403 | ) |
| 1404 | from src.agent import Session |
| 1405 | from src.tool_system.context import ToolContext |
| 1406 | from src.tool_system.defaults import build_default_registry |
| 1407 | |
| 1408 | cfg = sess.config |
| 1409 | provider_name = cfg.provider_name or get_default_provider() |
| 1410 | provider_cfg = get_provider_config(provider_name) |
| 1411 | api_key = resolve_api_key(provider_name, provider_cfg) |
| 1412 | if not api_key and provider_requires_api_key(provider_name): |
| 1413 | sess.init_error = ( |
| 1414 | f"API key for provider '{provider_name}' is not configured. " |
| 1415 | "Run `clawcodex login` to set it up." |
| 1416 | ) |
| 1417 | sess.provider_name = provider_name |
| 1418 | return |
| 1419 | |
| 1420 | provider_cls = get_provider_class(provider_name) |
| 1421 | model = cfg.model or provider_cfg.get("default_model") |
| 1422 | provider = provider_cls( |
| 1423 | api_key=api_key, base_url=provider_cfg.get("base_url"), model=model |
| 1424 | ) |
| 1425 | |
| 1426 | registry = build_default_registry(provider=provider) |
| 1427 | if cfg.allowed_tools: |
| 1428 | allow = {n.lower() for n in cfg.allowed_tools} |
| 1429 | _filter_registry(registry, keep=lambda n: n.lower() in allow) |
| 1430 | if cfg.disallowed_tools: |
| 1431 | deny = {n.lower() for n in cfg.disallowed_tools} |
| 1432 | _filter_registry(registry, keep=lambda n: n.lower() not in deny) |
| 1433 | |
| 1434 | # Connect configured MCP servers (guarded: no servers ⇒ no-op). Their |
| 1435 | # tools run on McpRuntime's dedicated loop so they survive the per-turn |
| 1436 | # asyncio.run. Registered after allow/deny filtering so an MCP-enabling |
| 1437 | # user always gets them. |
| 1438 | try: |
| 1439 | from src.server.mcp_runtime import McpRuntime |
| 1440 | |
| 1441 | mcp_rt = McpRuntime() |
| 1442 | if mcp_rt.start(): |
| 1443 | for mtool in mcp_rt.tools: |
| 1444 | try: |
| 1445 | registry.register(mtool) |
| 1446 | except Exception: # noqa: BLE001 |
no test coverage detected