From d904c2e04b63b19e94cc8e1c9088cfb2494db2ef Mon Sep 17 00:00:00 2001 From: Ouroboros Date: Sun, 20 Sep 2026 04:22:43 +0300 Subject: [PATCH] feat: add opt-in module widget theme bridge Add a resolved Light/Dark subscription to the existing sandboxed module widget bridge, validate appearance intent metadata, and prove the author-kit path in Chromium and WebKit. Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com> --- docs/CHECKLISTS.md | 2 +- docs/CREATING_SKILLS.md | 13 +++++++ docs/DESIGN.md | 5 ++- .../03-web-ui-pages-and-buttons.md | 10 +++-- docs/development/11-design-system.md | 4 +- docs/examples/author_ui_kit/README.md | 7 +++- docs/examples/author_ui_kit/plugin.py | 2 +- docs/examples/author_ui_kit/widget.js | 10 +++++ ouroboros/contracts/plugin_api.py | 6 +++ ouroboros/extension_ui_validation.py | 25 +++++++++++- tests/test_author_ui_kit_browser.py | 33 ++++++++++++++-- tests/test_extension_surfaces.py | 31 ++++++++++++++- tests/test_widgets_ui_static.py | 2 +- web/modules/widget_frame.js | 36 +++++++++++++++-- web/modules/widget_module.js | 35 ++++++++++++++++- web/tests/widget_bridge.test.js | 39 ++++++++++++++++++- web/tests/widget_external_relay.test.js | 21 ++++++++++ 17 files changed, 256 insertions(+), 25 deletions(-) diff --git a/docs/CHECKLISTS.md b/docs/CHECKLISTS.md index cf6e74a68..ccd2b6628 100644 --- a/docs/CHECKLISTS.md +++ b/docs/CHECKLISTS.md @@ -630,7 +630,7 @@ and do not return `PASS` for an item that also has a `FAIL` — the concrete | 5 | env_allowlist | Is `env_from_settings` a short, justified list of settings keys? Core keys in `FORBIDDEN_SKILL_SETTINGS` (`OPENROUTER_API_KEY`, `OPENAI_API_KEY`, `OPENAI_COMPATIBLE_API_KEY`, `CLOUDRU_FOUNDATION_MODELS_API_KEY`, `GIGACHAT_CREDENTIALS`, `GIGACHAT_PASSWORD`, `ANTHROPIC_API_KEY`, `MINIMAX_API_KEY`, `DEEPSEEK_API_KEY`, `GITHUB_TOKEN`, `OUROBOROS_NETWORK_PASSWORD`) may be declared only when the skill genuinely needs that provider/token for its stated purpose; runtime forwards them only after a fresh executable review and a content-bound desktop-launcher owner grant. v5.2.2 dual-track grants: both `type: script` skills (forwarded by `_scrub_env`) and `type: extension` skills (forwarded by `PluginAPIImpl.get_settings`) are eligible; `type: instruction` skills cannot receive core keys. Mark unjustified core-key requests or non-forbidden secrets unrelated to the purpose as FAIL. An empty list is the default and always fine. | critical | | 6 | timeout_and_output_discipline | Is `timeout_sec` reasonable for the stated workload (default 60, hard cap 300)? Do scripts print to stdout in chunks that the runtime can cap, rather than streaming unbounded output? Unbounded loops without a `break`/timeout path are a concrete FAIL. | advisory | | 7 | extension_namespace_discipline | `type: extension` only: does the extension register its tool/route/ws-handler/ui-tab under the namespace derived from its `name` (e.g. provider-safe tool/ws names like `ext___`, route `/api/extensions//…`)? Tool and WS short names must be alphanumeric/underscore and at most 24 characters. Namespace collisions with built-in surfaces are a concrete FAIL. If the extension uses `api.send_ws_message`, are emitted event names short/provider-safe and paired with reviewed host-owned widget `subscription` components rather than arbitrary same-origin JavaScript? If the extension declares streaming UI, is it a reviewed extension route consumed by a host-owned `stream` component? A reviewed `module` widget may also consume the skill's own routes (including streaming responses) and the skill's namespaced WebSocket events through the host-mediated bridge (`OuroborosWidget.fetch` / `OuroborosWidget.onEvent`), which is not arbitrary same-origin JavaScript. If the extension owns background resources (threads, sockets, EventSource clients, subprocesses), does it register cleanup with `api.on_unload(callback)`? If the extension declares a widget render block, is it one of the host-owned schemas (`iframe`, `module`, or declarative v1: forms/actions, markdown/code, JSON/kv/table, tabs/chart, stream/subscription, progress/poll, file/gallery/media, map/calendar/kanban, group/metric/callout), with media sourced from extension routes or safe data URLs and no arbitrary same-origin JavaScript? Nested interactive group/tab children must use stable identity and one host-owned lifecycle, while `subscription.render` stays transitively passive. For non-extension skills, verdict PASS with reason "Not applicable — type != extension." | severity-driven for applicable extensions | -| 8 | widget_module_safety | **v5.7.0+. ``kind: "module"`` widgets only.** The host fetches reviewed ``widget.js`` through ``GET /api/extensions//module/``, embeds the source into a sandboxed opaque-origin ``