New users were still discovering cloud.copilotkit.ai through docs pages,
the README, example READMEs, and in-app banners/console messages. Replace
all user-facing web links with dashboard.operations.copilotkit.ai (the
destination the marketing-site CTAs already use). Functional API endpoints
(api.cloud.copilotkit.ai) are deliberately untouched since existing cloud
customers depend on them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Our previous doc site had a fumadocs/shadcn theme that was a bit
hodge-podge. This brings all of it into one clean and visually appealing
design. The border radii are standardized, colors are standardized and
bunch of passes for mobile/tablet have been done. An added bonus is that
the header nav has been redone to feel more natural to the eyes.
<img width="1850" height="1256" alt="Screenshot 2026-06-05 at 7 48
00 AM"
src="https://github.com/user-attachments/assets/0c6032f2-e406-4e51-9c31-d69a9d258d61"
/>
Applies the updated shell-docs theme treatment to the overview cards, sample tabs, framework selectors, hero command controls, and Copilot Cloud CTAs.
Call-site enumeration:
- FrameworkSelector/FrameworkTabs/IntegrationGrid changes stay on the docs landing and framework overview surfaces that already own those controls.
- OpsPlatformCTA and LinkToCopilotCloud keep their existing call sites while switching to shared chrome variables.
Refreshes the shell docs chrome around the shared theme tokens, announcement banner, desktop and mobile navigation, search trigger, theme toggle, and sidebar footer actions.
Call-site enumeration:
- PrimaryDocsTabs: rendered by MobileTopNav for tablet header tabs and ShellDocsLayout for mobile sidebar tabs.
- MobileSidebarFooterTalk: rendered by ShellDocsLayout sidebar.footer and hidden outside mobile/tablet sidebar usage via responsive classes.
- INTELLIGENCE_CTA_HREF and TALK_TO_ENGINEER_HREF: exported from BrandNav and reused by BrandNav/MobileSidebarFooterTalk so CTA destinations stay centralized.
Drop the hand-rolled render_a2ui/generate_a2ui + a2ui_prompt framing.
Document the two real paths: prebuilt agent (add CopilotKitMiddleware)
and graph agent (wire get_a2ui_tools). injectA2UITool stays the single
on/off switch. Fixes langgraph + deepagents + the generic page;
corrects the streamed op names (createSurface/updateComponents/
updateDataModel).
Previously only the top-level /build-with-agents and built-in-agent pages
rendered the Skills section (via <BuildWithAgents />). Every framework
integration page used the MCP-only <CodingAgents /> snippet (or, for
langgraph, <MCPSetup /> directly), so Skills — the recommended path — was
hidden there.
Point the shared coding-agents.mdx snippet at <BuildWithAgents /> so all
pages that reference CodingAgents now render Skills + MCP, and switch the
langgraph page from <MCPSetup /> to <BuildWithAgents />. The snippet inliner
recurses with cycle protection, so no duplication is needed.
Addresses review feedback on the Build with agents page:
- Add a top-three skills table (copilotkit-setup / -develop / -integrations)
and call out that copilotkit-contribute is for working on CopilotKit
itself, not building with it, so the skills directory's build-vs-contribute
split is clear from the docs page.
- Clarify where to run `npx skills add`: from the project root, where any
coding agent (Claude Code, Codex, Cursor, Gemini CLI) discovers the skills
automatically — answering 'in your agent environment'.
- Demote the MCP per-tool section headers (Cursor, Claude Web, Claude Code,
...) from H2 to H3 so they nest under 'MCP Docs Server' in the on-this-page
TOC instead of sitting as flat siblings; demote the 'Other' subsections to
H4 accordingly.
Use `CopilotKit/CopilotKit/skills -y` instead of the repo root: root
discovery sweeps in the internal `showcase-demo-debugging` skill
(metadata.internal, lives in .claude/.agents, not skills/), so users got
12 skills incl. one internal. The /skills subpath yields exactly the 11
published skills. Drop -g so install defaults to project scope, letting each
project pin the skills version matching its CopilotKit dependencies.
The build-with-agents guide recommended a bare `npx skills add` that drops
human users into a multi-step interactive flow (skill multiselect, agent
selection, scope, install method, confirm). Recommend `-g -y` so all skills
install globally in one shot, with a Callout pointing to the flag-less command
for users who want to choose interactively.
## Problem
Shell-docs had conflicting v2 guidance around the provider import path.
Some migration/reference/quickstart pages either recommended
`CopilotKitProvider` or kept `CopilotKit` examples on the root
`@copilotkit/react-core` package even though v2 docs should import the
`CopilotKit` component from `@copilotkit/react-core/v2`.
## Why
The correct recommendation is the `CopilotKit` component name, imported
from the v2 entrypoint. Leaving root-package imports in v2-facing docs
makes the migration and reference guidance contradict the v2 package
layout.
## Fix
- Recommend `CopilotKit` from `@copilotkit/react-core/v2`, not
`CopilotKitProvider`.
- Update v2 migration, reference, and quickstart examples to use the v2
provider/style entrypoints.
- Leave root `@copilotkit/react-core` imports only in v1 docs and
explicit migration “Before” examples.
- Add regression coverage for stale provider/style package paths.
- Fix the shell-docs SignupLink SSR test typing exposed by typecheck.
Closes#5153
The reference route rendered fenced code blocks as bare, unstyled
<pre> (no highlighting, no copy button) because its MDXRemote call
omitted the rehypeCode plugin and the pre: MdxCodeBlock override that
the main docs pipeline uses. Wire both in (verbatim from the framework
route) so reference code blocks match the rest of the docs. Fixes
rendering for all reference SDKs (React v2/v1 + Core).
Introduces a third SDK in the reference docs alongside React v2 and v1:
- reference-items.ts: add the 'core' version, generalize root-vs-nested
routing, add 'types'/'enums' subdirs + categories, recognize a core/
slug prefix (literal strip), and emit its static params
- reference-version-selector.tsx: relabel the picker as an SDK switch
(React v2 / React v1 / Core (TypeScript)), import ReferenceVersion from
reference-items, give listbox options role=option/aria-selected
- app/reference/page.tsx: rename 'API Reference' to 'Overview' and add a
'Choose your SDK' card chooser
Clarify the wrapper's role (it forces noStore:false because unstable_noStore is
unavailable in middleware/Edge). Pure rename across shell, shell-docs, and
shell-dashboard: definitions, middleware call sites, and tests. No behavior
change.
Addresses PR #5087 review comment from @tylerslaton:
> When we open the cookbook section, the sidebar should update to include
> only the recipes. Similar to how Reference works today.
Mirror the dedicated-route approach Reference uses (app/reference/page.tsx
+ app/reference/[...slug]/page.tsx), reusing the existing MDX flow via
DocsPageView's pre-built navTree prop:
- app/cookbook/page.tsx — landing route. Builds a navTree scoped to the
cookbook subdir (buildNavTree(CONTENT_DIR/cookbook, 'cookbook')) and
passes it to DocsPageView so the sidebar shows only cookbook entries.
- app/cookbook/[...slug]/page.tsx — catch-all for /cookbook/<recipe>,
using the same scoped navTree.
- Remove the '---Cookbook---' divider and '...cookbook' spread from
showcase/shell-docs/src/content/docs/meta.json so cookbook no longer
appears in the Documentation sidebar (only via the navbar tab).
Verified locally:
- /cookbook and /cookbook/daytona serve 200 with sidebar scoped to
Overview + Daytona only (active highlight tracks the current page).
- / has exactly one /cookbook anchor — the navbar tab — and no cookbook
entries in the Documentation sidebar.
- /built-in-agent sidebar has zero /cookbook entries.
OSS-222
Non-functional cleanup pass on the showcase deploy-pipeline integration
branch. All changes are scoped to comment rot, log severity for already-
demoted runtime-config fields, length-aware env-name coalescing (a
deliberately-empty primary no longer masks a populated alternate), and
test-quality tightening. No production behavior change beyond the
specific items below.
Changes by area:
- shell/shell-dashboard/shell-docs runtime-config.ts: factor the
`process.env[primary] ?? process.env[alt]` chain into a shared
length-aware `readEnvPair` helper. The prior `??` form treated
`PRIMARY=""` as set, masking a populated alternate; the helper now
treats empty-string as unset and falls through to the alternate.
- shell-docs runtime-config.ts: demote the two recoverable URL fields
(`intelligenceSignupUrl`, `posthogHost`) from console.info to
console.warn. The `FATAL-CONFIG:` Sentry-alert prefix is preserved
only on the true sentinels; the demoted fields now clear prod log-
aggregation thresholds without raising ops alerts.
- All three shells' runtime-config.ts: prefix log lines with the shell
name (e.g. `[shell-docs runtime-config]`) so the shared log stream
identifies which shell emitted the line.
- shell-docs runtime-config-serialize.ts: rewrite the U+2028 / U+2029
RegExp arguments using six-character ASCII backslash-u escape
sequences (was: literal codepoints in the string arg). The literal
codepoints are line terminators that a formatter or editor could
silently strip, breaking the security-critical XSS escape. The
ASCII form is robust to any such pass.
- shell-docs use-google-analytics.test.ts: de-tautologize the hook-
order test. It now asserts `usePathname(` and `useEffect(` both
exist in the source, so deleting all hooks would fail the test
rather than trivially satisfying the early-return path.
- shell-dashboard baseline-types.test.ts: update the partner-count
expectation from 25 to 26 -- the 26th entry (Cloudflare) is a
legitimate integration that landed independently; the test was
stale and had nothing to do with this branch.
- scripts/resolve-verify-matrix.ts: drop the `FIX 7 --` plan-
internal prefix from a comment; keep the explanation.
- shell-docs/.env.example: correct the `NEXT_PUBLIC_SHELL_URL`
fallback claim (sentinel, not canonical prod host) and document
the remaining 7 consumed env vars with their FATAL/warn/silent
semantics so the example matches runtime-config.ts.
Skipped:
- C-SENTINEL-DEDUP (`http://ops.invalid` shared constant across
shell-dashboard's next.config.ts and runtime-config.ts): both
files are at different module levels (root vs src/lib) and the
string appears once in each; extracting to a shared module would
widen the diff into a refactor for marginal benefit. Skipped per
the spec's "if it widens diff awkwardly, skip" guidance.
- C-SSRTEST: already exhaustively covered. Each of the three shells
has an SSR placeholder test that exercises every URL field via
`new URL()` parseability and (for shell-docs) the analytics-key
empty-string semantics. Treated as a no-op.
Validation: shell + shell-dashboard + shell-docs runtime-config /
serialize / GA tests green; bin/showcase Ruby suite green (87 runs);
showcase/scripts resolve-verify-matrix + aggregate-build-results +
lint-rule-no-public-env green (79 runs).
Six fixes addressing CR findings on the Option-B runtime URL-injection migration:
1. SSR_PLACEHOLDER must be parseable URL sentinels — `new URL("")` throws on
SSR causing 500s for any consumer that constructs URLs from runtime-config
fields. Use `.invalid`-TLD sentinels (RFC 2606) for URL fields; analytics
keys stay empty string. Add `suppressHydrationWarning` on consumers that
render the placeholder server-side and the real value post-hydration
(integration-grid, page-actions popover).
2. Hook-order: move `usePathname()`/`useEffect` ABOVE the early-return in
use-google-analytics. Gate the effect bodies on `GA_ID` instead so React
sees a stable hook order across renders.
3. `readUrl`/`readKey` accept either bare or `NEXT_PUBLIC_*`-prefixed env
names via a fallback chain — covers both server-only and inlined-public
variable conventions without forcing a rename across deploy targets.
4. Extract `serializeRuntimeConfig` to `lib/runtime-config-serialize.ts` so
the OWASP-escape behavior (XSS via </script>, U+2028/U+2029 line-terminator
injection) can be unit-tested without importing the layout into vitest.
5. Reclassify `intelligenceSignupUrl`/`posthogHost` from FATAL-CONFIG to
info-level in shell-docs — these are optional integrations, not hard
wiring failures, so absence should not poison the error stream.
6. Comment-rot cleanup: drop "Option B", B12, "the bug we are fixing", fix
"four substrings"→"three substrings" miscounts, and refresh shell-docs
.env.example to describe the runtime-injection contract instead of a
stale next.config throw claim.
V1: shell + shell-docs `next build` succeeds (no Edge-runtime crash on
`unstable_noStore`).
V2: `OPS_BASE_URL=` shell-dashboard `next build` no longer throws —
`next.config.ts` is now a phase-aware function that emits a sentinel
destination at build time and throws only at start (PHASE_PRODUCTION_BUILD
from next/constants).
Tests: shell-docs 72/72, shell 12/12, shell-dashboard runtime-config 16/16
(pre-existing baseline-partner-count failure unchanged).
getRuntimeConfig() in each shell's runtime-config.client.ts threw when
typeof window === 'undefined'. But Next.js App Router executes 'use
client' component bodies on the SERVER during initial SSR, so any client
component that called getRuntimeConfig() in its render body 500'd the
page. shell-dashboard already had the fix.
Mirror shell-dashboard's pattern: return a typed SSR_PLACEHOLDER (empty
strings for URL/key fields; {} for shell-dojo whose RuntimeConfig is
empty) when window is undefined. Keep the loud throw when window IS
present but window.__SHOWCASE_CONFIG__ is missing — that's a genuine
wiring bug and should not be masked.
Updated shell-docs and shell client tests: replace 'throws on server'
case with 'returns SSR sentinel placeholder' assertion matching each
shell's RuntimeConfig shape. shell-dojo has no client test so verified
via tsc only.
Two coupled changes that complete the Option B runtime-injection
switch for shell-docs:
- app/sitemap.ts + app/robots.ts: add `export const dynamic =
"force-dynamic"` so Next.js regenerates both routes per request.
Without this Next would statically prerender them at build time
and freeze whichever NEXT_PUBLIC_BASE_URL was set during
`next build` — the exact freeze that defeats the runtime-config
plumbing.
- next.config.ts: REMOVE the `next build` gates that threw when
NEXT_PUBLIC_BASE_URL or NEXT_PUBLIC_SHELL_URL were unset. Those
gates assumed build-time URL injection. Under Option B both URLs
are read per-request from process.env via getRuntimeConfig(), so
a single built artifact must be allowed to build with neither var
set — they're a deploy-time concern now. Missing-value surfacing
moves to `console.error` from runtime-config.ts at request time.
Migrate the remaining shell-docs consumers of NEXT_PUBLIC_* env vars
off of process.env reads and onto the runtime-config readers:
- lib/providers/posthog-provider.tsx (client): the PostHog key is
pulled inside PostHogProvider so the value reflects the current
deploy's NEXT_PUBLIC_POSTHOG_KEY; empty string disables analytics
via the existing truthiness gates around init/capture
- lib/providers/scarf-pixel.tsx (client): pixel id read at render
time; empty string returns null (existing no-op behavior preserved)
- lib/hooks/use-google-analytics.tsx (client): GA tracking id read
at hook invocation; empty string short-circuits via the existing
`if (!GA_ID) return` guard
- middleware.ts (Edge): POSTHOG_HOST is now resolved per-request via
getRuntimeConfigEdge() (the Edge wrapper skips unstable_noStore()
since next/cache is unavailable there and middleware always runs
per-request anyway). Server-side POSTHOG_KEY is unchanged — it's
not a NEXT_PUBLIC_* var.
Migrate every consumer of NEXT_PUBLIC_BASE_URL / NEXT_PUBLIC_SHELL_URL
/ NEXT_PUBLIC_INTELLIGENCE_SIGNUP_URL in the shell-docs tree off of
process.env reads and onto the runtime-config readers:
- lib/sitemap-helpers.ts (server): getBaseUrl() now delegates to
getRuntimeConfig().baseUrl
- components/search-modal.tsx (client): reads shellHost once per
render and threads it into normalizeHref() + the integration href
builder; normalizeHref is now parameterized rather than closing
over a module-scope SHELL_HOST
- components/integration-grid.tsx (client): reads shellHost after
the framework-scoped early return so we never touch the client
reader on no-op renders
- components/ai/page-actions.tsx (client): getClientBaseUrl()
delegates to getRuntimeConfig().baseUrl; the local inline copy is
retained so a "use client" file doesn't reach into the Node-only
sitemap-helpers module
- components/react/signup-link.tsx + ops-platform-cta.tsx (client):
buildHref() reads the signup URL lazily inside the function body
so the URL reflects the current deploy's env
Module-scope reads of process.env.NEXT_PUBLIC_* are eliminated from
all six files — every URL is now resolved at render time from the
runtime-config object the root layout injects.
Add inline <script> tag as the first child of <head> that populates
window.__SHOWCASE_CONFIG__ with values read from process.env at request
time via getRuntimeConfig(). This is the server side of the Option B
runtime-injection plumbing — every shell-docs client component will
read its URLs/analytics keys from this object instead of compiled-in
NEXT_PUBLIC_* values, so a single built artifact can serve staging
and prod by changing Railway env vars.
The serializer escapes < (XSS guard against </script> breakout) plus
U+2028 / U+2029 (legal in JSON, illegal in JS string literals when the
page is parsed as text/javascript) per OWASP guidance. The regex
sources are constructed via new RegExp(String.fromCharCode(...)) to
sidestep the TypeScript tokenizer treating the literal codepoints as
line terminators inside /regex/ literals.
Drop in NODE_ENV writes via Record<string,string> cast in the server
test — modern @types/node marks NODE_ENV read-only, and the runtime
reader only inspects the string value.
Adds the server (runtime-config.ts) and client
(runtime-config.client.ts) runtime config readers for shell-docs as
the foundation of Option B's per-request URL/key resolution. The
server module reads from process.env at request time (gated by
unstable_noStore so callers are not statically prerendered); a thin
getRuntimeConfigEdge wrapper skips the cache opt-out for middleware
(Edge runtime cannot import next/cache). The client module reads
window.__SHOWCASE_CONFIG__ which the root layout will inject in the
next commit.
Red-green: verified the test files fail without the modules
(module-not-found) and pass once the modules land — 10/10 green.
Part of plan-B (showcase per-env runtime URL injection).
Implements plan-B B11. URL and analytics NEXT_PUBLIC_* values now reach
each shell at runtime via Option B (env-driven runtime-config), so the
GHA showcase_build.yml workflow no longer threads them through as Docker
build-args and the shell-dashboard/shell-docs Dockerfiles no longer
declare the matching ARG/ENV pairs.
- showcase_build.yml: shell-dashboard and shell-docs matrix entries lose
build_args_pb_url / build_args_shell_url / build_args_ops_url /
build_args_base_url / build_args_analytics; the 'Prepare build args'
step drops the corresponding env: keys and if-branches plus the five
analytics NEXT_PUBLIC_* secrets. COMMIT_SHA and BRANCH stay — they
identify the artifact.
- showcase/shell-dashboard/Dockerfile: remove ARG/ENV for
NEXT_PUBLIC_SHELL_URL, NEXT_PUBLIC_POCKETBASE_URL, OPS_BASE_URL plus
the explanatory comments. Update the runner-stage comment to point at
runtime-config.ts as the new source of truth.
- showcase/shell-docs/Dockerfile: remove ARG/ENV for
NEXT_PUBLIC_BASE_URL, NEXT_PUBLIC_SHELL_URL, NEXT_PUBLIC_POSTHOG_KEY,
NEXT_PUBLIC_REB2B_KEY, NEXT_PUBLIC_SCARF_PIXEL_ID, NEXT_PUBLIC_REO_KEY,
NEXT_PUBLIC_GOOGLE_ANALYTICS_TRACKING_ID. COMMIT_SHA / BRANCH retained.
shell/Dockerfile and shell-dojo/Dockerfile already only declare commit-sha
and branch ARGs — no changes needed there (per plan-B B11.4).
Three coupled bugs surfaced from the BIA-as-default cutover, all hitting
the A2UI snippet rendered on /built-in-agent/generative-ui/a2ui:
1. Framework-scoped link rewriter (docs-page-view) blindly prefixed
every root-relative MDX href with the active framework slug, so
`/a2a/generative-ui/declarative-a2ui` rendered as
`/built-in-agent/a2a/generative-ui/declarative-a2ui` (404). Now skip
the rewrite when the first URL segment matches a known framework
slug (registry integrations + docs-only a2a/agent-spec/deepagents)
or a reserved top-level route (/docs, /ag-ui, /reference, /api).
2. Snippet `shared/generative-ui/a2ui.mdx` "Learn More" block linked at
legacy paths (`/generative-ui/specs`, `/ag-ui-protocol`,
`/generative-ui/specs/*`) that the IA retired. Updated to canonical
destinations (`/concepts/generative-ui-overview`,
`/agentic-protocols/ag-ui`, `/generative-ui/<spec>`) so the
framework-prefix rewriter produces valid framework-scoped URLs.
3. S13 redirect (concepts/* -> framework root) was generated for every
legacy framework slug including those whose canonical slug didn't
change (mastra, ag2, agno, ...). The legacy docs never had
/<canonical-slug>/concepts/* pages so the rule never had legitimate
work for them — but shell-docs serves agnostic /concepts/* under
every framework's scope now, and the unconditional rule was 301'ing
those valid URLs to the framework root. Restricted to renamed
frameworks only.
Verified locally: every link rendered on
/built-in-agent/generative-ui/a2ui
now resolves to 200; /<unchanged-slug-fw>/concepts/architecture still
serves; /langgraph/concepts/* still collapses to /langgraph-python.
Embed the live demo directly in the cookbook recipe, mirroring the in-doc
iframe approach used by integration landing pages (framework-overview.tsx
liveDemos[] / IframeSwitcher). The recipe now opens with a 'Try it live'
section above the prerequisites, iframing the showcase deployment so
readers can drive the runCode tool against a real Daytona sandbox without
leaving the page.
URL is a placeholder ('showcase-daytona-runcode-production.up.railway.app',
matching the showcase-{slug}-production.up.railway.app backend host
pattern from the registry generator). Will render Railway's not-found
page until a permanent demo deployment is provisioned at that name; the
inline MDX comment notes the placeholder.
OSS-222
Three coupled bugs surfaced from the BIA-as-default cutover, all hitting
the A2UI snippet rendered on /built-in-agent/generative-ui/a2ui:
1. Framework-scoped link rewriter (docs-page-view) blindly prefixed
every root-relative MDX href with the active framework slug, so
`/a2a/generative-ui/declarative-a2ui` rendered as
`/built-in-agent/a2a/generative-ui/declarative-a2ui` (404). Now skip
the rewrite when the first URL segment matches a known framework
slug (registry integrations + docs-only a2a/agent-spec/deepagents)
or a reserved top-level route (/docs, /ag-ui, /reference, /api).
2. Snippet `shared/generative-ui/a2ui.mdx` "Learn More" block linked at
legacy paths (`/generative-ui/specs`, `/ag-ui-protocol`,
`/generative-ui/specs/*`) that the IA retired. Updated to canonical
destinations (`/concepts/generative-ui-overview`,
`/agentic-protocols/ag-ui`, `/generative-ui/<spec>`) so the
framework-prefix rewriter produces valid framework-scoped URLs.
3. S13 redirect (concepts/* -> framework root) was generated for every
legacy framework slug including those whose canonical slug didn't
change (mastra, ag2, agno, ...). The legacy docs never had
/<canonical-slug>/concepts/* pages so the rule never had legitimate
work for them — but shell-docs serves agnostic /concepts/* under
every framework's scope now, and the unconditional rule was 301'ing
those valid URLs to the framework root. Restricted to renamed
frameworks only.
Verified locally: every link rendered on /built-in-agent/generative-ui/a2ui
now resolves to 200; /<unchanged-slug-fw>/concepts/architecture still
serves; /langgraph/concepts/* still collapses to /langgraph-python.
shell-docs renders MDX server-side via MDXRemote, not Fumadocs's compile-
time MDX pipeline, so inline 'import { Boxes } from "lucide-react"' in an
.mdx body doesn't resolve at render time — Boxes ends up undefined and the
page errors with 'Expected component Boxes to be defined' on /cookbook.
Drop the import and the icon prop from the landing's <Card>. Existing
shell-docs pages (e.g. tutorials/ai-powered-textarea/*) use <Card> with no
icon, so this matches the local idiom. The Daytona recipe page is
unaffected because its icon is in frontmatter (icon: 'lucide/Boxes' — a
string, resolved by the page layout, not by MDX body).
OSS-222