Chat providers

A chat provider runs the turns of an agent chat that Terminay owns, such as a conversation with Pixel. Terminay keeps the conversation, mints the MCP capability, and shows approvals; the provider starts the user's installed agent and streams what it says. Added in API 3.2.

Manifest contribution

JSON
"api": "^3.2.0",
"permissions": ["agent-chat"],
"contributes": {
  "agentChatProviders": [{
    "id": "com.example.agent/chat",
    "displayName": "Example Agent",
    "description": "Chats through the Example Agent CLI.",
    "platforms": ["darwin", "linux"]
  }]
}

Declare contributes.agentChatProviders with a namespaced id, a displayName, and optional description and platforms. It requires the agent-chat permission. A manifest may declare up to 8.

Runtime contract

Register during activation with context.agents.registerChatProvider(id, runtime).

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

export default defineExtension({
  activate(context) {
    context.subscriptions.add(
      context.agents.registerChatProvider('com.example.agent/chat', {
        async availability(signal) {
          const cli = await findExampleCli(signal);
          if (!cli)
            return {
              available: false,
              reason: 'not_installed',
              detail: 'npm install -g example-agent',
            };
          return { available: true };
        },
        async openSession(request, events, signal) {
          // Start the user's installed CLI in request.workingDirectory, with
          // request.mcpServer as its only MCP server, and stream its output.
          const agent = await startExampleAgent(request, signal);
          agent.on('text', (delta) => events.text(delta));
          agent.on('tool', (call) =>
            events.tool({ callId: call.id, server: call.server, name: call.name, state: call.state }),
          );
          agent.on('exit', (code, sig) => events.exit({ code, signal: sig }));
          return {
            prompt: (text, turnSignal) => agent.runTurn(text, turnSignal),
            cancel: () => agent.interrupt(),
            close: () => agent.kill(),
          };
        },
      }),
    );
  },
});
MemberContract
availability(signal)Returns { available: true } or { available: false, reason, detail? }, where reason is not_installed, not_signed_in, or unsupported_platform. Put the command that fixes it in detail.
openSession(request, events, signal)Starts one session and resolves to { prompt, cancel, close }.
session.prompt(text, signal)Runs one turn and resolves when the agent finishes or cancel() stops it.
session.close()Ends the session and its process.
Request memberMeaning
sessionIdOpaque, at most 128 characters.
workingDirectoryAn empty, private directory for this session.
instruction?System-level instruction for the agent, at most 8,192 characters.
mcpServerThe one MCP server the agent may use (name, command, args, env), or null when the chat has no MCP reach.

Events

EventReport
events.text(delta)Reply text as it streams, at most 64 KiB per delta.
events.tool({ callId, server?, name, state })A tool call by name only, with state running, completed, or failed. Never arguments or results.
events.failure({ reason, detail? })not_installed, not_signed_in, crashed, or failed.
events.exit({ code, signal })The agent process ended.

Confinement

A provider must confine its agent to mcpServer:

  • Turn built-in tools off where the agent allows it, and decline every request for any other tool.
  • Load no other MCP server and no user MCP configuration.
  • Keep no session history on disk, and write nothing to the agent's own configuration, settings, hooks, or credentials.
  • Start the user's installed agent. Never download one.

A provider cannot read or change the conversation, the capability, or the approvals Terminay shows.

Testing

createAgentExtensionHarness drives a provider with chatAvailability(providerId) and openChatSession(providerId, request?), which fills the request from fixtureChatSessionRequest. The returned session records its validated events(), any violations(), and the reply text(). See Testing and conformance.