-
Notifications
You must be signed in to change notification settings - Fork 7
feat(omnium): Add caplet vat implementation #753
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
base: main
Are you sure you want to change the base?
Conversation
451a6dd to
605f8d9
Compare
6736065 to
89e5113
Compare
605f8d9 to
f08383c
Compare
f08383c to
34a1f08
Compare
89e5113 to
ee93a06
Compare
34a1f08 to
4caf528
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>
d014ffd to
1ab49c9
Compare
9ab3158 to
84ed1b2
Compare
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: - Add modular controller architecture with POLA attenuation via `makeFacet()` - `makeFacet()` is tentative, and may be removed later in the stack in favor of just public and private methods. - Add `ControllerStorage` abstraction with `immer`-based state management and debounced persistence - Add platform storage adapters and Chrome-specific implementation - Add `Controller` abstract base class - Add `CapletController` for managing installed caplets **Part 2 of 4 in PR stack** Depends on: #751 Followed by: #753 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces a modular controller layer and integrates it into the extension background, enabling managed, persisted state and caplet lifecycle operations. > > - **Controller framework:** Adds abstract `Controller`, `ControllerStorage` (immer-based with debounced, per-key persistence), and a Chrome storage adapter > - **Caplet management:** New `CapletController` with `install/uninstall/list/get`, manifest validation, and kernel subcluster launch/terminate via `initializeControllers` > - **Background integration:** Refactors globals to `omnium` (`E`, `getKernel`, `ping`) and exposes `omnium.caplet` methods; adds error propagation to offscreen stream > - **Types/utilities:** Exports `Promisified` type; caplet types/guards with superstruct and semver > - **Config/tests:** Adds deps (exo, superstruct, immer, semver), grants `storage` permission in manifest, and comprehensive unit/e2e tests > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 0e330b6. 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>
Implements Phase 1a of the caplet system, establishing the foundational architecture for caplet vats with a working echo-caplet example. This validates the caplet vat contract and installation lifecycle before tackling service injection complexity. Changes: - Add comprehensive caplet vat contract documentation - Create echo-caplet.js demonstrating buildRootObject pattern - Add bundle build script using @endo/bundle-source - Implement caplet integration tests (8 new tests, all passing) - Create test fixtures for caplet manifests - Refactor makeMockStorageAdapter to support shared storage - Add plan in .claude/plans for follow-up work Key achievements: - Caplet vat contract fully documented with examples - Echo-caplet bundles successfully (696KB) - Install/uninstall lifecycle tested and working - Service lookup by name validated - State persistence across controller restarts verified - 100% code coverage for CapletController maintained Deferred to future work (Phase 1b): - Kref capture mechanism - Service parameter injection - Consumer caplet implementation - Two-caplet communication Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements Phase 1b functionality to store caplet root kernel references (krefs) and expose them via omnium.caplet.getCapletRoot(). This enables: omnium.caplet.install(manifest), omnium.caplet.getCapletRoot(capletId), and E(presence).method() for calling vat methods from background console. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add explicit type annotation for kernelP and use spread operator for optional rootKref field. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove KrefWrapper type from kernel-browser-runtime types - Make rootKref a required string field in LaunchResult (not optional) - Make rootKref required in InstalledCaplet and omnium LaunchResult - Add assertions in kernel-facade for capData, subclusterId, and rootKref - Remove isKrefWrapper function (inline check kept in makeKrefTables) - Update tests to use simplified types and improved mocks Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests for validation errors in kernel-facade launchSubcluster: - Throws when kernel returns no capData - Throws when capData body has no subclusterId - Throws when capData slots is empty (no root kref) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add omnium.manifests.echo so users can install caplets from the console: await omnium.caplet.install(omnium.manifests.echo) Changes: - Create src/manifests.ts with echo caplet manifest using chrome.runtime.getURL - Add echo-caplet.bundle to vite static copy targets - Expose manifests in background.ts via omnium.manifests - Update global.d.ts with manifests type and missing getCapletRoot Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add omnium.loadCaplet(id) to dynamically fetch caplet manifest and bundle - Fix vatPowers.logger missing in browser vats (iframe.ts) - Fix SubclusterLaunchResult to return bootstrapRootKref directly instead of trying to extract it from bootstrap() return slots The bootstrapRootKref is the kref of the vat root object, which is already known when the vat launches. Previously we incorrectly tried to get it from the slots of the bootstrap() method return value. Next step: Wire up CapTP marshalling so E(root).echo() works with the caplet root presence. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use nullable() instead of optional() for bootstrapResult field, and define a JSON-compatible LaunchSubclusterRpcResult type that uses null instead of undefined for JSON serialization. Also update tests to match the new behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
84ed1b2 to
675ff3e
Compare
Coverage Report
File Coverage |
Add comprehensive unit test coverage for the getCapletRoot method: - Test successful retrieval of caplet root and getVatRoot call verification - Test error case when caplet not found - Test error case when caplet has no root object (empty rootKref) Co-Authored-By: Claude <noreply@anthropic.com>
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 3 potential issues.
| uninstall: async (capletId: string) => | ||
| E(capletController).uninstall(capletId), | ||
| list: async () => E(capletController).list(), | ||
| load: loadCaplet, |
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.
loadCaplet exposed at wrong path in implementation
High Severity
The loadCaplet function is exposed as omnium.caplet.load but the type definition declares it as omnium.loadCaplet. This mismatch causes the documented API omnium.loadCaplet('echo') to fail at runtime with an undefined error, while only omnium.caplet.load('echo') would work. The function should be added as a top-level property of omnium, not nested under caplet.
| // Return wrapped kref for future CapTP marshalling to presence | ||
| // TODO: Enable custom CapTP marshalling tables to convert this to a presence | ||
| return { kref: krefString }; | ||
| }, |
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.
getCapletRoot returns plain object not CapTP presence
High Severity
The getCapletRoot method promises to return a CapTP presence (per JSDoc at line 298) that can be invoked with E(), but the implementation returns a plain object { kref: string } instead. The getVatRoot method in kernel-facade returns this plain object with a TODO comment indicating the feature is incomplete. This means calling methods on the returned "presence" like E(await omnium.caplet.getCapletRoot('echo')).echo('hello') fails because the object isn't actually a CapTP presence.
Additional Locations (1)
| throw Error(`this can't happen but eslint is stupid`); | ||
| } | ||
| return kunser(bootstrapResultRaw); | ||
| return bootstrapResult && kunser(bootstrapResult); |
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.
Helper silently returns undefined instead of throwing error
Medium Severity
The runTestVats helper now returns undefined when bootstrapResult is undefined, whereas the previous implementation (and the kernel-test version) throws an error. This silently changes error handling behavior and could mask test failures when a bootstrap method unexpectedly returns nothing. Tests that previously would fail fast with an explicit error now receive undefined and may continue executing with incorrect state.
Part 3 of 4 in PR stack
Depends on: #752
Followed by: #754
Note
Introduces a structured launch result and initial caplet plumbing.
launchSubclusternow returnsSubclusterLaunchResultwithsubclusterId,bootstrapRootKref, andbootstrapResult;KernelandSubclusterManagerupdated accordingly with tests{ subclusterId, rootKref }; newgetVatRoot(krefString); CapTP kref marshalling tables added (currently disabled); integration/unit tests updatedlaunchSubclusterhandler/spec now return a JSON-safe object (bootstrapResultnullable)omnium.loadCaplet()to fetch manifest/bundle; CapletController storesrootKref, exposesgetCapletRoot; echo caplet vat + manifest and integration tests; Vite copies caplet assets; build addsbuild:vatsvatPowers.loggerto iframe vats; update NodeJS/test helpers and types for new APIWritten by Cursor Bugbot for commit d5bfd65. This will update automatically on new commits. Configure here.