Skip to main content

The extension for your app

The extension is the surface that needs no change to the page it assists. That makes it the right one when you cannot embed a widget: an application you do not own, an internal tool nobody wants to redeploy, or a build you are still developing. Everything the widget can do the extension can do, the same configuration serves both, and a page that registers client actions is operated by either one.

This page is for the developer who wants the extension pointed at their own application. For rolling it out to a whole organization, see Enterprise deployment.

Install the build​

There is no Chrome Web Store listing while the beta is closed, so the extension is installed by hand:

  1. Download the ZIP from appilot.space/downloads.
  2. Unpack it somewhere permanent. Chrome loads it from that folder every time it starts, so a folder in ~/Downloads that you later clear takes the extension with it.
  3. Open chrome://extensions, turn on Developer mode, choose Load unpacked, and select the unpacked folder.

The build talks to the Appilot cloud. There is no auto-update: when a new version ships, the backend answers 426 and the panel says so, and you repeat these steps with the new ZIP.

Sign in​

Open the panel and choose to sign in. The extension opens its own page inside the extension origin, and from there it sends you to My Account to sign in with whatever your account already uses: a password, Google, or your organization's SSO. You confirm the handoff there and the session travels back to the extension. The extension asks for no credentials of its own, so a host page never sees one and there is no second set of sign-in methods to keep in step.

One thing still trips people up: your account needs an active membership. An account with no organization, or one whose join request is still pending, signs in and gets nowhere: the turn is refused because there is no tenant to run it in. Create an organization or accept the invitation in My Account first.

How the extension knows which app it is on​

The extension does not carry an app id. On every page it asks the backend which tenant the page's hostname belongs to.

  • The hostname is matched without the port, so myapp.lvh.me:3000 and myapp.lvh.me:5173 are the same domain.
  • A hostname belongs to exactly one organization. If another organization registered it first, your users are refused with TENANT_DOMAIN_MISMATCH when they send a turn, and the panel says the site belongs to a different organization.
  • On a hostname nobody registered, the turn still runs. It is grounded in the page the user is on and in your organization's own knowledge, with no views, controls, forms or action plans behind it. The panel calls this live page mode.

So the whole of "make the extension use my configuration" is: register the hostname as a domain of your app, and be a member of the organization that owns it.

Verification is not part of this. The extension serves an unverified domain; the TXT record gates the live widget key and the site adapter. See Verify your domain.

On your own machine​

Register a hostname that resolves to 127.0.0.1, such as myapp.lvh.me, and serve your dev build on it. Bare localhost cannot be registered as a domain, and a test widget key does nothing for the extension, which authenticates as the user rather than as a key. The full contract is in Develop on localhost.

Client actions with no widget​

A page that registers client actions is operable by the extension with no widget present. The extension reads the page's registry from a main-world bridge at the start of each turn and invokes the handlers through the same channel:

import { registerTool } from 'appilot';

registerTool({
name: 'open_invoice',
description: 'Open an invoice by number.',
inputSchema: { type: 'object', properties: { number: { type: 'string' } } },
annotations: { readOnlyHint: true },
execute: ({ number }) => { router.push(`/invoices/${number}`); },
});

The page must load the appilot runtime for the registry to exist. Everything else, including the confirmation rule for mutating actions, is identical to the widget. See Client actions.

Focused Sessions work the same way: startFocusedSession from a page under the extension reaches the extension's relay provider, so an app-initiated, mission-scoped conversation runs with no widget embedded. See Focused Sessions.

The iteration loop​

The extension reads the domain and the app configuration per page load, and the content model per turn. So the loop is:

  1. Change the configuration, in the Backoffice or through your MCP client.
  2. Reload the page.
  3. Ask.

There is no cache to clear on the extension side. Changing the extension itself (rather than your configuration) does need Chrome to reload the unpacked extension from chrome://extensions.

What a turn can and cannot reach​

  • A turn belongs to the tab it started in. The tab is fixed when the user sends, and every read and action goes there, so switching tabs while the assistant works does not redirect it. A tab that was closed or navigated to another origin refuses the read.
  • Credential fields are never read or written. Passwords, one-time codes, payment-card fields and hidden inputs are excluded from every reader on both surfaces, with no exemption by caller.
  • The extension does not mount on Appilot's own hosts (the public site, this documentation, downloads, My Account and the Backoffice), and a user can pause it per site from the context menu.
  • Where a page embeds the widget, the extension stands aside so the user never sees two assistants. The page announces the widget with a data-appilot-widget attribute on <html>; the extension announces itself with data-appilot-extension, which your page can read to detect it.

Testing​

Drive the built extension from a real browser rather than a jsdom test: the content script, the isolated world and the main-world bridge are the parts most worth checking, and none of them exists outside Chrome. Load the unpacked build in a browser your coding agent controls (the Chrome DevTools MCP server is the practical route), open your page on the registered hostname, and run a turn.