From 1707a7b09d680c40d1b9c7b83f93bf0391eedf95 Mon Sep 17 00:00:00 2001 From: Svrnty Date: Fri, 29 May 2026 02:53:40 -0400 Subject: [PATCH] Extract Cortex OS Hermes WebUI Host Adapter --- CONNECTION-MAP.md | 33 ++++++------ manifest.yaml | 3 ++ plugin.py | 9 ++-- routes/cortex_os_extension.py | 30 +++++++++++ tests/integration/test_loader_contract.py | 10 ++++ .../unit/test_cortex_os_extension_adapter.py | 51 +++++++++++++++++++ 6 files changed, 116 insertions(+), 20 deletions(-) create mode 100644 routes/cortex_os_extension.py create mode 100644 tests/unit/test_cortex_os_extension_adapter.py diff --git a/CONNECTION-MAP.md b/CONNECTION-MAP.md index 4f0c0b8..d5abcca 100644 --- a/CONNECTION-MAP.md +++ b/CONNECTION-MAP.md @@ -2,7 +2,7 @@ **Upstream version:** v0.51.118 **Plugin version:** 0.5.0 -**Total dependencies:** 61 (40 public API · 0 forced internal · 21 frontend) +**Total dependencies:** 62 (41 public API · 0 forced internal · 21 frontend) > **Auto-generated by `scripts/ast-connection-map.py`. Do not hand-edit.** > To change a justification, edit the `# CONNECTION:` comment above the @@ -14,21 +14,19 @@ | Plugin location | Upstream symbol | Snippet | |---|---|---| -| `plugin.py:29` | `api.logger` | `log = api.logger("svrnty.plugin")` | -| `plugin.py:34` | `api.register_static` | `api.register_static(STATIC_PREFIX, str(STATIC_DIR))` | -| `plugin.py:35` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/app.css")` | -| `plugin.py:36` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/app.js")` | -| `plugin.py:39` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/svrnty_nav.js")` | -| `plugin.py:42` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/adwright.css")` | -| `plugin.py:43` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/adwright.js")` | -| `plugin.py:45` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/bte.css")` | -| `plugin.py:46` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/bte.js")` | -| `plugin.py:48` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/canvas.css")` | -| `plugin.py:49` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/canvas.js")` | -| `plugin.py:51` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css")` | -| `plugin.py:52` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js")` | -| `plugin.py:54` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.c` | -| `plugin.py:55` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.js")` | +| `plugin.py:31` | `api.logger` | `log = api.logger("svrnty.plugin")` | +| `plugin.py:36` | `api.register_static` | `api.register_static(STATIC_PREFIX, str(STATIC_DIR))` | +| `plugin.py:37` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/app.css")` | +| `plugin.py:38` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/app.js")` | +| `plugin.py:41` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/svrnty_nav.js")` | +| `plugin.py:44` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/adwright.css")` | +| `plugin.py:45` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/adwright.js")` | +| `plugin.py:47` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/bte.css")` | +| `plugin.py:48` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/bte.js")` | +| `plugin.py:50` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/canvas.css")` | +| `plugin.py:51` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/canvas.js")` | +| `plugin.py:53` | `api.inject_stylesheet` | `api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css")` | +| `plugin.py:54` | `api.inject_script` | `api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js")` | | `routes/adwright.py:68` | `api.logger` | `log = api.logger("svrnty.routes.adwright")` | | `routes/adwright.py:69` | `api.register_route` | `api.register_route(` | | `routes/adwright.py:71` | `api.register_route` | `api.register_route(` | @@ -44,6 +42,9 @@ | `routes/canvas.py:57` | `api.register_route` | `api.register_route("/api/canvas/command", "POST", _handle_command)` | | `routes/canvas.py:58` | `api.register_route` | `api.register_route("/api/canvas/design-context", "GET", _handle_design_context)` | | `routes/canvas.py:59` | `api.register_route` | `api.register_route("/api/canvas/events", "GET", _handle_events)` | +| `routes/cortex_os_extension.py:17` | `api.logger` | `log = api.logger("svrnty.routes.cortex_os_extension")` | +| `routes/cortex_os_extension.py:21` | `api.inject_stylesheet` | `api.inject_stylesheet(stylesheet)` | +| `routes/cortex_os_extension.py:22` | `api.inject_script` | `api.inject_script(script)` | | `routes/cortex_os_runtime_health.py:26` | `api.logger` | `log = api.logger("svrnty.routes.cortex_os_runtime_health")` | | `routes/cortex_os_runtime_health.py:27` | `api.register_route` | `api.register_route(ROUTE_PATH, ROUTE_METHOD, _handle_runtime_health)` | | `routes/transcribe.py:37` | `api.logger` | `log = api.logger("svrnty.routes.transcribe")` | diff --git a/manifest.yaml b/manifest.yaml index b535d4b..14ec564 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -35,12 +35,14 @@ assets: - /plugins/svrnty/bte.js - /plugins/svrnty/canvas.js - /plugins/svrnty/umbrella_inline.js + - /plugins/svrnty/cortex-os/runtime-health/runtime_health.js stylesheets: - /plugins/svrnty/app.css - /plugins/svrnty/adwright.css - /plugins/svrnty/bte.css - /plugins/svrnty/canvas.css - /plugins/svrnty/umbrella_inline.css + - /plugins/svrnty/cortex-os/runtime-health/runtime_health.css # Routes this plugin registers at load time (declarative cross-check vs runtime). # Each row maps to a routes/.py. @@ -59,6 +61,7 @@ routes: - { path: /api/canvas/command, method: POST, file: routes/canvas.py, status: live } - { path: /api/canvas/design-context, method: GET, file: routes/canvas.py, status: seed } - { path: /api/canvas/events, method: GET, file: routes/canvas.py, status: seed } + - { path: /api/cortex-os/runtime-health, method: GET, file: routes/cortex_os_extension.py, status: live } # Audio-attachment processors (called by streaming.py before agent receives message). audio_processors: diff --git a/plugin.py b/plugin.py index 3360316..d71f6ce 100644 --- a/plugin.py +++ b/plugin.py @@ -14,6 +14,8 @@ in `static/`. The map of every upstream dependency is in CONNECTION-MAP.md import os from pathlib import Path +from routes import cortex_os_extension + # Static + asset URL prefix (per protocol §12, decision Q5: /plugins/svrnty/) STATIC_PREFIX = "svrnty" STATIC_DIR = Path(__file__).resolve().parent / "static" @@ -50,11 +52,11 @@ def register(api): # Inline Umbrella graph for the Hermes Workspace right panel. api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/umbrella_inline.css") api.inject_script(f"/plugins/{STATIC_PREFIX}/umbrella_inline.js") - # Cortex OS Runtime Health slice: read-only route status display. - api.inject_stylesheet(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.css") - api.inject_script(f"/plugins/{STATIC_PREFIX}/cortex-os/runtime-health/runtime_health.js") log.info("static + assets wired at /plugins/%s/", STATIC_PREFIX) + # Cortex OS Plugin development mount: S23 Runtime Health read-only member. + cortex_os_extension.register(api, STATIC_PREFIX) + # Routes — each feature lives in its own module under routes/. # Phase 2 will populate these. Import-and-register pattern; failures are # logged but don't take down the rest of the plugin. @@ -84,5 +86,4 @@ def _phase2_routes(): "bte_proxy", # P2.D — BTE Command Center same-origin proxy (PRD §3) ✓ "canvas", # Sovereign Stitch-like design canvas proxy + event seed "umbrella", # P2.E — cortex-os umbrella graph viz (UMBRELLA-VIZ-PRD) ✓ - "cortex_os_runtime_health", # S23.0-I4 — Cortex OS Runtime Health read-only slice ✓ ] diff --git a/routes/cortex_os_extension.py b/routes/cortex_os_extension.py new file mode 100644 index 0000000..b346b47 --- /dev/null +++ b/routes/cortex_os_extension.py @@ -0,0 +1,30 @@ +"""Cortex OS Plugin Hermes WebUI Host Adapter extraction surface.""" + +from routes import cortex_os_runtime_health + +PACKAGE_ID = "cortex-os-extension" +MEMBER_ID = "s23-runtime-health-read-only-slice" +ACTIVE_DEVELOPMENT_MOUNT_TARGET = "hermes-webui" +SURFACE_ID = "hermes-webui-host-adapter" +RUNTIME_HEALTH_ROUTE = "/api/cortex-os/runtime-health" +RUNTIME_HEALTH_METHOD = "GET" +RUNTIME_HEALTH_STYLESHEET = "/plugins/{static_prefix}/cortex-os/runtime-health/runtime_health.css" +RUNTIME_HEALTH_SCRIPT = "/plugins/{static_prefix}/cortex-os/runtime-health/runtime_health.js" + + +def register(api, static_prefix): + """Register only the Cortex OS Runtime Health member for Hermes WebUI.""" + log = api.logger("svrnty.routes.cortex_os_extension") + stylesheet = RUNTIME_HEALTH_STYLESHEET.format(static_prefix=static_prefix) + script = RUNTIME_HEALTH_SCRIPT.format(static_prefix=static_prefix) + + api.inject_stylesheet(stylesheet) + api.inject_script(script) + cortex_os_runtime_health.register(api) + + log.info( + "cortex-os-extension mounted: %s %s via %s", + RUNTIME_HEALTH_METHOD, + RUNTIME_HEALTH_ROUTE, + SURFACE_ID, + ) diff --git a/tests/integration/test_loader_contract.py b/tests/integration/test_loader_contract.py index ba9f776..d4f6ff6 100644 --- a/tests/integration/test_loader_contract.py +++ b/tests/integration/test_loader_contract.py @@ -13,6 +13,7 @@ import pytest PLUGIN_REPO = Path(__file__).resolve().parents[2] FORK_REPO = PLUGIN_REPO.parent / "hermes-webui" +MANIFEST = PLUGIN_REPO / "manifest.yaml" @pytest.fixture(scope="module") @@ -85,6 +86,15 @@ def test_loader_register_wires_our_plugin(loader, monkeypatch): assert len(loader._AUDIO_PROCESSORS) == 1 +def test_manifest_declares_cortex_os_runtime_health_surface(): + manifest = MANIFEST.read_text(encoding="utf-8") + + assert "/api/cortex-os/runtime-health" in manifest + assert "routes/cortex_os_extension.py" in manifest + assert "/plugins/svrnty/cortex-os/runtime-health/runtime_health.css" in manifest + assert "/plugins/svrnty/cortex-os/runtime-health/runtime_health.js" in manifest + + def test_loader_noop_when_env_unset(loader, monkeypatch): """No env var = no plugin loaded. Upstream behavior fully preserved.""" monkeypatch.delenv("HERMES_WEBUI_PYTHON_PLUGIN", raising=False) diff --git a/tests/unit/test_cortex_os_extension_adapter.py b/tests/unit/test_cortex_os_extension_adapter.py new file mode 100644 index 0000000..cac143e --- /dev/null +++ b/tests/unit/test_cortex_os_extension_adapter.py @@ -0,0 +1,51 @@ +from unittest.mock import Mock + +from routes import cortex_os_extension + + +def test_adapter_constants_preserve_s27_identity(): + assert cortex_os_extension.PACKAGE_ID == "cortex-os-extension" + assert cortex_os_extension.MEMBER_ID == "s23-runtime-health-read-only-slice" + assert cortex_os_extension.ACTIVE_DEVELOPMENT_MOUNT_TARGET == "hermes-webui" + assert cortex_os_extension.RUNTIME_HEALTH_METHOD == "GET" + assert cortex_os_extension.RUNTIME_HEALTH_ROUTE == "/api/cortex-os/runtime-health" + + +def test_adapter_registers_runtime_health_route_and_assets_only(): + api = Mock() + api.logger.return_value = Mock() + + cortex_os_extension.register(api, "svrnty") + + api.inject_stylesheet.assert_called_once_with( + "/plugins/svrnty/cortex-os/runtime-health/runtime_health.css" + ) + api.inject_script.assert_called_once_with( + "/plugins/svrnty/cortex-os/runtime-health/runtime_health.js" + ) + api.register_route.assert_called_once() + method, path = api.register_route.call_args.args[:2] + assert method == "/api/cortex-os/runtime-health" + assert path == "GET" + api.register_static.assert_not_called() + api.config_get.assert_not_called() + api.register_audio_attachment_processor.assert_not_called() + + +def test_adapter_source_has_no_forbidden_runtime_discovery(): + source = cortex_os_extension.__loader__.get_source(cortex_os_extension.__name__) + forbidden = [ + "glob(", + "rglob(", + "subprocess", + "os.walk", + "sot/08-OUTPUTS", + "hermes_webui", + "hermes_agent", + "register_static", + "config_get", + "register_audio_attachment_processor", + "product readiness", + ] + for snippet in forbidden: + assert snippet not in source