Developer Guide
Workflow
Develop against a folder on your machine, reload without restarting, then submit the same files as a marketplace draft.
Folder layout
A workspace is any folder with one subfolder per extension. Each subfolder holds manifest.json and one JS module.
my-extensions/ <- the workspace folder you pick in the app
hello-extension/ <- folder name must equal manifest.key
manifest.json
hello-extension.js <- exactly one .js file
another-extension/
manifest.json
another-extension.js{
"apiVersion": 7,
"version": "1.0.0",
"key": "hello-extension",
"name": "Hello Extension",
"category": "integration",
"icon": "Sparkles",
"description": "Adds one browser-side tool."
}The JSON manifest and the module's export const manifest must agree on the shared fields. See manifest.json and the module export.
Dev Extensions
- 1In the app open Settings → Advanced and turn on Developer Mode and Show developer features. Dev Extensions appears in the sidebar.
- 2On Dev Extensions, pick your workspace folder. Chromium browsers and the desktop app use the folder picker and keep read access between visits. Other browsers upload the folder instead and need a re-upload after edits.
- 3Press Scan workspace. Each subfolder shows green badges when installable or red blockers when not.
- 4Press Install to All Personas on a card. One dev extension occupies the dev slot at a time, and the server refuses a second install until you unload the first. A dev build is enabled for every persona while installed, whatever you pick in the persona menu.
- 5Edit your files, then press Reload on the card or the reload button in the top bar. The card flags when the source on disk differs from what is loaded.
- 6Unload clears the slot. Saved variables stay until you delete them.
- 7Edit settings from the extension's own detail page. Quick-layout edits made in the top bar, footer, or panel do not persist for a dev build.
Scan rules
The card scanner runs in the browser and shows blockers in red. The install request is checked again on the server, which rejects a few more things.
Card blockers
No manifest.json, or it is not a JSON object.
manifest.json missing apiVersion, key, name, category, or description.
Zero or more than one .js file in the folder.
Folder name differs from manifest.key.
The JS file does not contain the text export const manifest.
Server rejections
Source contains eval(), new Function(), document.write(), or an assignment to innerHTML, outerHTML, or dangerouslySetInnerHTML. The card shows these as warnings, but the install fails.
Key is not 3 to 100 lowercase letters, digits, and hyphens.
Key belongs to a built-in extension, to another author's marketplace extension, or to a non-dev extension you already have installed.
The card also warns when manifest.json has neither settings nor a tools array. The warning is harmless; the runtime reads tools from getTools(), not the JSON.
Loading a single file
The Extensions page also has a Load Extension button that takes one .js file and shows its SHA-256 before install. The file name without the extension becomes the install key, so name it after your manifest key. It does not support reload; use Dev Extensions for iteration.
Debugging
Install the built-in Devtools extension from the Extensions page and enable it for the persona you are testing. It adds a chat panel with Timeline, Tools, Hooks, Prompt, Registry, Extensions, Logs, and Invoke tabs: every tool call and its result, lifecycle hook timings and failures, the prompt contributions collected each turn, tool-name collisions, and a Logs tab fed by ctx.runtime.devtools. Invoke lets you call one of your tools by hand. Ordinary console.* output goes to the browser console as usual.
Marketplace drafts and review
Press Publish on a Dev Extensions card to open the submission form with the manifest and source filled in. Or open Marketplace → Submit and paste them. Each account may submit five times and create two new keys per 24 hours.
manifestJSONrequiredListing baseline. Only toy, utility, and integration categories are accepted.sourceJS textrequiredThe exact module the app will load. It is hashed on submit and the hash is verified on every load.content ratingsfw | nsfwrequiredDecides who can see the listing. Stored as a tag alongside yours.tagsstring[]Free-form discovery tags.name, description, long descriptiontextOverride the manifest values for the listing. Description up to 320 characters, long description up to 6000.version and changelogtextDefaults to manifest.version. Each submission creates a version.icon and screenshotsuploadsIcon image or preset with foreground and background colours. Up to three screenshots.Review flow
- 1A submission starts as a draft. Only you can see and install it, and the success screen gives you a share link that lets others install the draft directly.
- 2Press Request publish on the version when it is ready. Its status becomes pending review.
- 3Review ends in approved, changes requested, or rejected. Approved versions are public. Submitting a new version string adds a version. Submitting the same version string again only works while it is a draft, changes requested, or rejected, and the form asks you to confirm the replacement.
- 4Users on auto-update move to each newly approved version. Users on manual update see an Update available chip.
My Media
Marketplace → My Media stores audio and images with stable ids. Users paste an id into one of your settings fields; you resolve it with runtime.media.getUrl(id). Uploading needs admin approval, requested on Discord. Every upload is checked by extension, MIME type, and file signature.
.jpg500 KBImages..png100 KBImages..mp3 / .wav10 MBAudio.Security model
Extension code runs in the app's page with the app's privileges: storage, network, Web Bluetooth, Web Serial. The trust boundary is the author, not the install button.
What the app checks
Manifest shape and API version on load.
SHA-256 of the stored source on every load; a mismatch refuses to load.
Marketplace review before a version is public.
Media uploads by extension, MIME type, and signature.
What it does not do
Sandbox your code or limit which APIs it can reach.
Hide the user's session from you. Treat it with care.
Review dev-slot or single-file loads. Only load code you have read.