Skip to content

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.

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.

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.

SendQueryOptions carries everything a turn needs beyond the prompt. The fields are provider-neutral: a provider consumes what it supports and ignores the rest.

OptionFor
modelThe model id; absent uses the provider default.
systemPromptThe assembled system prompt for the turn.
toolsThe ToolDefinitions to project (the harness tool registry).
allowedTools / disallowedToolsSDK-level tool gates. Claude gates by name; Copilot gates the built-in capability the names map to.
thinkingExtended thinking. Consumed by Claude.
reasoningEffortnone … xhigh. Copilot and Codex consume it on reasoning models; Claude maps it to thinking budgets.
mcpServersstdio MCP servers to attach.
abortSignalCancels an in-flight turn.
onSessionIdCalled once when the backend session id is known, so the next turn can resume it.
hooksPer-node hook matchers. Claude honors all events; Copilot honors PreToolUse / PostToolUse.

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.

ProvideridShips with keelsonResumeStreamTool projectionAuth
GitHub Copilotcopilotyesyesyesyeskeelson keychain
Claudeclaudeno (on demand)yesyesyeskeelson keychain (subscription or API key)
Codexcodexno (on demand)yesyesnoself-managed
Pi (community)pino (on demand)noyesyesself-managed
Stub (echo)stubyesnoyesnonone

A few rows need their footnote:

  • Codex runs its own shell and file-edit tools inside the codex exec subprocess, 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.

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.

idNameCost tier
claude-fable-5Fablehigh
claude-opus-4-8Opusmid
claude-sonnet-5Sonnetmid
claude-haiku-4-5Haikulow

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.

idNameCost tier
gpt-6-solSolhigh
gpt-6-lunaLunalow

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.

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.

TraitGateway provider
idthe gateway name (lowercase kebab-case, cannot shadow a built-in id)
Resumeno
Streamyes
Tool projectionno (tools: false; OpenAI function-calling is not wired yet)
Protocolopenai (Chat Completions) only today
AuthOS 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.

ScopeMechanism
A chat turnThe model picker in the app, or keelson chat --provider <id>.
A workflow nodeThe node’s provider: field, or the workflow’s top-level provider:.
The workflow floorKEELSON_WORKFLOW_PROVIDER pins the provider every prompt node uses, independent of chat.
Which providers loadThe 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.

  • Configuration: enabling providers, the precedence rules, and how each provider’s credentials resolve.
  • Workflow nodes: the provider and model fields a node and a workflow can set.
  • Architecture: where the provider registry sits in the server’s composition root.