Extension anatomy
A Terminay extension is one npm package, one immutable extension ID, one ESM entrypoint, and at least one declared contribution. The manifest describes its public contract before code runs.
The manifest lives in package.json
"terminay": {
"manifestVersion": 1,
"id": "com.example.my-agent",
"displayName": "My agent",
"api": "^1.2.0",
"engines": { "terminay": ">=1.0.0", "node": ">=24" },
"entrypoint": "dist/index.js",
"permissions": ["agent-observation"],
"contributes": { "agentProviders": [/* … */] }
}| Field | Why it matters |
|---|---|
id | Immutable extension identity. Package name and extension ID are intentionally separate. |
api | The supported Terminay Extension API range. A major version is breaking; a minor is additive. |
engines | Required Terminay and Node versions. |
entrypoint | A relative ESM file included in the packed package. |
permissions | Explicit host capabilities, such as agent-observation. |
contributes | One or more project-environment or agent-provider declarations. |
Activation is small and explicit
Export defineExtension({ activate(context) { … } }) from the entrypoint.
The context provides your extension ID, negotiated API version, private configuration/data/cache
paths, a disposable subscription bag, and the two registration surfaces.
| Context surface | Use it for |
|---|---|
context.agents.registerProvider() | Register the runtime for a declared agent provider. |
context.registerProjectEnvironmentProvider() | Register a declared project-environment definition and runtime. |
context.subscriptions | Give Terminay disposables to clean up during deactivation. |
context.paths | Find the package’s own configuration, data, and cache directories. |
Contributions
Agent providers
An agent provider matches a foreground process, receives a terminal-scoped observation
context, and returns unavailable, not-bound, or a bound session. It
can observe bounded process and file facts, follow an agent journal, and publish normalized
lifecycle events. It cannot control the terminal or mutate Terminay’s agent store directly.
Project-environment providers
An environment provider declares host-rendered forms and environment capabilities, then implements the runtime methods that validate profiles, create or resume an environment, report its status, and run declared actions.
export default defineExtension({
activate(context) {
context.registerProjectEnvironmentProvider({
definition: {
providerId: 'com.example.host/ssh',
displayName: 'Example host',
capabilities: ['terminal', 'filesystem'],
},
runtime: {
async testProfile() { return []; },
async resolveOptions() { return { options: [] }; },
async createEnvironment(request) { /* return ready or pending */ },
async resumeOperation(request) { /* resume a pending operation */ },
async getStatus(request) { /* return current status */ },
async invokeAction(request) { /* return complete or pending */ },
},
});
},
});Provider calls include a deadline, cancellation signal, and (for mutations) idempotency and revision information. Profiles, secrets, SSH signing, and extension dependencies are only available through scoped brokers on that call context.
Keep the boundary clear
- Extensions can use public Node modules and declared npm dependencies.
- They cannot import private Terminay packages, Electron APIs, or host bridges.
- They cannot add custom renderer components, routes, themes, menus, or raw terminal access.
- They run as trusted server-side code, so administrators review permissions before installing them.