-
Notifications
You must be signed in to change notification settings - Fork 7
feat(omnium): Add controller architecture #752
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
packages/omnium-gatherum/src/controllers/caplet/caplet-controller.ts
Outdated
Show resolved
Hide resolved
6736065 to
89e5113
Compare
89e5113 to
ee93a06
Compare
Answers @kumavis's challenge of "What're you afraid of CapTP or something?" by replacing our kernel JSON-RPC API with `E()` on a facet of the kernel. This makes it easy to expose as much of the kernel API as we want via eventual send, and allows us to benefit from pipelining internally. In addition, it facilitates the removal of the command stream and the related RPC API logic. Finally, a number of rationalizations are applied to the extension and omnium. This PR is part of a stack followed by: #752, #753, and #754 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces CapTP-based communication and removes the JSON-RPC command stream. > > - Add CapTP utilities in `kernel-browser-runtime`: `makeBackgroundCapTP`, `makeKernelCapTP`, `makeKernelFacade`, and JSON-RPC `captp` notification helpers; export new `KernelFacade` and `CapTPMessage` types > - Update `kernel-worker` to use CapTP for background↔kernel messaging; initialize kernel without a command stream; route internal RPC only for panel/internal comms > - Refactor `@MetaMask/ocap-kernel` to drop command stream handling and kernel RPC entrypoints; `Kernel.make` now accepts only platform services and the database; adjust `stop()` accordingly > - Migrate extension and omnium backgrounds/offscreens to CapTP over `ChromeRuntimeDuplexStream`, use global `E` and `kernel`/`omnium` helpers; remove `env/dev-console` and background trusted prelude files; add TS globals > - Add unit and integration tests for CapTP (`background-captp`, kernel-side CapTP, and E() end-to-end); introduce `vitest.integration.config.ts` and `test:integration` scripts; CI gains an "Integration Tests" job > - Update deps (add `@endo/captp`, `@endo/eventual-send`), tsconfigs, build constants, and minor coverage thresholds > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit cbf22fd. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Implement Phase 1.2 of the omnium plan - Define Caplet Structure: - Add modular controller architecture with POLA attenuation via makeFacet() - Add storage abstraction layer (StorageAdapter, NamespacedStorage) - Add Chrome storage adapter for platform storage - Add CapletController for managing installed caplets - Add Caplet types with superstruct validation - Wire CapletController into background.ts and expose on globalThis.omnium.caplet - Add comprehensive unit tests for all controller code - Update PLAN.md to reflect implementation
Consolidate CapletControllerState from multiple top-level keys (installed, manifests, subclusters, installedAt) into a single `caplets: Record<CapletId, InstalledCaplet>` structure. Changes: - Add ControllerStorage abstraction using Immer for state management - Controllers work with typed state object instead of storage keys - Only modified top-level keys are persisted (via Immer patches) - Remove state corruption checks (no longer possible with atomic storage) - Fix makeFacet type - use string | symbol instead of keyof MethodGuard - Update PLAN.md to reflect new storage architecture 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add abstract Controller class with state management via ControllerStorage - Convert CapletController to extend Controller base class - Use makeFacet() pattern for returning hardened exo methods - Add base-controller tests (12 tests) - Add semver deep import type declaration - Add storage permission to manifest.json Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Convert ControllerStorage from factory to class with static make() method - Implement synchronous update() with debounced fire-and-forget persistence - Fix critical debounce bug: accumulate modified keys across debounce window - Implement bounded latency (timer not reset, max delay = one debounce interval) - Add immediate writes when idle > debounceMs for better responsiveness - Add clear() and clearState() methods to reset storage to defaults - Remove old namespaced-storage implementation - Refactor all tests to use actual ControllerStorage with mock adapters - Add shared makeMockStorageAdapter() utility in test/utils.ts - Update controllers to create their own storage from adapters Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove strict reverse DNS format requirement for CapletId to allow more flexibility during early development. Now accepts any non-empty ASCII string without whitespace, removing restrictions on hyphens, underscores, uppercase, and segment count. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
d014ffd to
1ab49c9
Compare
- Add afterEach to restore real timers in caplet-controller tests - Use fake timers consistently in controller-storage tests for deterministic debounce testing - Replace setTimeout patterns with vi.runAllTimersAsync() for non-flaky tests - Add error handling tests for Chrome Storage adapter (get, set, delete, keys) - Add concurrent uninstall test to verify race condition handling - Document that facet methods are async for E() compatibility - Document Chrome Storage keys() limitation (loads all data into memory) Co-Authored-By: Claude <noreply@anthropic.com>
| }> { | ||
| const storageAdapter = makeChromeStorageAdapter(); | ||
|
|
||
| const capletController = await CapletController.make( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caplet controller construction is simplified later in the stack.
| /** | ||
| * Returns the hardened exo with public methods. | ||
| * Subclasses implement this to define their public interface. | ||
| * | ||
| * @returns A hardened exo object with the controller's public methods. | ||
| */ | ||
| abstract makeFacet(): Methods; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm probably going to get rid of this conceit in a follow-up PR, but would prefer to avoid for now to not complicate merge train.
| logger.injectStream(stream as unknown as DuplexStream<LogMessage>); | ||
|
|
||
| await waitUntilQuiescent(1000); | ||
| await waitUntilQuiescent(10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incidental change after the root test:dev was failing due to these timeouts being needlessly long.
- Replace test data constants with factory functions for better isolation - Make logger factory recreate fresh mocks for each test - Make manifest factory provide consistent test data - Remove facet.ts and facet.test.ts - inline facet logic into controllers - Simplify controller storage API and type definitions - Remove unnecessary storage adapter abstractions - Reduce controller-storage test complexity - Consolidate controller tests with better setup patterns - Update caplet controller to use simplified storage API Co-Authored-By: Claude <noreply@anthropic.com>
|
CI currently failing due to an npm outage. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Adds an actual caplet implementation, the echo caplet, and enables installing it and retrieving a reference to its root object from the extension's background console. The returned object is not actually a usable presence, but we implement this in #754. In detail: - Adds echo caplet - Store and retrieve caplet root krefs from the kernel API - Add caplet manifest loading and installation - Return subcluster id, root kref, and bootstrap result from `launchSubcluster()` - Add `omnium.caplet.load()` for dynamic caplet loading **Part 3 of 4 in PR stack** Depends on: #752 Followed by: #754 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces caplet plumbing and updates kernel launch semantics to return identifiers needed to reference caplet roots. > > - **Kernel API**: `launchSubcluster()` now returns `SubclusterLaunchResult` `{ subclusterId, bootstrapRootKref, bootstrapResult }`; `SubclusterManager` and `Kernel` updated; types exported > - **Kernel browser facade/CapTP**: facade `launchSubcluster` now returns `{ subclusterId, rootKref }`; added `getVatRoot(krefString)`; CapTP/RPC handler/spec updated to return structured result and normalize `bootstrapResult` undefined→null > - **Omnium (extension)**: added **Echo caplet** (bundle + manifest) and static copy in build; background exposes `omnium.caplet.load()`; CapletController now stores `rootKref`, supports `getCapletRoot()`, and uses kernel facade `launchSubcluster`/`getVatRoot`; integration tests added > - **Tests/fixtures**: updated across kernel, browser-runtime, node helpers, and persistence/tests to consume new launch result and root krefs > - **Tooling**: ESLint allows caplet JS under `src/**/caplets`; build script bundles caplets > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit bc6e395. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Introduces "controllers" to Omnium. A controller is an object (class) responsible for a slice of application state. We inherit this nomenclature from MetaMask.
In detail:
makeFacet()makeFacet()is tentative, and may be removed later in the stack in favor of just public and private methods.ControllerStorageabstraction withimmer-based state management and debounced persistenceControllerabstract base classCapletControllerfor managing installed capletsPart 2 of 4 in PR stack
Depends on: #751
Followed by: #753
Note
Introduces a modular controller layer and integrates it into the extension background, enabling managed, persisted state and caplet lifecycle operations.
Controller,ControllerStorage(immer-based with debounced, per-key persistence), and a Chrome storage adapterCapletControllerwithinstall/uninstall/list/get, manifest validation, and kernel subcluster launch/terminate viainitializeControllersomnium(E,getKernel,ping) and exposesomnium.capletmethods; adds error propagation to offscreen streamPromisifiedtype; caplet types/guards with superstruct and semverstoragepermission in manifest, and comprehensive unit/e2e testsWritten by Cursor Bugbot for commit 0e330b6. This will update automatically on new commits. Configure here.