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.agent",
"displayName": "Example Agent",
"api": "^3.0.0",
"engines": { "terminay": ">=1.0.0", "node": ">=24" },
"entrypoint": "extension.js",
"permissions": ["agent-observation"],
"contributes": {
"agentSessionSources": [{
"id": "com.example.agent/sessions",
"displayName": "Example Agent",
"harnesses": [{ "id": "example-agent", "displayName": "Example Agent" }]
}]
}
}| Field | Why it matters |
|---|---|
id | Immutable extension identity. Package name and extension ID are 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 .js ESM file that the package also lists in exports. |
permissions | Explicit host capabilities, such as agent-observation. |
contributes | One or more of agentSessionSources, mcpInstallTargets, languageServers, and worktreeInsights. |
The manifest is data only. See the manifest reference for every field.
Activation is small and explicit
Default-export the value returned by defineExtension(). There is no global
Terminay object: everything the host grants arrives on context or on a
callback argument.
import { defineExtension } from '@terminay/extension-api';
import { exampleSessionSource } from './example-agent.js';
export default defineExtension({
activate(context) {
context.subscriptions.add(
context.agents.registerSessionSource(
'com.example.agent/sessions',
exampleSessionSource,
),
);
},
});| Context surface | Use it for |
|---|---|
context.agents.registerSessionSource() | Register the runtime for a declared agent session source. |
context.mcp.registerInstallTarget() | Register the runtime for a declared MCP install target. |
context.registerLanguageServerProvider() | Register the launch callback for a declared language server. |
context.worktrees.registerInsightSource() | Register the runtime for a declared worktree insight source. |
context.subscriptions | Give Terminay disposables to clean up when the activation ends. |
context.paths | Find the extension's own configuration, data, and cache directories. |
A registration is accepted only for an ID the manifest declares. An undeclared ID or a second registration of the same ID throws.
Contributions
Agent session sources
A session source reports every live coding-agent session of its harnesses on the server's machine, however the session was started. It publishes bounded snapshots. Terminay decides which project a session belongs to and which terminal, if any, it binds to. See Agent session sources.
MCP install targets
An install target owns one client's Terminay MCP registration. It reports status and writes or removes the host-supplied server command in that client's configuration file. See MCP install targets.
Language servers
A language server contribution declares the languages and file extensions it serves and returns a launch command for a project root. Terminay spawns the server and speaks the Language Server Protocol to it. See Language servers.
Worktree insight sources
An insight source receives the repositories of open projects and publishes a pull request and checks summary per worktree. Terminay renders them. See Worktree insights.
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 renderer components, routes, themes, menus, or raw terminal access.
- They run as trusted server-side code, so administrators review permissions before installing them.