The prompt a developer copies from the Inspector, the docs hero and the feature
cards opened with two sentences of instruction to the coding agent: identify
yourself with a slug, and pass it as a flag. It is the one piece of text a human
reads, decides on and pastes, and most of it was addressed to something else.
Help me get started with CopilotKit. Run this command and follow the
instructions:
npx --yes copilotkit@latest onboard start --run <run-id>
Identification moves into the prompt graph, which is the surface that talks to
the agent for the rest of the run: `onboard identify --coding-agent <slug>` is
run by `authenticate/start` (Intelligence OSS-1157). The CLI still accepts
`--coding-agent` on `start`, so this wording and the old wording both work and
neither repository has to land first.
The feature prompts keep their standing permission for the CLI session check.
That sentence is the developer granting something by copying the text, and the
graph cannot grant it to itself, so only the identification half is cut.
Kept byte-identical across the Inspector template, the docs constant, and
Intelligence's own `createCodingAgentOnboardingPrompt`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The seven `--intent` feature routes shipped with no caller. Every copyable
prompt still ran `onboard start --run … --coding-agent …`, so the only way
to reach a route was to type the flag by hand, and the Inspector solved the
same problem again with its own per-feature prose.
Home's feature buttons now emit `onboard start … --intent <slug>` and carry
no feature-specific instruction. The route owns the guide links, the plan,
the per-phase check-ins, the refusal when a prerequisite is missing, and the
proof step. One table maps each tile to one intent, and a test holds the two
sets equal in both directions.
The two docs feature prompts follow: Learning and Rich Threads name an
intent instead of repeating setup prose that had already drifted from the
shipped API once. The generic prompts on docs and the Intelligence web app
stay generic, because every feature route requires an app that those
readers do not have yet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two independent docs-frontend improvements in `showcase/shell-docs`.
Rebased onto current `main` (the branch was 158 commits behind).
## 1. `<Tabs persist>` now actually persists
101 tab groups in the content tree are authored as `<Tabs groupId="..."
persist>`. The wrapper accepted both props and ignored them — the
comment in `docs-tabs.tsx` said so outright:
> `groupId` and `persist` are accepted and currently ignored
So every page reopened on its own default. A reader working through the
LangGraph guide in TypeScript had to reselect TypeScript on each page.
Fumadocs holds tab selection in local component state and exposes no
persistence hook, so the wrapper takes over the controlled
`value`/`onValueChange` pair and mirrors the pick into `localStorage`
under `shell-docs.tab.<groupId>`.
**Selection precedence**, strongest first:
| # | Source | Where it comes from |
|---|--------|---------------------|
| 1 | `urlDefault` | The framework-route override the page shell derives
from the URL (`TAB_DEFAULTS_BY_SLUG`) |
| 2 | Stored pick | `localStorage`, same `groupId` |
| 3 | `default=` | The author's value in the MDX |
| 4 | First item | Fallback |
The page shell passes its URL-derived value as a **separate `urlDefault`
prop** rather than overwriting `default`. This matters: 45 of the 101
`persist` groups also carry an author `default=`. Collapsing the two
sources into one prop ranks the author's default above storage, which
would leave the stored pick unreachable on almost half of the pages the
feature exists for. `language_langgraph_agent` appears both ways (30
sites with `default="Python"`, 8 without), so the same group would have
behaved inconsistently within one guide.
Two details worth noting for review:
- The stored value is read in an **effect**, not in the initial state,
so server and client render identical markup and hydration stays clean.
- Every `localStorage` read and write is wrapped in `try`/`catch`.
Private mode and quota errors leave the tabs fully working, just without
persistence.
## 2. API-key hints under `.env` snippets
The LangGraph quickstart tells the reader to put `OPENAI_API_KEY` in
`.env` and leaves them to go find the key page. `<ApiKeyHint
provider="openai" />` renders a muted one-line link under the snippet.
The component maps a provider id to a label and URL — `openai`,
`anthropic`, `google`, `langsmith`, `copilotkit`. An unknown id renders
nothing, so a typo degrades to today's behaviour instead of throwing. It
is navigational only: it neither reads nor writes a key. Both `.env`
steps on the LangGraph quickstart use it.
## Removed from this branch
The earlier revision led the LangGraph quickstart with a `<InlineDemo
demo="agentic-chat" />` block under a "See it working" heading. That is
gone, along with the `inline-demo.test.tsx` file that covered it —
`InlineDemo` is pre-existing `main` code this PR no longer touches.
Two tests in `docs-page-view-toc.test.tsx` were also dropped rather than
kept. They were named for this PR's components but did not exercise
them: `docs-page-view-toc.test.tsx` asserts on `DocsPage` props, and the
page body is never rendered. Verified by mutation — deleting
`ApiKeyHint` from the MDX registry left the test titled `renders the
LangGraph quickstart (InlineDemo + ApiKeyHint) without errors`
**passing**. Real coverage lives in `api-key-hint.test.tsx` instead.
## Testing
No CI job runs the `showcase/shell-docs` vitest suite.
`test_unit-showcase.yml` covers only `harness` and `shell-dashboard`;
`showcase_validate.yml` runs vitest only in `showcase/scripts`.
Everything below was therefore run locally.
**Full suite, branch vs. pristine `origin/main` in the same
environment** — `main` carries 6 pre-existing failures here, so the
failure *set* is the comparison, not zero:
```
base (origin/main) 843 tests, 6 failed
branch (this PR) 858 tests, 6 failed
NEW failures: none
```
The 6 are identical on both sides: `brand-nav` layout cap, 3 ×
`angular-docs-content`, `llm-text` mastra, `ms-agent-python-stable-api`.
**Mutation checks** — every new test was verified to fail when the
mechanism it claims to cover is broken:
| Mutation | Result |
|----------|--------|
| `canPersist = false` (persistence off) | ✅ `persists a groupId pick
and reapplies it on a fresh mount` fails |
| Author `default` outranks storage (the pre-fix precedence) | ✅ `ranks
a stored pick above the author's MDX default` fails |
| `urlDefault` demoted below author `default` | ✅ `ranks a urlDefault
above the author's MDX default` fails |
| `ApiKeyHint` removed from the MDX registry | ✅ `is registered as an
MDX component` fails |
| `href={meta.url}` → `href={undefined}` | ✅ 5 of 7 `ApiKeyHint` tests
fail |
**End-to-end render** — `ApiKeyHint` was rendered through the real
`MDXRemote` pipeline (same `remarkGfm` options, nested in
`<Steps>/<Step>` as the quickstart uses it) to confirm the `provider`
prop survives compilation and the anchor reaches the HTML:
```
✓ ApiKeyHint through the real MDX pipeline > survives compilation with its provider prop
expect(html).toContain("https://platform.openai.com/api-keys")
```
**Typecheck and lint** (`showcase/shell-docs`):
```
$ npx tsc --noEmit → exit 0
$ npx oxlint . → Found 28 warnings and 0 errors
$ npx oxfmt --check <touched files> → All matched files use the correct format
```
The 28 lint warnings are pre-existing. The only two in a file this PR
touches (`mdx-registry.tsx`) are `iframe-missing-sandbox` on
pre-existing `InlineDemo` iframes, untouched here.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added inline API key guidance below relevant documentation code
blocks, with links to provider credential pages.
* Added tab selection persistence across documentation pages, with
support for URL and author-defined defaults.
* Added API key guidance to the LangGraph quickstart.
* **Tests**
* Added coverage for tab persistence, selection precedence, invalid
values, disabled persistence, and API key hint behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The component fix alone was inert on the real page. next-mdx-remote 6
defaults `blockJS` to true, which runs a remark plugin that deletes
every JSX attribute whose value is an expression. On the docs route
`<CTACards columns={2} cards={[...]} />` reached the component with no
props at all, so it still rendered an empty grid. The unit tests passed
because they call the component directly and skip the MDX pipeline.
- Turn `blockJS` off for the docs route. Every source there is
first-party content from `src/content`. `blockDangerousJS` keeps its
default. The other MDXRemote call sites keep the default too, because
`ag-ui/introduction.mdx` authors inline `onMouseEnter` handlers that
the stripping currently keeps out of a server component.
- Resolve each card href against the framework being read. The cards
render through the registry `Card`, so they never reached the
href-resolving `Card` override, and a reader on
`/ms-agent-python/human-in-the-loop` was redirected to the .NET page.
Content now authors the hrefs root-relative.
- Stack the grid to one column below the `sm` breakpoint. An inline
`grid-template-columns` cannot be overridden by a class, so the two
cards stayed 157px wide side by side on a 390px viewport.
- Pass each description through the `Card` `description` prop, the same
as every other card grid in the docs.
- Match `iconKey` against own properties only.
- Add a test that compiles the four authored blocks through the MDX
pipeline, which is the check the earlier tests were missing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The LangGraph quickstart tells the reader to put `OPENAI_API_KEY` in
`.env` and leaves them to find the key page themselves. `<ApiKeyHint
provider="openai" />` renders a muted one-line link under the snippet.
The component maps a provider id to a label and a URL, covering openai,
anthropic, google, langsmith and copilotkit. An unknown id renders
nothing, so a typo degrades to the current behaviour instead of
throwing. It is navigational only: it neither reads nor writes a key.
Both `.env` steps on the LangGraph quickstart use it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`<Tabs groupId="..." persist>` is written on 101 tab groups in the docs
content tree, but the wrapper accepted both props and ignored them: the
comment in docs-tabs.tsx said so outright. Every page therefore reopened
on its own default, so a reader working through the LangGraph guide in
TypeScript had to reselect TypeScript on each page.
Fumadocs holds tab selection in local component state and exposes no
persistence hook, so the wrapper takes over the controlled
`value`/`onValueChange` pair and mirrors the pick into localStorage
under `shell-docs.tab.<groupId>`.
Selection precedence, strongest first:
1. `urlDefault` — the framework-route override the docs page shell
derives from the URL via TAB_DEFAULTS_BY_SLUG.
2. A stored pick for the same `groupId`.
3. The author's `default=` written in the MDX.
4. The first item.
The page shell now passes its URL-derived value as a separate
`urlDefault` prop instead of overwriting `default`. Collapsing the two
into one prop would rank the author's default above storage, and 45 of
the 101 `persist` groups carry an author `default=` — the stored pick
would have been unreachable on almost half the pages the feature exists
for.
The stored value is read in an effect rather than in the initial state,
so server and client render the same markup. Reads and writes are
wrapped in try/catch: private-mode and quota errors leave the tabs
working without persistence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Announce recommendation selection without invalid active descendants, omit
missing controlled elements, and cover empty-result and result-slot behavior.
Report snippet expansion failures and reject partially staged content roots
before either search index is overwritten.
Validation: 829 tests pass; three Angular/Mastra content failures already
reported in PR #6887 remain. Typecheck and lint pass (existing lint warnings).
Browser-verified local search, keyboard selection, and recommendation navigation.
Pin Intelligence in the header and sidebar, add an Explore docs mega menu, and collapse top-level sidebar sections so people do not have to scroll past a long list to reach Intelligence.
Adds a five-minute quickstart for connecting an existing CopilotKit app
to Intelligence. Developers can use a coding-agent prompt or follow
framework-specific manual steps. The guide ends with a saved-thread
check in Inspector.
Linear:
[OSS-1076](https://linear.app/copilotkit/issue/OSS-1076/add-a-quickstart-for-intelligence)
## What changed
- Added the Intelligence quickstart to the docs navigation and overview.
- Added setup examples for React, Vue, Angular, and React Native.
- Added Inspector checks for browser apps and a hosted-project check for
React Native.
- Updated the coding-agent prompt with route, identity, and
authorization requirements.
- Updated Next.js and TanStack Start adapters to forward thread mutation
methods.
## Testing
Commands run:
- `npm exec -- vitest run
src/lib/__tests__/intelligence-quickstart-docs.test.ts
src/lib/__tests__/docs-render.test.ts
src/lib/__tests__/rich-threads-setup-docs.test.ts
src/components/__tests__/rich-threads-setup-prompt.test.tsx`: 39 tests
passed.
- `npm run typecheck`: passed.
- `npm run lint`: passed with existing warnings in unrelated files.
- `npm run build`: passed.
- The pre-commit package, binary, environment-name, lint, and commit
checks passed.
The full shell-docs test run still has unrelated Windows failures in
asset, generated Angular content, and LLM text tests.
Manual test:
1. Start shell-docs and open `/intelligence/quickstart`.
2. Select React, Vue, Angular, and React Native in the frontend
selector.
3. Check that each frontend shows its matching provider example.
4. Check that browser frontends use Inspector to find the new thread.
5. Check that React Native uses the hosted Intelligence project instead.
The new contract test checks the route, navigation, framework examples,
agent prompt, and Inspector completion path.
## Risk / rollback
Risk is low because this PR changes documentation and examples only.
Revert the commit to remove the quickstart and restore the prior adapter
examples.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added an Intelligence quickstart guide covering project setup,
authentication, thread authorization, runtime integration, frontend
configuration, and connection verification.
- Added the quickstart to Intelligence documentation navigation and
linked to it from the overview page.
- Expanded setup guidance with browser and React Native steps for
creating threads and sending messages.
- **Documentation**
- Updated Next.js and TanStack Start examples to forward all Runtime
methods, including `PATCH` and `DELETE`.
- Clarified authentication and thread ownership requirements for Runtime
routes and operations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The "Copy agent prompt" button already told the CLI's onboarding graph
which agent framework the reader had selected. The docs carry a second
selector — Frontend — whose choice the prompt dropped, so the graph still
had to ask which frontend to configure.
Add a frontend sentence built exactly like the framework one and placed
between it and the page sentence, which is the order the graph works in:
it settles the agent framework first, then the frontend. Each sentence
leads with its own subject and can be empty independently, so all four
combinations read correctly.
The docs frontend is resolved from the URL, never from a stored
preference — choosing a frontend navigates to another URL, so the URL is
what asserts the selection. This is the rule the selector itself applies
(`urlFrontend ?? "react"`), and the same standard the framework sentence
already holds to. Resolution happens server-side and reaches the button
as a prop, so it cannot drift from the page being rendered.
Why `react` maps to the graph's `nextjs`: every frontend prompt under
`apps/cli/onboarding-prompts/frontend/` names the docs page it belongs
to, and `nextjs.md` is the only one whose documentation link is the
UNPREFIXED https://docs.copilotkit.ai/quickstart.md — the docs' default
`react` frontend, served at the root. Every other prompt points at its
own prefixed page (`/vue.md`, `/angular.md`, `/react-spa.md`,
`/react-native.md`). Same frontend, spelled differently on each side.
Slack and Teams stay deliberately unmapped. They are chat channels, not
application frontends; the graph has no node for either, so naming one
would promise a path the CLI cannot walk. The analytics `frontend`
property follows the same rule as `agent_framework`: the graph slug, and
the key omitted entirely when there is no equivalent.
A guard test over the docs frontend registry reads `FRONTEND_OPTIONS` —
the same list the app itself renders — so adding a frontend forces a
mapping decision instead of silently producing no sentence.
`a2a` and `agent-spec` are documented like agent frameworks — their own
route, their own quickstart, their own page-tools row — but neither is a
registered integration, so there is no display name to substitute into the
framework sentence. Gating the button on that name kept it off the two
quickstart pages where an onboarding offer belongs most.
The framework is now optional. Those pages get the button with the page
sentence alone; the prompt claims no framework rather than naming one the
registry cannot confirm. Nothing is lost by the silence: the CLI's graph
inspects the repository and determines the framework itself, and under A2A
or Agent Spec that is in practice one of the frameworks it already knows.
With the last gate gone, every page reaching DocsPageTools now renders the
button. That is the intended rule: DocsPageView serves only docs surfaces,
and the offer holds on all of them.
The docs spell the framework `deepagents`, the onboarding graph spells it
`deep-agents`, and nothing mapped between them — so every `/deepagents/...`
page copied a prompt with the framework sentence silently missing, the same
way `built-in-agent` did before it was mapped.
The registry-coverage guard did not catch it because it read
`data/registry.json` directly, and docs-only integrations are merged in by
`registry.ts` and never appear in that file. It now reads `getIntegrations()`,
so the slugs most likely to be forgotten are the ones it actually covers;
removing the new rename makes it fail with `[ 'deepagents' ]`.
The Built-in Agent's call-site display name drops to "Built-in": the shared
sentence template already supplies the word "agent", so the previous name
rendered as "the Built-in Agent agent framework".
The root surface is not a frameworkless surface. It is the Built-in
Agent's lens on the same docs the `/<framework>/…` URLs serve, so
`/faq` and `/mastra/faq` are one page read with two different framework
selections. Gating the page-tools "Copy agent prompt" button on
`frameworkOverride` — a CONTENT-resolution signal, true only for pages
carrying a snippet cell or a Built-in-Agent override file — made those
identical pages behave differently depending on the URL the reader
arrived through: `/mastra/backend/copilot-runtime` had the button,
`/backend/copilot-runtime` did not, and likewise for `/faq`,
`/concepts/architecture`, `/troubleshooting/common-issues` and
`/cookbook/arcade`.
`UnscopedDocsPage` now resolves the prompt framework once, for
`ROOT_FRAMEWORK`, and hands it to both branches; the cookbook routes do
the same. `frameworkOverride` is untouched and keeps its narrower
per-branch value — the two props answer different questions and are no
longer expected to agree.
The same reasoning applies to the frontend routes, where a missing
backend segment is how the Built-in Agent is spelled:
`/vue/built-in-agent/<slug>` redirects to `/vue/<slug>`, the framework
selector shows the Built-in Agent as active there, and `resolveAngularDoc`
already resolved `backendFramework ?? ROOT_FRAMEWORK`. The three frontend
branches that still resolved to no framework now do the same, so
`/vue/<slug>` matches `/vue/mastra/<slug>`.
121 of the 141 root-surface docs URLs now carry the button. The 20 that
do not are not root-surface docs pages: 14 legacy tutorial slugs and
`/built-in-agent` redirect to `/`, one lives on the `/reference` surface,
and five are the docs-only `agent-spec` framework, whose slug the
registry has no display name for.
The Built-in Agent's docs are served at the root surface without a framework
prefix, so `unscoped-docs-page.tsx` rendered them and never passed
`onboardingFramework` — 56 root URLs (`/frontend-tools`, `/quickstart`,
`/shared-state`, …) had no "Copy agent prompt" button. The prop is now derived
from the same `frameworkOverride` the component already computes for content
resolution, so the button covers exactly the pages that render BIA-scoped and
nothing else. Genuinely frameworkless pages (`/faq`, `/examples`) still get no
button.
`DOCS_SLUG_RENAMES` now maps `built-in-agent` to the graph's `built-in`. The
old doc comment claimed the CLI's onboarding graph does not know this
framework, which is wrong: `ONBOARDING_AGENT_FRAMEWORKS` lists `built-in` and
the graph ships `onboarding-prompts/framework/built-in.md`. Note that this
rename also changes the EXISTING hero onboarding button: on BIA surfaces its
copied prompt previously named no framework at all and now carries the
framework sentence too. That is the point of the fix, not a side effect.
The registry name is "CopilotKit's Built-in Agent", which the shared sentence
template renders as "the CopilotKit's Built-in Agent agent framework". Rather
than special-case the template, `onboardingFrameworkFor` moves out of the
framework route into `lib/docs-onboarding-framework.ts` and overrides the
display name to "Built-in Agent" there — one place, used by both surfaces that
can render a BIA page.
Reverts the suppression added in edcd9268ee. On the ten framework roots
whose landing page is an authored index.mdx, the hero keeps its own
"Copy onboarding prompt" alongside the page-tools "Copy agent prompt".
The two prompts differ only in that the page-tools one also names the
page it was copied from, so neither contradicts the other.