Extension manifest
The manifest is the closed terminay object in your package's package.json.
Terminay validates it before importing your extension. Its type is TerminayExtensionManifest.
Example
{
"name": "terminay-example-session-source",
"version": "1.0.0",
"type": "module",
"exports": { ".": "./extension.js" },
"peerDependencies": { "@terminay/extension-api": "^3.0.0" },
"terminay": {
"manifestVersion": 1,
"id": "com.example.agent",
"displayName": "Example Agent",
"description": "Shows Example Agent sessions in Terminay and registers the Terminay MCP server with it.",
"api": "^3.0.0",
"engines": { "terminay": ">=1.0.0", "node": ">=24" },
"entrypoint": "extension.js",
"platforms": ["darwin", "linux"],
"permissions": ["agent-observation", "mcp-registration"],
"contributes": {
"agentSessionSources": [{
"id": "com.example.agent/sessions",
"displayName": "Example Agent",
"harnesses": [{ "id": "example-agent", "displayName": "Example Agent" }],
"environmentVariables": ["EXAMPLE_AGENT_HOME"]
}],
"mcpInstallTargets": [{
"id": "com.example.agent/example-agent",
"displayName": "Example Agent"
}]
}
}
} name, version, and an exports entry for the entrypoint
are package requirements. Package name and extension id are deliberately separate.
Top-level fields
| Field | Required | Meaning |
|---|---|---|
manifestVersion | Yes | Manifest format. Use 1. |
id | Yes | Immutable extension identity: lowercase letters, digits, dots, and hyphens, starting and ending with a letter or digit, at most 128 characters. |
displayName | Yes | Human-readable name, at most 96 characters. |
description | No | Short description shown by Terminay, at most 1,024 characters. |
api | Yes | Extension API range. See range syntax. |
engines | Yes | Exactly terminay and node ranges. |
entrypoint | Yes | Relative, forward-slash path ending in .js, with no .. segment. It must also appear in the package's exports. |
platforms | No | Non-empty list of darwin, linux, or win32. |
permissions | Yes | Requested host capabilities, possibly empty. See Permissions. |
extensionDependencies | No | Up to 32 other extensions: extensionId, apiRange, and optional boolean optional. |
contributes | Yes | The contribution arrays below. At least one is required, and a declared array cannot be empty. |
contributes.agentSessionSources
Each item declares a machine-wide agent session source. Declaring one requires the agent-observation permission.
| Field | Meaning |
|---|---|
id | Required. <extensionId>/<local-id>, where the local ID is lowercase kebab-case. |
displayName | Required label. |
description, platforms | Optional metadata and OS restriction. |
harnesses | Required. One to 16 objects with a unique kebab-case id and a displayName. Each is one agent the source can report, with its own switch in Settings. |
environmentVariables | Optional. Up to 32 server environment variable names the extension process should receive when they are set. |
contributes.mcpInstallTargets
Each item declares one client the Terminay MCP server can be registered with. Declaring one requires the mcp-registration permission. At most 16.
| Field | Meaning |
|---|---|
id | Required. Namespaced by the extension ID. |
displayName | Required. The client's name as the install surface shows it. |
contributes.languageServers
Each item declares one language server. No permission is required. At most 8.
| Field | Meaning |
|---|---|
id | Required. Extension-local kebab-case ID such as typescript. It is not namespaced. |
displayName | Required label. |
description | Optional. |
languageIds | Required. One to 32 unique language IDs such as typescriptreact. |
fileExtensions | Required. One to 32 unique lowercase extensions with the leading dot, such as .tsx. |
runtimeNotes | Optional text shown in Settings. |
contributes.worktreeInsights
Each item declares one worktree insight source. Declaring one requires the worktree-observation permission and API ^3.1.0. At most 8.
| Field | Meaning |
|---|---|
id | Required. Namespaced by the extension ID. |
displayName | Required label. |
description | Optional. |
Validation
validateExtensionManifest(value) returns either the manifest or a list of
issues, each with a path, code, and message.
parseExtensionManifest(value) throws ExtensionSchemaError instead.
| Code | Cause |
|---|---|
unknown_field | A field the schema does not define, at any level. |
unsupported_version | manifestVersion is not 1. |
invalid_id, invalid_namespace | A malformed ID, or a contribution ID not prefixed by the extension ID. |
unsafe_entrypoint | An absolute, escaping, backslash, or non-.js entrypoint. |
unknown_permission, missing_permission | An unrecognized permission, or a contribution without the permission it requires. |
missing_contribution | contributes declares no supported array. |
duplicate | A repeated permission, contribution ID, harness ID, or dependency. |
invalid_language_id, invalid_file_extension | A malformed language server selector. |
missing_exports, entrypoint_not_exported | The package does not export the entrypoint. |
Before import, the host also rejects an entrypoint that is a symbolic link or not a regular file inside the package.