The in-product assistant: a chat panel in the admin, a bounded run loop on the server, and the model backends a deployment has chosen to reach. It owns its transcript and ships its migrations.
Install
npm install @apograph/copilot-server @apograph/copilot-adminAnd at least one model backend. The copilot ships none of its own, and a
deployment that registers none must leave COPILOT_ENABLED off:
| Backend | Package |
|---|---|
| Claude | copilot-provider-anthropic |
| OpenAI-wire | copilot-provider-openai — Ollama, vLLM, LiteLLM, Azure, OpenAI |
Register
// apps/server/src/plugins.ts
import { CopilotPlugin } from '@apograph/copilot-server';
CopilotPlugin({
providers: copilotProviders(config),
config: config.plugins.copilot
});// apps/admin/src/plugins.ts
import { CopilotPlugin } from '@apograph/copilot-admin';
CopilotPlugin();Register it after workspaces (runs are workspace-scoped) and identity (runs
execute as the calling user, gated on copilot:use).
The order of providers is the setting. There is no defaultProvider: the
first entry serves a run that names no provider. Register only what is
configured — a keyless backend at the top of the list would be the house default
and would fail on the first message.
The list may be empty, and on a fresh clone it is. That is legal only while
COPILOT_ENABLED is false; turning the copilot on with no backend registered
fails at boot rather than at the first message.
Environment
| Variable | Default | What it does |
|---|---|---|
COPILOT_ENABLED | false | The kill switch. Nothing reaches a model until this is true |
COPILOT_MAX_OUTPUT_TOKENS | 8192 | Ceiling on one response |
COPILOT_MAX_TOTAL_TOKENS | see config | Ceiling on one run |
COPILOT_MAX_STEPS | see config | Tool-call steps one run may take |
COPILOT_WALL_CLOCK_MS | see config | Wall-clock ceiling on one run |
ANTHROPIC_API_KEY | unset | Setting it is what registers the Claude backend |
ANTHROPIC_BASE_URL | unset | An Anthropic-compatible gateway |
COPILOT_ANTHROPIC_MODELS | claude-sonnet-5 | Comma-separated models offered |
COPILOT_OPENAI_BASE_URL | unset | Setting it is what registers the OpenAI-wire backend |
COPILOT_OPENAI_API_KEY | empty | Omit for a local Ollama |
COPILOT_OPENAI_MODELS | llama3.1 | Comma-separated models offered |
Off by default, deliberately
Enabling a hosted provider sends workspace content to a third party. That is an
operator’s decision to make explicitly, so COPILOT_ENABLED defaults to
false even in an app that was scaffolded with a backend installed.
A missing key is what un-registers a backend, rather than what breaks it.
Leave ANTHROPIC_API_KEY empty and there is no claude in the picker at all —
not one that fails on the first message.
There is no offline adapter in the shipped set
The scripted backend is a private test fixture and is not registered. A deployment with no key configured has no copilot at all, which is the honest reading of that state — a canned answer from a scripted adapter is indistinguishable from a working model and hides a lost API key.
To run without a hosted key, point
copilot-provider-openai at a local Ollama or
vLLM. To reach a backend neither adapter speaks, see writing a model
provider.
Authority
A copilot run executes as the calling user and is gated on copilot:use.
It cannot do anything that user could not do through the UI, and every change
it makes is recorded as a proposal you can read back. See
the copilot for the run loop and
agent tools for contributing a tool of your own.