Project environments

A project-environment provider creates and operates one kind of environment for a Terminay project. The host renders its declarative forms and calls its typed runtime; extension code never renders UI directly.

Manifest contribution

Declare the provider in contributes.projectEnvironments, then register its runtime during activation.

FieldMeaning
idProvider identity, namespaced by the immutable extension ID.
displayName, description, iconHost-rendered provider metadata. Icons are limited to Terminay’s named icon allowlist.
capabilitiesEnvironment services this provider makes available; listed below.
profileSaveOptional { createEnvironment: true }. Without it, saving a profile does not create an environment.
dependencyOperationsOptional allowlist of public operation names that compatible extension dependencies may call.
CapabilityEnvironment service
terminalTerminal access routed through this environment.
filesystem, filesystem-observationFilesystem access or bounded filesystem observation.
gitGit integration.
process-observation, agent-journalBounded process facts and agent-journal observation.
mcp-bridge, infrastructure, shell-discoveryMCP bridging, infrastructure operations, and shell discovery.

Register a provider

TypeScript
import { defineExtension } from '@terminay/extension-api';

export default defineExtension({
  activate(context) {
    context.registerProjectEnvironmentProvider({
      definition: {
        providerId: 'acme-cloud',
        displayName: 'Acme Cloud',
        capabilities: ['terminal', 'filesystem', 'agent-journal'],
      },
      runtime: {
        async testProfile({ values }) {
          return values.token ? [] : [{
            fieldId: 'token', code: 'required', message: 'Add an API token.',
          }];
        },
        async resolveOptions() { return { options: [] }; },
        async createEnvironment(request) {
          return {
            state: 'ready',
            providerState: { instanceId: request.environmentId },
            status: { state: 'available', revision: 1 },
          };
        },
        async resumeOperation() { throw new Error('No pending operations.'); },
        async getStatus({ providerState }) {
          return { state: 'available', revision: 1, card: {
            id: 'instance', title: 'Acme Cloud', summary: String(providerState),
          }};
        },
        async invokeAction(request) {
          return { state: 'complete', providerState: request.providerState,
            status: { state: 'available', revision: 1 } };
        },
      },
    });
  },
});

registerProjectEnvironmentProvider() receives a definition, a required runtime, and optionally dependencyOperations: a handler for the operations declared in the manifest.

Provider definitionType
providerId, displayName, description?, icon?Provider identity and host-rendered metadata.
capabilitiesRequired EnvironmentCapability[]; it must match the declared provider.
profileForm?, createForm?Optional declarative forms for saved profiles and environment creation.

Declarative forms and presentation

A form has id, title, optional description, ordered sections, and submitLabel. A section has id, title, optional description, disclosure (always, expanded, or collapsed), and fields.

Field typeAdditional fields
All fieldsid, label, optional description, required, disabled reason, default value, and visibleWhen (equals / notEquals).
text, url, secret, textareaPlaceholder, length bounds, pattern, or an asynchronous suggestion source and label.
numberMinimum, maximum, and step.
checkbox, switchBoolean controls.
selectStatic options or optionSource, searchable and multiple selection flags.
preset-cardsStatic options with an optional allowed icon, or optionSource.

Runtime responses may provide field ValidationIssues, status cards, guarded HTTPS links, actions (including an ordinary or destructive confirmation), and resumable progress stages.

Runtime methods

MethodRequest and result
testProfile{ profileId?, values }ValidationIssue[].
resolveOptions{ sourceId, profileId?, query?, cursor?, values } → select options and optional next cursor.
createEnvironment{ environmentId, displayName, profileId?, values } → ready status or a pending resumable operation.
resumeOperationEnvironment state and operationId → ready status or a still-pending operation.
getStatus{ environmentId, profileId?, providerState } → availability, message, root, card/progress, and revision.
invokeActionEnvironment state, actionId, and optional values → complete or pending action result.
invokeService?Optional, environment-bound call with one declared capability, operation, project ID, revision, and JSON input.
updateEnvironment?Optional environment state plus new values → complete or pending action result.
deleteEnvironment?Optional environment state → complete or pending action result.

Every method receives ProviderCallContext: an absolute deadlineAt, cancellation signal, retry-stable idempotencyKey?, optimistic expectedRevision?, and scoped profiles, secrets, SSH-agent, and dependency brokers. See scoped brokers.

Boundaries

Provider output is declarative and bounded: no custom React, HTML, CSS, routes, or raw application-protocol handlers. The server owns project bindings, environment revisions, confirmation, cancellation, and lifecycle policy.