Three names for one value were live in CopilotKit's own documentation, and
following the wrong one with a CLI-provisioned project yields an undefined
key:
- `INTELLIGENCE_API_KEY` — what `copilotkit project select` writes, used by
all 34 integration examples and the docs site.
- `COPILOTKIT_INTELLIGENCE_API_KEY` — the seven Channels package READMEs and
the packaged skills. Nothing ever read it.
- `COPILOTKIT_API_KEY` — the Slack and Teams examples, and the TSDoc on
`CopilotKitIntelligence` itself, which is what an IDE shows on hover.
`INTELLIGENCE_API_KEY` wins, because it is the name the CLI provisions and
changing it would break every scaffolded project in the wild.
`COPILOTKIT_INTELLIGENCE_API_KEY` is retired outright — no code read it.
`COPILOTKIT_API_KEY` stays readable as a deprecated alias in the two
examples that consume it, so an existing `.env` keeps working, and is
documented as deprecated everywhere it appears.
The skills reference also documented `organizationId`, sourced from a fourth
and fifth env name, as a `CopilotKitIntelligence` option. It is not one:
`CopilotKitIntelligenceConfig` has no such field, so the copy-pasteable
sample it appeared in would not compile. Removed from the samples, and the
prose that told readers to fetch a value for it corrected.
The Intelligence wiring itself was published only inside
`node_modules/@copilotkit/runtime/skills/`, and the only docs pages showing
`CopilotKitIntelligence` were the two Channels frontends — so a developer on
the plain web path had no page to reach it from. Adds
`/premium/connect-your-runtime`, which covers the wiring, how to confirm the
credential is actually consumed, and the self-hosted two-URL rule.
`scripts/validate-intelligence-env-names.ts` keeps this from drifting back.
It runs unfiltered in CI on purpose: the two workflows that would otherwise
cover it filter paths, and static/quality ignores `examples/**` — exactly
where the deprecated alias lives.
`CopilotKitIntelligence` required `apiUrl` and `wsUrl` on every construction,
so the two correct hosts had to be found and copied by hand — which is how an
agent came to invent them. Both now default to CopilotKit's managed platform,
making `new CopilotKitIntelligence({ apiKey })` the whole managed-service setup.
Overrides are unchanged for self-hosted and non-production deployments, with two
guards that the previous required-field signature made unnecessary:
- A blank value counts as unset. These URLs are usually wired from env vars, and
a declared-but-empty variable arrives as `""`, which would otherwise produce
host-relative requests instead of falling back to the managed platform.
- Setting only one of the pair warns. The API and realtime planes are separate
hosts, so a lone override silently splits the client across two deployments —
and that failure surfaces as a hang, not an error.
Sweeps the doc, skill, README, and example surfaces to the short form so the
copy-paste path no longer hands anyone URLs to get wrong, and reattaches the
`CopilotKitIntelligence` class JSDoc, which was orphaned above an interface and
so never appeared on hover.
Linear: OSS-638
The Slack and Teams examples derived COPILOTKIT_INTELLIGENCE_WS_URL from
COPILOTKIT_INTELLIGENCE_URL with a scheme-only swap when it was unset, and
documented it as optional. That derive preserves host and port, so it is only
correct on a deployment that puts both planes behind one host+port — which is
neither prod (api.intelligence… vs realtime.intelligence…) nor local dev (4201
vs 4401). In practice it was always wrong, and wrong in the worst way: the
resulting join hangs in `connecting` for 30s and reports only a timeout.
All three call sites (slack native, slack managed, teams) now require both
URLs explicitly and deriveWsUrl is gone. The Teams startup error names both
vars and says why one cannot be computed from the other.
managed.test.ts previously deleted the WS var to exercise the derive; it now
sets a host+port deliberately different from the API URL, so the test encodes
that the planes are deployed apart rather than assuming they are not.
Refs OSS-621
Both run their Channel through an Intelligence runtime that owns lifecycle:
new CopilotRuntime({ intelligence, identifyUser, channels: [bot] }) + handler.channels.ready()/stop(),
no bot.start(). Direct adapters retained (multi-platform slack now runs under Intelligence). Env + example READMEs note the required Intelligence key (free tier).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two fixes from the pre-merge adversarial CR of this PR (both pre-existing,
flagged as in-subject):
- ChannelManager.status() reported overall "online" for a manager stopped
BEFORE activate() (e.g. SIGTERM during startup): `entries` is empty, so the
empty-set fold returned "online" — a torn-down manager reading healthy. Now
short-circuits to "stopped" when `this.stopped`, matching the documented
status() contract. New red-green test covers the stop()-before-activate() case.
- examples/slack/.env.example: COPILOTKIT_INTELLIGENCE_WS_URL example was
ws://localhost:4401, but derivation is a scheme-only swap of the :4201 API URL
(→ ws://localhost:4201) and 4401 is used nowhere — a user uncommenting it hit a
dead port. Corrected to :4201 and clarified the derivation note.
Pre-existing .env.example issues noted in the OSS-473 CR:
- AGENT_MODEL example was `anthropic/claude-sonnet-4.5`, but runtime.ts is
OpenAI-only (web search is an OpenAI hosted tool; it strips a leading
`openai/` and passes the rest to `openaiText`). Use an OpenAI example and say
so.
- Removed ANTHROPIC_API_KEY / GOOGLE_API_KEY — never read by this runtime.
- LINEAR_API_KEY, NOTION_TOKEN and NOTION_MCP_AUTH_TOKEN were non-blank
placeholders, but runtime.ts turns the Linear/Notion MCPs ON purely on the
presence of LINEAR_API_KEY / NOTION_MCP_AUTH_TOKEN — a placeholder wires a
broken MCP. Blanked them (they're optional integrations, like the commented
Discord/Telegram/WhatsApp creds).
- Documented NOTION_MCP_PORT (the `pnpm notion-mcp` sidecar port, default 3001;
must match NOTION_MCP_URL).
- Added WhatsApp to the header list of supported adapters.
The StateStore interface and the in-memory MemoryStore default remain;
durable backends can be reintroduced as a follow-up. Both adapter packages
were merged in #5613 but never published to npm, so removal is a clean
delete with no consumer impact.
- Delete packages/bot-store-redis and packages/bot-store-postgres.
- Revert the bot release scope and drift guard to bot + bot-ui.
- Strip the Redis dep, demo:restart script, restart demo, docker-compose,
and REDIS_URL env from examples/slack.
- Rewrite the bot persistence/transcripts docs around "MemoryStore default
+ implement the StateStore interface yourself for durability".
Adds a durable persistence layer for @copilotkit/bot, replacing the
in-memory-only ActionStore with a pluggable StateStore.
- StateStore interface (kv/list/lock/dedup/queue) with a shared
conformance suite; MemoryStore default plus @copilotkit/bot-store-redis
and @copilotkit/bot-store-postgres backends.
- createBot({ store }): typed per-thread state via Standard Schema,
action snapshots persisted through the store, per-conversation turn
lock (onLockConflict drop|force), and inbound-event dedup keyed on a
stable eventId. ActionStore is kept as a deprecated alias.
- Cross-platform transcripts (bot.transcripts + identity resolver) with
age-bounded retention (prune on append + filter on read), and
runAgent({ transcript: true }) to auto-inject history and capture the
reply.
- createBot({ components }) re-registers components so durable actions
re-fire after a restart; restart-durability demo in examples/slack.
- Dedup is marked seen only after the turn lock is acquired, so a turn
dropped on lock-conflict does not burn its eventId (no lost retries).
- Release lockstep: bot-store-redis/postgres version with bot + bot-ui.
Unify WhatsApp with main's Slack+Discord multi-adapter demo: WhatsApp becomes a
third env-gated platform block in examples/slack/app/index.ts (listening on
Railway $PORT, with a malformed-PORT guard). Keep the platform-aware
senderContext (also fixes the Discord 'Slack user' label); drop the superseded
buildAdapters helper for main's inline per-platform pattern. package.json takes
main's ~0.0.2 bumps + bot-discord and adds bot-whatsapp (workspace:~); README
intro + deploy section cover all three surfaces.
examples/slack now starts a Slack bot and/or a Telegram bot from one platform-neutral app layer, env-conditional on which credentials are set. Neutralized Slack-specific rendering so the shared components work on both platforms: unicode glyphs instead of mrkdwn shortcodes, no Block Kit raw fallbacks, neutral context/tool wording. Migrated the Telegram e2e smoke harness and BotFather setup docs; removed the separate examples/telegram app.
100% JSX bot at feature parity with the PoC example: issue/page cards,
tables, Chart.js charts, Mermaid diagrams, incident/status/links cards,
a confirm_write HITL gate, and /agent + /triage slash commands.
Registers examples/slack in the pnpm workspace.