Providers
A provider is a coding-agent SDK behind one interface. Chat turns and workflow
prompt nodes both run through it, so swapping the provider swaps the agent
without touching the conversation, the workflow, or the harness. For how to
enable each one and where its credentials come from, read
Configuration. What follows is the
IAgentProvider contract and the support matrix.
The interface
Section titled “The interface”interface IAgentProvider { sendQuery( prompt: string, cwd: string, resumeSessionId?: string, options?: SendQueryOptions, ): AsyncGenerator<MessageChunk>;
getType(): string; getCapabilities(): ProviderCapabilities; listModels(): Promise<ModelInfo[]>; // must never throw}sendQuery streams the turn as MessageChunks, the same unit chat and
workflows consume. listModels must never throw: on a failed probe it falls
back to bare ids from capabilities.models, so the model picker never empties
out.
Capabilities
Section titled “Capabilities”interface ProviderCapabilities { sessionResume: boolean; // multi-turn context survives across turns streaming: boolean; // emits incremental chunks, not one final blob tools: boolean; // keelson projects its MCP / rib tools into the provider models: string[]; // baseline ids, updated when Copilot discovers live models defaultModel: string; // "" means "let the SDK decide" (no model on the wire) modelClasses?: { fast: string; balanced: string; deep: string };}The tools capability is specifically about tool projection: whether the
harness hands its tool registry to the provider. A provider can be tools: false
and still act agentically with its own built-in tools, as Codex does.
SendQuery options
Section titled “SendQuery options”SendQueryOptions carries everything a turn needs beyond the prompt. The fields
are provider-neutral: a provider consumes what it supports and ignores the rest.
| Option | For |
|---|---|
model | The model id; absent uses the provider default. |
systemPrompt | The assembled system prompt for the turn. |
tools | The ToolDefinitions to project (the harness tool registry). |
allowedTools / disallowedTools | SDK-level tool gates. Claude gates by name; Copilot gates the built-in capability the names map to. |
thinking | Extended thinking. Consumed by Claude. |
reasoningEffort | none … xhigh. Copilot and Codex consume it on reasoning models; Claude maps it to thinking budgets. |
mcpServers | stdio MCP servers to attach. |
abortSignal | Cancels an in-flight turn. |
onSessionId | Called once when the backend session id is known, so the next turn can resume it. |
hooks | Per-node hook matchers. Claude honors all events; Copilot honors PreToolUse / PostToolUse. |
The provider matrix
Section titled “The provider matrix”Five agent providers ship built in. Out of the box only Copilot loads; the rest are opt-in (see Configuration).
Opt-in also means installed: the Claude, Codex, and pi SDKs are hundreds of
megabytes apiece and serve one vendor each, so a fresh install carries none of
them. keelson provider add <id> fetches one into the keelson home and enables
it; keelson provider remove <id> uninstalls the SDK and disables it in
config.json. Note that KEELSON_PROVIDERS is an exact override: while it still
lists an id, the config file cannot disable that provider, and provider remove
says so rather than claiming the provider is off. An enabled provider whose SDK
is absent is left unregistered rather than offered and broken.
| Provider | id | Ships with keelson | Resume | Stream | Tool projection | Auth |
|---|---|---|---|---|---|---|
| GitHub Copilot | copilot | yes | yes | yes | yes | keelson keychain |
| Claude | claude | no (on demand) | yes | yes | yes | keelson keychain (subscription or API key) |
| Codex | codex | no (on demand) | yes | yes | no | self-managed |
| Pi (community) | pi | no (on demand) | no | yes | yes | self-managed |
| Stub (echo) | stub | yes | no | yes | no | none |
A few rows need their footnote:
- Codex runs its own shell and file-edit tools inside the
codex execsubprocess, gated by its sandbox, so the harness does not project tools into it. Its activity surfaces in chat as command-execution and file-change rows. - Pi projects the harness tool registry into its session as pi custom tools, so rib and workflow tools reach a pi turn. Its own built-in file and shell tools stay off (they would run outside the harness rails), and session resume is not wired yet, so each turn is a fresh session.
- Stub is the offline echo provider: no credentials, no tool calls, deterministic output. It exists so every loop can be verified without keys.
A sixth id, workflow, is also registered as a built-in, but it is an internal
meta-provider, not an agent an operator selects. It carries no models and runs no
turn of its own, and defaultProvider: "workflow" is rejected, so it never
appears as a choice. The five rows above are the selectable set.
Model catalogs
Section titled “Model catalogs”capabilities.models starts as a baseline. Claude and Codex publish known ids;
Copilot discovers the ids available to your account. GET /api/providers/:id/models returns the live list the picker prefers.
Portable workflow tiers (fast, balanced, and deep) resolve through provider
model classes and defaults. A provider without a class map can use its default
for all three tiers. Operators can replace individual derived ids with
per-provider modelClasses settings.
Claude. The default is claude-opus-4-8.
| id | Name | Cost tier |
|---|---|---|
claude-fable-5 | Fable | high |
claude-opus-4-8 | Opus | mid |
claude-sonnet-5 | Sonnet | mid |
claude-haiku-4-5 | Haiku | low |
Codex. The default is "", which sends no model on the wire and lets codex
resolve the account’s own ~/.codex default rather than forcing one it may not
be able to reach.
| id | Name | Cost tier |
|---|---|---|
gpt-6-sol | Sol | high |
gpt-6-luna | Luna | low |
Copilot and Pi publish no fixed concrete catalog, by design. Copilot starts
with the synthetic auto, which delegates routing to Copilot. After registration,
it fetches the account’s catalog in the background, without delaying server
startup, rib activation, or CLI bootstrap. It derives fast, balanced, and
deep from concrete models’ cost tiers (the catalog’s price category: low,
medium, high, very high), excluding auto from the ranking; entries without cost
metadata use catalog order. The first model in catalog order wins within a tier,
so pin the classes in config.json when a specific model matters. Its published models then include auto
and the discovered concrete ids. A class-based Copilot turn waits for that bounded
fetch if it is still in progress. An explicit model, a class pinned in
config.json, or an unclassed turn never waits for it, and the unclassed default
remains auto.
If discovery fails or returns no concrete ids, all three classes request auto.
A catalog with only one concrete model also legitimately maps every class to the
same id. Assignments stay fixed once derived for the process; restart to derive
from a changed catalog. keelson doctor warns when the three effective classes
request the same id, even with no workflows installed. With auto, identical
routing requests do not prove the same backend model served each turn.
keelson doctor --strict treats that warning as a failing exit status.
Pi is multi-vendor and resolves its catalog from the vendors your credentials authenticate, so it also has no fixed list to publish.
Gateway providers
Section titled “Gateway providers”Beyond the built-ins, any OpenAI-compatible endpoint can be registered
as a provider at runtime. A gateway (OpenRouter, a local Ollama or vLLM, Azure
OpenAI, a LiteLLM proxy) is added with keelson gateway add and shows up in the
picker under its own name, so the model picker treats it like a built-in.
| Trait | Gateway provider |
|---|---|
| id | the gateway name (lowercase kebab-case, cannot shadow a built-in id) |
| Resume | no |
| Stream | yes |
| Tool projection | no (tools: false; OpenAI function-calling is not wired yet) |
| Protocol | openai (Chat Completions) only today |
| Auth | OS keychain under gateway-<name>, or keyless |
listModels probes the endpoint’s /models, falling back to the gateway’s
configured model if that call fails, so the picker is never empty for a gateway
with a model set. Configure one in the Gateways
section of the configuration guide.
Keyless gateways may use remote HTTP. A gateway with a stored key must use HTTPS
or loopback HTTP. On keyed remote HTTP, live model discovery makes no request,
reports a warning, and remains not checked; listModels may still return the
configured-model fallback. Chat returns an explicit error without sending a
request.
Where a provider is chosen
Section titled “Where a provider is chosen”| Scope | Mechanism |
|---|---|
| A chat turn | The model picker in the app, or keelson chat --provider <id>. |
| A workflow node | The node’s provider: field, or the workflow’s top-level provider:. |
| The workflow floor | KEELSON_WORKFLOW_PROVIDER pins the provider every prompt node uses, independent of chat. |
| Which providers load | The providers map in config.json, or KEELSON_PROVIDERS (an exact override). |
Models follow the same two-tier pattern: capabilities.models is a curated
baseline, and GET /api/providers/:id/models returns the live list the picker
prefers.
Related
Section titled “Related”- Configuration: enabling providers, the precedence rules, and how each provider’s credentials resolve.
- Workflow nodes: the
providerandmodelfields a node and a workflow can set. - Architecture: where the provider registry sits in the server’s composition root.