Navigácia
ELOWEN · Dokumentácia

Posledná aktualizácia: 20. októbra 2026

Prechádzať dokumentáciu · Plugin decisions and verification

Tento článok zatiaľ nie je preložený. Je dostupný v angličtine.

Developer reference

Plugin decisions and verification

Decision table

I want to... Use this seam
Add model-callable domain behavior ctx.registerTool
Add progressive-disclosure instructions ctx.registerSkill
Add a slash macro ctx.registerCommand({ kind: "prompt" })
Open a local browser chooser web.chatPickers and a picker command
Add stable agent instructions ctx.registerText('system-prompt', text)
Add data that changes on every turn ctx.registerText('turn-context', fn, { placement: "after-user" })
Transform only explicit current-turn input ctx.registerText('input-transform', fn)
Add a reminder during a long tool loop ctx.registerText('step-context', fn)
Learn skills from a finished turn in the background ctx.registerSkillPostTurnReviewer
Observe or narrowly patch a wired lifecycle point ctx.registerHook
Expose a user-facing authenticated endpoint ctx.registerApiRoute
Receive an external callback ctx.registerHttpRoute
Rate-limit or audit a public plugin webhook by canonical network origin PluginHttpRequest.origin; authenticate the webhook separately
Time a retried request from the host's committed policy instead of a private default ctx.retryPolicy() and ctx.onRetryPolicyChanged(listener)
Start an account-scoped synthetic platform turn and observe live progress PlatformControlApi.relay(src, text, observer)
Stream bytes to a browser or integration ctx.registerWebSocketRoute plus issueWebSocketTicket
Publish or consume a domain capability ctx.registerControl with manifest provides.controls; ctx.control(key) with consumesControls and reads: ["controls"]
Add a Project row indicator registerSurfaceFact('projectIndicators', fn) or web.projectRows
Add fields to Brain status registerSurfaceFact('brainStatus', fn)
Add cleanup or periodic work registerBootReconcile, registerService, or registerInterval
Render a plugin card, rail section, metric, or history branch web.chatCards, web.chatRailSections, web.dashboardMetrics, or web.historyBranches
Keep something of the conversation's Project visible in its chat web.chatDock
Persist domain data await ctx.db() with db.migrate(...), the entry module's migrate(db) export, and ctx.dataDir()
Use a core security boundary The matching ctx.host accessor, never a copied ACL/path/process implementation

If the value changes every turn, it belongs in turn context with placement: "after-user". It never belongs in the system prompt, because the system prompt is the cached prefix. Every provider request still pays for the volatile text as input; system-prompt bytes are paid again only after a cold or invalidated prefix. Host-only details such as toolTrace and ownRow do not cost model tokens.

Where the seams are implemented

  • The PluginContext interface, with the signatures and rules of every register* method, is in src/plugins/api.ts. Platform relays use the PlatformControlApi interface in the same file (line 1250).
  • The implementations live in src/plugins/pluginContext.ts, in the object that builds each plugin's context. Tools, platforms and HTTP, API and WebSocket routes are checked against the manifest first: an undeclared one is refused with a warning. Commands are checked for their name, collisions and prompt content instead.
  • Manifest validation for web slots, such as chatDock and chatCards, is in src/plugins/manifest.ts; the contributions reach the browser through src/plugins/pluginWebUi.ts.
  • The rows of the seam catalog are in Seam catalog. tests/contract/pluginSeamCatalog.test.ts fails when a new register* method or web slot has no catalog row.

Local development and verification

For a bundled plugin, install dependencies in the Elowen checkout and run the normal plugin build:

npm ci
npm run build:plugins-web
npm run build

The web build is needed when a plugin has browser sources. The main build compiles the daemon, builds browser bundles, and copies bundled plugins into the distribution tree. npm run build compiles the daemon and discovered TypeScript plugin projects, builds browser and desktop guest bundles, then copies complete plugins and assets into an empty sibling candidate. Executable-bit and integrity checks run before that candidate replaces dist/; a plugin folder without its manifest or a missing or orphaned output fails the build and preserves the previous distribution. See Development for the publication seam and its two-rename interval. A plugin developed outside the catalog builds and tests against a local core checkout: depend on the host packages with file: dependencies (elowen, elowen-plugin-shared, elowen-plugin-ui-kit) instead of the published versions, then run the host contract checks.

Host-lent SDKs

A registry plugin does not need to vendor an SDK the host already ships. After every install and update, the marketplace installer symlinks the plugin's own node_modules to the host's (linkHostModules in src/plugins/marketplace.ts), so the plugin's bare imports resolve against the host's copy at runtime. That is why grammy (Telegram), baileys and qrcode (WhatsApp), botframework-connector (Teams), and puppeteer-core and proxy-chain (Browser) have zero importers anywhere in this checkout: the host carries the dependency so it can be lent, not because it is dead. They are excluded from the dead-dependency sweep for the same reason, in knip.json's ignoreDependencies; do not remove one from package.json without first checking whether an installed registry plugin still imports it.

node-html-parser (Parts: the CoraHB e-shop and PartsLink24 HTML parsing) is lent the same way, but the bundled Sandbox guest also imports it, so it is not in ignoreDependencies. It must stay in dependencies, not devDependencies, or a production install does not carry it and the plugin cannot import it.

Use focused checks first:

npx vitest run tests/plugins
npx vitest run tests/api/pluginUiRoutes.test.ts
npx vitest run tests/plugins/marketplace.test.ts
npx vitest run tests/contract/pluginApiSubpath.test.ts
npx vitest run tests/contract/pluginSharedPackage.test.ts
npx vitest run tests/contract/pluginBundleContracts.test.ts
npx vitest run tests/contract/pluginSeamCatalog.test.ts
npm run check
npm test

For a manifest change, verify that the manifest parses, every declared entry exists, every declared browser entry and stylesheet exists, and the runtime contribution report matches the intended registration. For a route or grant change, run the focused API and plugin-grant tests. For a hook change, run the hook integration tests. Inspect the plugin log after a restart when a plugin is skipped.

Before publishing, test the exact built tree, not only source files. Do not commit generated output unless the owning repository requires it.