Compare commits

...

1 Commits

Author SHA1 Message Date
harry-yao_data 4fc575e518 perf(terminals): keep FastAPI out of the native CLI launch path
`claude_native.py` imported two integer close codes from
`omnigent.terminals.ws_bridge`. That bridge serves the `/attach`
WebSocket, so it imports FastAPI (~120ms) and, through the package
barrel, the tmux registry (~100ms) — all of it loaded on every
`omnigent claude` launch to compare a close code to `4404`.

Move the four 4xxx codes into a dependency-free
`omnigent.terminals.close_codes`. They are the wire contract between the
server route, the runner, the native client, and the browser, so no
consumer should have to import the socket implementation to read one.
Every consumer now imports from the leaf module, leaving one definition
site rather than an implicit re-export.

Also resolve the `omnigent.terminals` barrel's two exports through PEP
562, so importing a leaf module no longer builds `TerminalRegistry` (and
`omnigent.inner.terminal` under it). `from omnigent.terminals import
TerminalRegistry` is unchanged.

`import omnigent.claude_native`: 0.72s -> 0.57s (-150ms), with FastAPI,
Starlette, and the tmux registry no longer in the graph. Reading a close
code loads 85 modules instead of 288.

The guards pin the published code values (a change there is a protocol
break needing the browser mirror updated) and both import boundaries.

Co-authored-by: Isaac <no-reply@databricks.com>
2026-08-22 08:50:24 +00:00
10 changed files with 184 additions and 28 deletions
+1 -1
View File
@@ -133,7 +133,7 @@ from omnigent.native_terminal import (
terminal_attach_url as _attach_url,
)
from omnigent.onboarding.provider_config import SUBSCRIPTION_KIND
from omnigent.terminals.ws_bridge import (
from omnigent.terminals.close_codes import (
WS_CLOSE_TERMINAL_DETACHED,
WS_CLOSE_TERMINAL_NOT_FOUND,
)
+1 -1
View File
@@ -158,9 +158,9 @@ from omnigent.server.schemas import (
)
from omnigent.spec.skill_sources import SkillSourceContext, resolve_harness_skills
from omnigent.spec.types import AgentSpec, LocalToolInfo, SkillSpec
from omnigent.terminals.close_codes import WS_CLOSE_TERMINAL_NOT_FOUND
from omnigent.terminals.control_bridge import bridge_tmux_control_to_websocket
from omnigent.terminals.ws_bridge import (
WS_CLOSE_TERMINAL_NOT_FOUND,
bridge_tmux_pty_to_websocket,
)
from omnigent.tools.builtins.load_skill import (
+4 -2
View File
@@ -89,11 +89,13 @@ from omnigent.server.auth import LEVEL_OWNER, LEVEL_READ, AuthProvider
from omnigent.server.routes._auth_helpers import require_access
from omnigent.stores import ConversationStore
from omnigent.stores.permission_store import PermissionStore
from omnigent.terminals.control_bridge import bridge_tmux_control_to_websocket
from omnigent.terminals.ws_bridge import (
from omnigent.terminals.close_codes import (
WS_CLOSE_INTERNAL_ERROR,
WS_CLOSE_TERMINAL_NOT_FOUND,
WS_CLOSE_WRONG_REPLICA,
)
from omnigent.terminals.control_bridge import bridge_tmux_control_to_websocket
from omnigent.terminals.ws_bridge import (
bridge_tmux_pty_to_websocket,
)
+37 -1
View File
@@ -9,7 +9,43 @@ See ``designs/OMNIGENT_TERMINAL_BRIDGE.md`` for the design and the
:mod:`omnigent.inner.terminal` for the underlying tmux machinery.
"""
from omnigent.terminals.registry import TerminalListEntry, TerminalRegistry
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from omnigent.terminals.registry import TerminalListEntry, TerminalRegistry
# Resolved on first access (PEP 562) so importing a leaf module such as
# ``omnigent.terminals.close_codes`` does not build the tmux registry —
# ``registry`` reaches into ``omnigent.inner.terminal``, ~100ms of import
# a CLI client reading a WebSocket close code has no use for.
_REGISTRY_EXPORTS: frozenset[str] = frozenset({"TerminalListEntry", "TerminalRegistry"})
def __getattr__(name: str) -> object:
"""
Import :mod:`omnigent.terminals.registry` on first attribute access.
:param name: A public attribute, e.g. ``"TerminalRegistry"``.
:returns: The requested object.
:raises AttributeError: If *name* is not exported.
"""
if name not in _REGISTRY_EXPORTS:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
from omnigent.terminals import registry
value = getattr(registry, name)
globals()[name] = value # cache so later reads skip __getattr__
return value
def __dir__() -> list[str]:
"""
List the package's public names without importing the registry.
:returns: Sorted :data:`__all__`.
"""
return sorted(__all__)
__all__ = [
"TerminalListEntry",
+47
View File
@@ -0,0 +1,47 @@
"""Application-level WebSocket close codes for the terminal bridges.
A leaf module on purpose: these codes are the wire contract between the
server's ``/attach`` route, the runner, the native CLI client, and the
browser, so all four need them without importing the bridge that serves
the socket. :mod:`omnigent.terminals.ws_bridge` pulls in FastAPI and the
tmux machinery, which a CLI client reading a close code has no use for.
Keep this module dependency-free. ``web/src/components/blocks/
TerminalSession.ts`` mirrors these values on the browser side.
"""
from __future__ import annotations
from typing import Final
# RFC 6455 reserves the 4xxx band for application use. The 44xx band
# mirrors HTTP 4xx, as 4500 mirrors 5xx.
# 4404 tells the client's reconnect loop to stop — sent on a
# pre-attach lookup miss and on PTY EOF when the tmux session is
# genuinely gone (Claude exited / the session was killed).
WS_CLOSE_TERMINAL_NOT_FOUND: Final[int] = 4404
# 4405 means the user *detached* from tmux: the ``tmux attach`` child
# exited (PTY EOF) but the session is still alive. The client must NOT
# treat this as a terminal-gone exit: a detach misread as 4404 would
# tear the whole session (and runner) down.
WS_CLOSE_TERMINAL_DETACHED: Final[int] = 4405
# 4400 is the WS analogue of the HTTP 400 ``wrong_replica``: the runner
# tunnel is bound but not on this replica (the ``?omnigent_slice_key=``
# reached a replica that doesn't hold the tunnel — the key doesn't match
# where it lives). Unlike 4500 (a genuine failure), the request is valid
# and just misrouted: the client re-dials keyless and reaches the replica
# the tunnel actually lives on. Mirrors the fetch path's keyless
# re-address on a ``wrong_replica`` 400.
WS_CLOSE_WRONG_REPLICA: Final[int] = 4400
WS_CLOSE_INTERNAL_ERROR: Final[int] = 4500
__all__ = [
"WS_CLOSE_INTERNAL_ERROR",
"WS_CLOSE_TERMINAL_DETACHED",
"WS_CLOSE_TERMINAL_NOT_FOUND",
"WS_CLOSE_WRONG_REPLICA",
]
+3 -1
View File
@@ -68,10 +68,12 @@ from fastapi import WebSocket, WebSocketDisconnect
# backlog forms, and the forwarder collapses thousands of tiny per-line frames
# into a few large ones. ``_coalesce_limit_after_input`` keeps the frame right
# after a keystroke small so the echo stays on xterm's synchronous paint path.
from omnigent.terminals.ws_bridge import (
from omnigent.terminals.close_codes import (
WS_CLOSE_INTERNAL_ERROR,
WS_CLOSE_TERMINAL_DETACHED,
WS_CLOSE_TERMINAL_NOT_FOUND,
)
from omnigent.terminals.ws_bridge import (
_coalesce_limit_after_input,
_forward_pty_to_ws,
_monotonic,
+6 -20
View File
@@ -54,6 +54,12 @@ if sys.platform != "win32":
from fastapi import WebSocket, WebSocketDisconnect
from omnigent.terminals.close_codes import (
WS_CLOSE_INTERNAL_ERROR,
WS_CLOSE_TERMINAL_DETACHED,
WS_CLOSE_TERMINAL_NOT_FOUND,
)
_logger = logging.getLogger(__name__)
# 4 KiB matches tmux's own copy/redraw paths and is a good fit for
@@ -75,26 +81,6 @@ _PANE_LIVENESS_CHECK_CACHE_S: Final[float] = 0.1 # 100ms cache to avoid per-key
_TMUX_ATTACH_WAIT_GRACE_S: Final[float] = 0.5
_TMUX_ATTACH_WAIT_POLL_S: Final[float] = 0.02
# Application-level WebSocket close codes (RFC 6455 reserves 4xxx).
# 4404 tells the client's reconnect loop to stop — sent on a
# pre-attach lookup miss and on PTY EOF when the tmux session is
# genuinely gone (Claude exited / the session was killed).
WS_CLOSE_TERMINAL_NOT_FOUND: Final[int] = 4404
# 4405 means the user *detached* from tmux: the ``tmux attach`` child
# exited (PTY EOF) but the session is still alive. The client must NOT
# treat this as a terminal-gone exit: a detach misread as 4404 would
# tear the whole session (and runner) down.
WS_CLOSE_TERMINAL_DETACHED: Final[int] = 4405
# 4400 is the WS analogue of the HTTP 400 ``wrong_replica`` (the 44xx band
# mirrors HTTP 4xx, as 4500 mirrors 5xx): the runner tunnel is bound but not on
# this replica (the ``?omnigent_slice_key=`` reached a replica that doesn't hold
# the tunnel — the key doesn't match where it lives). Unlike 4500 (a genuine
# failure), the request is valid and just misrouted: the client re-dials keyless
# and reaches the replica the tunnel actually lives on. Mirrors the fetch path's
# keyless re-address on a ``wrong_replica`` 400.
WS_CLOSE_WRONG_REPLICA: Final[int] = 4400
WS_CLOSE_INTERNAL_ERROR: Final[int] = 4500
# A ``tmux has-session`` liveness probe is local and near-instant; cap
# it so a wedged tmux server can't stall the bridge's teardown.
_TMUX_HAS_SESSION_TIMEOUT_S: Final[float] = 2.0
+83
View File
@@ -0,0 +1,83 @@
"""Guard: the terminal close codes stay reachable without FastAPI.
The 4xxx close codes are the wire contract between the server's
``/attach`` route, the runner, the native CLI client, and the browser.
They used to live in :mod:`omnigent.terminals.ws_bridge`, so the CLI's
``omnigent claude`` launch imported FastAPI (~120ms) and the tmux
registry (~100ms) just to compare an integer.
These tests pin the codes to their published values — a change here is a
protocol break that also needs the browser mirror updated — and pin the
import boundary that keeps them cheap to read.
"""
from __future__ import annotations
import subprocess
import sys
from omnigent.terminals import close_codes
# Mirrored in ``web/src/components/blocks/TerminalSession.ts``.
_PUBLISHED_CODES = {
"WS_CLOSE_WRONG_REPLICA": 4400,
"WS_CLOSE_TERMINAL_NOT_FOUND": 4404,
"WS_CLOSE_TERMINAL_DETACHED": 4405,
"WS_CLOSE_INTERNAL_ERROR": 4500,
}
_MUST_NOT_LOAD = (
"fastapi",
"omnigent.inner.terminal",
"omnigent.terminals.registry",
"omnigent.terminals.ws_bridge",
"starlette",
)
def test_published_close_codes_are_stable() -> None:
"""These integers are on the wire — they cannot drift silently."""
actual = {name: getattr(close_codes, name) for name in _PUBLISHED_CODES}
assert actual == _PUBLISHED_CODES
def test_all_lists_every_code() -> None:
"""``__all__`` must not fall behind the module's contents."""
assert sorted(close_codes.__all__) == sorted(_PUBLISHED_CODES)
def test_reading_a_close_code_does_not_import_the_bridge() -> None:
"""A CLI client must not pay for FastAPI to compare an integer."""
proc = subprocess.run(
[
sys.executable,
"-c",
"from omnigent.terminals.close_codes import WS_CLOSE_TERMINAL_NOT_FOUND\n"
"import sys\n"
f"print(sorted(m for m in {_MUST_NOT_LOAD!r} if m in sys.modules))",
],
capture_output=True,
text=True,
check=True,
)
assert proc.stdout.strip() == "[]", (
f"close_codes pulled in heavy modules: {proc.stdout.strip()}"
)
def test_native_client_does_not_import_fastapi() -> None:
"""``omnigent claude``'s module graph must stay FastAPI-free."""
proc = subprocess.run(
[
sys.executable,
"-c",
"import omnigent.claude_native\nimport sys\n"
"print(sorted(m for m in ('fastapi', 'starlette') if m in sys.modules))",
],
capture_output=True,
text=True,
check=True,
)
assert proc.stdout.strip() == "[]", (
f"claude_native imports a server framework: {proc.stdout.strip()}"
)
+1 -1
View File
@@ -29,8 +29,8 @@ from pathlib import Path
import pytest
import omnigent.terminals.ws_bridge as ws_bridge
from omnigent.terminals.close_codes import WS_CLOSE_TERMINAL_DETACHED
from omnigent.terminals.ws_bridge import (
WS_CLOSE_TERMINAL_DETACHED,
_check_pane_dead_definitive,
_forward_pty_to_ws,
_reap_tmux_attach_child,
+1 -1
View File
@@ -35,7 +35,7 @@ from omnigent.databricks_model_discovery import DatabricksClaudeCatalog
from omnigent.runner.identity import OMNIGENT_INTERNAL_WS_ORIGIN
from omnigent.runtime import tool_result_replay as trc
from omnigent.spec import load_omnigent_yaml
from omnigent.terminals.ws_bridge import (
from omnigent.terminals.close_codes import (
WS_CLOSE_TERMINAL_DETACHED,
WS_CLOSE_TERMINAL_NOT_FOUND,
)