Providers and models
A provider is one agent CLI the host can run. This page covers how a CLI is named, what provider.list says about it, its model catalog and the permission modes a chat runs in. Accounts covers the config folders a CLI signs in with.
Agent kinds
AgentKindSchema is open: lowercase letters, digits and hyphens, starting with a letter, at most AGENT_KIND_MAX_LENGTH characters. A host can offer its own CLI under a new kind, and older clients still read it. KNOWN_AGENT_KINDS lists the kinds this version names (claude, codex, gemini, copilot, apple), with the type KnownAgentKind and the guard isKnownAgentKind. AgentKind accepts any string and still autocompletes the known ones.
Whether a CLI opens as a chat, in a terminal or both is a capability of its provider. For a kind without its own mark or color, fall back to the provider's name from provider.list, a generic icon and its capabilities. UsageProviderSchema stays closed, as every usage provider needs a reader in code.
import { AgentKindSchema, isKnownAgentKind } from '@adecore/agent-contracts/agent';
AgentKindSchema.parse('grok'); // 'grok'
AgentKindSchema.safeParse('../grok').success; // false
isKnownAgentKind('grok'); // falseAgentStatusSchema (AgentStatus) is a chat's or a terminal session's status: running, needs-you, idle, error or exited.
Models
import { ModelCatalogDataSchema, ModelSelectionSchema } from '@adecore/agent-contracts/model';
const catalog = ModelCatalogDataSchema.parse({
updatedAt: '2026-10-05T00:00:00Z',
defaultModel: 'example-model',
profiles: {
standard: { options: [], contextWindowTokens: { '*': 32000 } }
},
models: [{ slug: 'example-model', name: 'Example model', profile: 'standard' }]
});
const selection = ModelSelectionSchema.parse({ model: catalog.defaultModel, options: {} });A catalog holds one provider's models. A newer copy can replace the shipped one, and updatedAt orders them. The schema requires an ISO date and time, at least one model, a profile per model, and a default model from the list. A slug is lowercase letters, digits, dots, underscores and hyphens, never starting with a hyphen, as it goes on a command line. A profile holds its models' options and context size, per contextWindow option value or under *. The schema does not check that slugs and aliases are unique or that a defaultChoice exists.
ModelCatalogData is the whole catalog. Each of its models is a ModelCatalogEntry (ModelCatalogEntrySchema) with a slug, a name and the name of its profile, and optionally a badge, a vendor, aliases, legacy and a minCliVersion. Each profile is a ModelCatalogProfile (ModelCatalogProfileSchema): options and contextWindowTokens.
ModelSelection is { model, options }, with options as strings and booleans. Parsing checks the shape only, and the host's catalog normalizes it. ModelInfo (ModelInfoSchema) is a model as provider.list describes it, with options as ModelOptionDescriptors (ModelOptionDescriptorSchema): a select with choices and a defaultChoice, or a boolean with a defaultValue. A choice is a ModelOptionChoice (ModelOptionChoiceSchema), { id, label, description? }.
Providers
provider.list returns a ProviderListResult. ProviderInfo (ProviderInfoSchema) is one CLI in it: installed, version, models, defaultModel, capabilities and resumeCommand. The capabilities are ProviderCapabilities (ProviderCapabilitiesSchema). Draw a control only when its capability is there:
| Capability | |
|---|---|
chat, terminal | Where the CLI can open: as a chat, in a terminal, or both. |
hooks | The host reads the CLI's hooks; without them a terminal shows the session status only. |
streamsToolOutput | Output arrives while a tool call runs. |
diffs | How a file change arrives: unified, before-after or none. |
attachments, mentions | Whether a message may carry files and @ paths. |
denyReason, allowAlways | Whether a denial carries a reason, and whether a rule can be remembered. |
asyncQuestions | A question the person may leave while the turn goes on. |
steer | A message sent while a turn runs can join that turn; absent where it cannot. |
compaction | Folding the context: native, as a prompt (prompt), or none. |
reportsCost, reportsContextWindow, reportsThinking | What the CLI tells about a turn. |
slashCommands | Whether the CLI takes slash commands. |
goals | How /goal reaches the CLI: calls that pause, resume and clear (native), the slash command only (command), or absent. |
install, a ProviderInstall, tells a person how to get a CLI that is not installed, for an install page such as the one in the providers pane. It is absent when the host knows no way.
| Field | |
|---|---|
description | One line on what the CLI is. |
commands | One command per ProviderPlatform (ProviderPlatformSchema: darwin, linux, win32). Leave out a system without a one-line install. |
guideUrl | The CLI's own install guide, optional. |
signIn | The ways it signs in, preferred first, each a ProviderSignIn (ProviderSignInSchema): { kind: 'command', command } runs in a terminal, { kind: 'env', variable } is a key in the environment, { kind: 'chat' } asks in the first chat. |
import { ProviderInstallSchema } from '@adecore/agent-contracts/model';
ProviderInstallSchema.parse({
description: 'A coding agent in a terminal.',
commands: { darwin: 'brew install example-cli', linux: 'curl -fsSL https://example.com/install.sh | sh' },
signIn: [{ kind: 'command', command: 'example-cli login' }, { kind: 'env', variable: 'EXAMPLE_API_KEY' }]
});resumeCommandFor(template, sessionId, flags = []) fills a resumeCommand. It shell-quotes the session id and puts the flags at {flags}. The flags go in unquoted, so quote any that come from outside.
import { resumeCommandFor } from '@adecore/agent-contracts/model';
resumeCommandFor('example-cli {flags} --resume {id}', "it's-1", ['--read-only']);
// example-cli --read-only --resume 'it'\''s-1'pickerGroupsOf(providers) splits anything with a kind and name into ProviderPickerGroups, { frontier, others, api }, each sorted by name. A picker lists them in that order, with separators between nonempty groups. FRONTIER_PROVIDER_KINDS holds the frontier kinds and API_PROVIDER_KINDS the providers billed per key. Every other kind goes in others. providerGroupOf(kind) returns one kind's ProviderPickerGroup, for drawing separators in an ordered list.
import { pickerGroupsOf } from '@adecore/agent-contracts/model';
const { frontier, others, api } = pickerGroupsOf(providers);
const ordered = [...frontier, ...others, ...api];Runtime modes
RuntimeModeSchema (RuntimeMode) is a chat's permission policy, shared by every provider: supervised, auto-accept-edits, auto and full-access, narrowest first. Each host adapter maps it onto its CLI (see Providers). A chat keeps the requested mode in runtimeMode and the confirmed one in effectiveRuntimeMode. Show the second when present.
Terminal agents
A CLI can also run in a terminal, where the host reads its hooks. These contracts live outside the agent tables:
AgentRequestSchema(AgentRequest) is a terminal hook request:id,source,kind(approvalorquestion),toolName, nullabletitle,textandcreatedAt. It reports the request. The terminal still owns the answer.AgentInfo(AgentInfoSchema) is a terminal session:kind,agentSessionId,transcriptPath,status, optionalrequests, andlive, false after a host restart when the session can only be resumed.SessionStatusEvent(SessionStatusEventSchema) carries it per session.AgentResumePayload(AgentResumePayloadSchema) names a session to resume.ApprovalRequest(ApprovalRequestSchema) is a permission the terminal agent waits on, withchoicesand anexpiresAt. A choice is anApprovalChoice(ApprovalChoiceSchema):allow,rememberordeny. The CLI asks in its own prompt too, and the first answer wins.SessionApprovalsEvent(SessionApprovalsEventSchema) carries a session's pending list.ApprovalAnswerPayload(ApprovalAnswerPayloadSchema) names the choice.ApprovalAnswerResult.accepted(ApprovalAnswerResultSchema) is false when the request had already settled.ApprovalPreferencePayload(ApprovalPreferencePayloadSchema) says whether a client wants these requests. A client that never sends it gets them.
SuggestedTitleSchema is a session title from the CLI, at most SUGGESTED_TITLE_LIMIT characters. A model wrote it, so show it as plain text. SessionIdSchema (SessionId, from @adecore/agent-contracts/ids) is a terminal session id, any nonempty string the client picks.