First live run of the claude agent (all prior verification used codex)
failed instantly: skillgrade containers run as root, and Claude Code
refuses --dangerously-skip-permissions as root unless IS_SANDBOX=1 marks
the environment as a sandbox, which a skillgrade container is. A claude
wrapper in docker.setup sets it, mirroring the codex wrapper. Without this
every claude run fails in ~1s, including the canonical
pnpm eval:skill:setup flow.
Auth is whatever the forwarded .env provides: ANTHROPIC_API_KEY (Console)
or CLAUDE_CODE_OAUTH_TOKEN from claude setup-token (subscription billing).
Verified live with a subscription token: PASS 1.00, 8/8 deterministic, 61s
(roughly 5x faster than the codex trials).
Installs the codex CLI in the eval image unconditionally, working around
skillgrade 0.1.5 only baking the YAML-default agent into the Dockerfile
(which made the --agent CLI override fail with command-not-found). With
both CLIs present, an OpenAI-only contributor runs the eval against the
canonical claude-default config via:
echo "OPENAI_API_KEY=sk-..." > skill-evals/copilotkit-setup/.env
pnpm exec skillgrade --trials=1 --agent=codex --grader=deterministic
The --grader filter skips the anthropic-keyed rubric; skillgrade
renormalizes reward over the graders that run, so deterministic-only still
scores 0..1 against the same threshold. Verified live: PASS 1.00 with the
key sourced from .env and the YAML untouched.
npx copilotkit skills install runs the skills CLI against skills/, which
copies each skill directory into user projects wholesale (its only excludes
are metadata.json, .git, __pycache__). That would ship eval.yaml and the
vite-react fixture to every user, and the fixture's package.json would get
indexed by GitHub's dependency graph in their repos. The harness now lives
in skill-evals/copilotkit-setup/, pointed back at the skill under test via
skillgrade's skill: field; skills/copilotkit-setup/ contains only shippable
content.
Also instructs eval agents not to start dev servers: a live trial timed out
at 600s because codex booted vite and the Express backend to verify its
(complete) solution, and codex exec never returned. Verified end-to-end
from the new location: PASS 1.00 (8/8 deterministic + rubric, 324s).
The skills/copilotkit-setup/workspace/vite-react fixture is a static eval
workspace copied into skillgrade containers, not a built project; its
vite.config.ts trips the build-config supply-chain guard.
PR #5345 synced SKILL.md to the canonical v2 surface but missed the asset
templates and reference docs, which still taught BuiltInAgent/defineTool
from the nonexistent @copilotkit/agent package, the deprecated
createCopilotEndpoint* factories (with wrong import subpaths), and the old
@copilotkit/react stylesheet path. These files are mounted into agent
context during skill use, so the drift actively contradicted SKILL.md.
All now match: imports from @copilotkit/runtime/v2 and /v2/express,
createCopilotHonoHandler / createCopilotExpressHandler with the mode
option, and @copilotkit/react-core/v2/styles.css. Skill version 1.1.3.
A real skillgrade run surfaced that the skill itself (including its .ts/.tsx
asset templates) gets baked into /workspace/.agents/skills and
/workspace/.claude/skills inside the eval container, where the source greps
matched it: a no-op agent scored 0.62. Graders now exclude those mounts and
the rubrics tell the LLM grader to ignore harness files; a no-op scores 0.00
and a verified end-to-end codex run scores 1.00 (8/8 deterministic + rubric).
The docker setup also gains what codex needs to run in a container at all:
ca-certificates (codex is a Rust binary using the system CA store, absent
from node:20-slim) and a codex wrapper that performs the API-key login
codex 0.1xx+ requires (it ignores the OPENAI_API_KEY env var otherwise) and
swaps --full-auto for the sandbox bypass flag, since codex's Landlock
sandbox cannot initialize inside Docker. All no-ops for claude runs.
The eval.yaml files predated the v2 API cleanup and were never wired to a
runner. This adopts skillgrade (whose schema the setup eval already used)
and syncs the graders with what SKILL.md actually teaches post-#5345:
@copilotkit/react-core + @copilotkit/runtime, the CopilotKit provider from
/v2, and the createCopilot*Handler factories instead of the deprecated
createCopilotEndpoint aliases. Graders now reject the legacy APIs so the
eval measures whether the skill steers agents to the canonical surface.
Adds the previously-missing workspace/vite-react fixture the second task
references, and a manual pnpm eval:skill:setup command (Docker +
ANTHROPIC_API_KEY required); no CI gate yet since runs cost real agent
tokens. Deletes the copilotkit-develop eval outright: it used a different
ad-hoc grader schema, graded for legacy APIs, and had no fixtures, so it
needs a ground-up rewrite once that skill's own API drift is fixed.
## What
Documentation for the new Slack bot stack (`@copilotkit/bot`,
`@copilotkit/bot-ui`, `@copilotkit/bot-slack` — live on npm at 0.0.1),
plus the reference-picker restructure.
### 1. Slack quickstart — `/slack`
Flat **Slack** entry at the top of the **Platforms** sidebar section
(above React Native), with a flat slug matching `/react-native`. Zero →
working bot:
- Create the Slack app **from the checked-in manifest**
(`examples/slack/slack-app-manifest.yaml`), with a callout to delete the
two `assistant:write` / `assistant_thread_started` lines if present
(Slack's validator rejects them without an `assistant_view` block;
conditional phrasing stays correct once `tyler/slack-example-standalone`
lands)
- Tokens (`xoxb-` from OAuth & Permissions after install; `xapp-`
app-level token with `connections:write`), Socket Mode = no public URL
- Gotcha callouts: `/invite` before `app_mention` fires; mention
autocomplete matches the bot **user's** Default username (propagates on
reinstall; full uninstall→reinstall rotates the `xoxb-` token); slash
commands silently dropped unless declared in the app config
- One-file bot (`createBot` + `slack()` + `onMention` →
`thread.runAgent()`, run with `tsx`), then interactive JSX (Button with
inline `onClick`), then `/agent` slash command via `runAgent({ prompt
})`
- ESM-only packaging + in-memory ActionStore restart caveat, and the
production bot/agent split via `AGENT_URL` (mirrors `examples/slack`)
### 2. "Bots" tab in the reference SDK picker
Per-symbol reference modeled on the React (V2) pages (Overview →
Import/Signature → PropertyReference props → Usage → Behavior →
Related), with the sidebar **grouped by package** — each package
separator carries its mark (CopilotKit kite / Slack logo):
- **`@copilotkit/bot`** (separator + kite mark) with collapsed
kind-folders:
- *Components* (13): Message, Header, Section, Markdown, Fields,
Context, Actions, Button, Select, Input, Image, Divider, Table — each
with props, usage, and its Block Kit mapping/budget
- *Functions* (5): createBot, defineBotTool, defineBotCommand,
renderToIR, bind
- *Classes* (1): Thread · *Types* (3): ActionStore, BotNode,
InteractionContext
- **`@copilotkit/bot-slack`** (separator + Slack mark) with a closed
**Core** folder: `slack()` (the adapter — `/reference/bot/slack`),
renderBlockKit (mapping + SLACK_LIMITS budgets), markdownToMrkdwn,
defaultSlackTools, defaultSlackContext, SanitizingHttpAgent
- Wiring follows the in-file recipe (`REFERENCE_VERSIONS += "bot"`, new
`functions`/`slack` subdirs, selector label, Bots card on `/reference`)
plus a bot-specific `buildBotPageTree` for the package-grouped sidebar
### 3. Picker labels + stale SDK pages
- Labels renamed to **React (V2)** / **React (V1)** (Core unchanged)
- Deleted the retired `/reference/sdk/` pages (LangGraph SDK ×2, CrewAI
SDK, CrewAIAgent, LangGraphAGUIAgent, Remote Endpoints);
search/sitemap/llms indexes are generated from the content tree, so they
de-index with the deletion; `sdk` dropped from the v2 subdir list; the
one inbound link retargeted to its `/reference/v1` copy
## Verification
- Every API name verified against package source (`src/index.ts`, type
declarations), not READMEs — caught two README-only patterns that don't
compile (`onClick` one-liners returning `MessageRef`; in-process
`BuiltInAgent` blocked by the `@ag-ui/client` 0.0.53/0.0.56 nominal
split, hence the loopback AG-UI pattern in the quickstart)
- All quickstart/reference snippets assembled into a scratch `.tsx`
project and **typechecked clean** against the built workspace packages
(strict, `jsxImportSource: "@copilotkit/bot-ui"`)
- `npm run build` (production) ✅ · `npm run typecheck` ✅ ·
`oxlint`/`oxfmt --check` on touched app files ✅ · internal link audit:
every `/reference/bot/*` and `/slack` link resolves ✅ · old routes
(`/reference/sdk/*`, `/platform/slack`,
`/reference/bot/functions/slack`) 404, new routes 200 on the dev server
✅
- **Third-party review round**: two independent reviewer agents — a
cold-read new-user pass on the quickstart (verdict: ~25–30% verbose →
trimmed ~26%, callouts 7→4, paste-along ambiguities fixed) and a
source-level correctness audit of all 29 reference pages (~280 claims; 8
errors found and corrected, incl. honest wording for action expiry, what
crosses the wire on a click, Input’s block-level placement, and bind()’s
v1 cold-path caveat). Two of the audit findings are SDK bugs, filed
separately: Input-inside-Actions silently dropped by the Slack renderer,
and bind() `boundArgs` written to the ActionStore but never consumed on
rehydration.
- `npm test`: 90/91 — the 1 failure (`framework-overview.test.tsx`,
"Start the quickstart" CTA copy) **pre-dates this branch** (hero copy
changed in #5248) and touches no file in this diff; flagged separately
## Reviewer checklist
- [ ] `/slack` — try the quickstart against a real workspace (manifest
paste, tokens, `npx tsx bot.tsx`)
- [ ] Sidebar: **Platforms** lists Slack (flat, above React Native);
reference picker shows React (V2) / React (V1) / Core (TypeScript) /
Bots
- [ ] Bots tab sidebar: `@copilotkit/bot` separator (kite mark) with
closed Components/Functions/Classes/Types folders, then
`@copilotkit/bot-slack` separator (16px Slack mark) with a closed Core
folder of the six adapter entries
- [ ] Spot-check API accuracy: Button, slack() (`/reference/bot/slack`),
Thread, ActionStore
- [ ] Manifest callout wording still correct if
`tyler/slack-example-standalone` merges first
- [ ] Comfortable deleting the six `/reference/sdk/` pages with no
redirects (they 404 now; only inbound link was retargeted)
- [ ] OK with the quickstart's single-process loopback pattern
(BuiltInAgent served over AG-UI on :8200) until the `@ag-ui/client`
version split is healed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- New Platforms entry: /platform/slack quickstart — manifest-based app
creation, Socket Mode tokens, minimal createBot bot run with tsx,
interactive JSX with inline onClick, slash commands, production split
- New "Bots" SDK tab in the reference picker with per-symbol pages for
@copilotkit/bot, @copilotkit/bot-ui, and @copilotkit/bot-slack
(Components / Functions / Classes / Types)
- Rename reference picker labels to React (V2) / React (V1)
- Remove the retired /reference/sdk pages (LangGraph/CrewAI SDK,
Remote Endpoints); search/sitemap/llms indexes derive from the
content tree, so they de-index with the deletion
- Retarget the one inbound link to its /reference/v1 copy
Co-Authored-By: Claude <noreply@anthropic.com>
Hotfix for the deployed Kite bot: every Linear-MCP run fails with
```
Agent error: Cannot set property protocolVersion of #<StreamableHTTPClientTransport> which has only a getter
```
**Root cause** — `@copilotkit/runtime@1.59.5` declares `@ai-sdk/mcp:
^1.0.21`. The standalone example lockfile (new in #5366) resolved
**1.0.47**, which (unlike the workspace-tested **1.0.21**) assigns
`transport.protocolVersion` after the server's initialize response — a
getter-only property on `@modelcontextprotocol/sdk@1.29.0`'s transport.
Verified by source diff of both published tarballs; the assignment
exists only in 1.0.47 (`dist/index.js:1950`).
**Fix** — `pnpm.overrides` pin to 1.0.21 in the example + regenerated
standalone lockfile (resolution verified). Workspace installs are
governed by the root manifest and unaffected.
**Upstream** — this combination breaks *any* fresh install of
`@copilotkit/runtime@1.59.5` that uses MCP; a proper compat fix in the
runtime package is filed separately.
Merging this auto-deploys the hosted bot (watch-path on
`examples/slack/**`) — live verification on Kite follows.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
@copilotkit/runtime@1.59.5 declares @ai-sdk/mcp ^1.0.21; fresh installs
resolve 1.0.47, whose MCP client assigns transport.protocolVersion after
the server's initialize response — a getter-only property on
@modelcontextprotocol/sdk@1.29.0's StreamableHTTPClientTransport. Every
MCP-enabled run then fails with:
TypeError: Cannot set property protocolVersion of
#<StreamableHTTPClientTransport> which has only a getter
The workspace-tested resolution was 1.0.21 (no such assignment — verified
by source diff of the published tarballs). Pin it via pnpm.overrides until
@copilotkit/runtime supports the newer client line.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes [OSS-299](https://linear.app/copilotkit/issue/OSS-299).
Follow-up to #5248 (already merged).
## Problem
The `hero_command_copied` PostHog event added in #5248
(`showcase/shell-docs/src/components/hero-start-commands.tsx`) carries
no surface discriminator. `HeroStartActions` renders on **both** the
home hero and **every** framework landing hero:
- The **create** card embeds the framework in `command` (`--framework
langgraph-js`), so it's recoverable.
- The **onboard** card's command (`npx copilotkit@latest skills
onboard`) is byte-identical on every page — so onboard copies **cannot**
be attributed to a surface from the event alone.
Every sibling event in shell-docs already carries a "where" property —
`cli_command_copied` → `location: window.location.pathname`, the nav
events → `location`, `markdown_copied`/`open_in_llm_clicked` → `path`.
`hero_command_copied` was the only one without one.
## Fix
Add `location: window.location.pathname` to the `hero_command_copied`
payload, mirroring the `cli_command_copied` event the global
`<CopyTracker>` already emits for the same copy (verified: it
monkeypatches `navigator.clipboard.writeText`, which the hero calls).
The two paired events now join cleanly on the same dimension. Guarded
for SSR (`typeof window !== "undefined"`) to match the sibling.
## Test
Adds a colocated source-assertion guard test. shell-docs vitest runs in
the `node` environment (no jsdom/RTL), so this follows the suite's
existing convention (`readFileSync` + assertions, like
`brand-nav.test.tsx`) rather than introducing a behavioral render
harness.
```
✓ src/components/__tests__/hero-start-commands.test.tsx (3 tests)
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The Slack example is a **consumer** of the packages we shipped today,
but its `workspace:*` deps forced every deployment to rebuild the
monorepo (and fight nx inside builders — exit-130 city). This PR makes
the example what an example should be: installable and runnable anywhere
with zero monorepo context.
## Changes
- **`workspace:*` → published ranges** (`@copilotkit/bot*` `~0.0.1`,
`@copilotkit/runtime` `^1.59.5`). In-repo it now installs from the
registry like any user's project; `tsx` runs the source directly — there
is no build step anymore, anywhere.
- **Drop the private `@copilotkit/typescript-config` devDep**, inlining
the base compiler options into the example's `tsconfig.json` (identical
`tsc` behavior, verified).
- **Standalone `pnpm-lock.yaml`** inside `examples/slack` so isolated
installs (Railway `rootDirectory`, users copying the folder) are
reproducible. Root workspace installs ignore it.
- **Slack manifest fixes** (both variants): remove the `assistant:write`
scope + `assistant_thread_started` event — Slack's manifest validator
rejects them without an `assistant_view` feature block, and the bot
doesn't implement that surface; add the `/triage` slash command the bot
actually registers (previously had to be added by hand).
## Verification
- `slack-example` tests: **38/38** against the published packages
- Direct `tsc --noEmit`: clean
- Clean-room (gitless snapshot, isolated dir): install from registry →
runtime boots to `listening`, bot boots to a loud Slack auth failure on
dummy tokens (the correct failure)
- Live deploy validation on Railway follows this merge
(rootDirectory=/examples/slack, no build command)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Pin alignment fixes 9 validate-pins FAILs; ratchet the drift baseline
count and hash accordingly. Also tighten the _comment: document the exact
hash recipe (SHA-256 of the stderr-only [FAIL] lines, LC_ALL=C sort -u)
and correct baselineDemoCount semantics (exact expected demo count per
package; deviation either direction warns).
Align showcase integration requirements.txt files (strands,
langgraph-fastapi, langgraph-python, pydantic-ai, google-adk,
crewai-crews) to the fleet pin standard, including an accurate
typing_extensions comment in crewai-crews and a trailing newline in
langgraph-python.
Replace floating "beta" dist-tags with exact versions for @ag-ui/mastra,
@mastra/{client-js,core,libsql,memory}, and mastra in both the examples
and showcase mastra packages. Showcase mastra also raises its zod floor
^3.24.0 -> ^3.25.0. The examples mastra package additionally carries the
fleet-wide @ag-ui/client 0.0.55 bump and single-tree overrides here, since
its manifest mixes both changes.
Bring the starter agents' Python dependency pins (pyproject.toml + uv.lock
for adk, langgraph-fastapi, langgraph-python, pydantic-ai, strands-python;
requirements.txt + docker override for crewai-crews) in line with the
showcase fleet pin standard.
Bump @ag-ui/client 0.0.53 -> 0.0.55 across 8 starter example packages and
add npm overrides pinning @ag-ui/{client,core,encoder,proto} to 0.0.55 so
each install resolves a single @ag-ui tree. The mastra starter receives the
same bump alongside its dist-tag pin fixes in a separate commit.
- dependencies: workspace:* -> published ranges (@copilotkit/bot* ~0.0.1,
@copilotkit/runtime ^1.59.5) — the example is a consumer of the released
packages, installable and deployable with zero monorepo context
- drop the private @copilotkit/typescript-config devDep; inline the base
compiler options into tsconfig.json (verified identical tsc result)
- commit a standalone examples/slack/pnpm-lock.yaml for isolated installs
(root workspace installs ignore it)
- slack-app-manifest.{yaml,json}: remove assistant:write scope +
assistant_thread_started event (Slack rejects them without an
assistant_view feature block; the bot doesn't implement that surface),
add the /triage slash command the bot registers
Verified: slack-example tests 38/38 against the published packages; direct
tsc --noEmit clean; standalone install + runtime/bot boot exercised in a
gitless clean-room snapshot.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round-2 review fixes for the GITHUB_OUTPUT helper and the release scripts
that emit through it.
emitGithubOutputs (scripts/release/lib/github-output.ts):
- Replace the key newline/CR check with a full GitHub-Actions-safe charset
check: /^[A-Za-z_][A-Za-z0-9_-]*$/. A key containing "=" or whitespace
would silently corrupt the key=value line; rejecting up-front is
strictly safer. Value validation (single-line) is unchanged — "=" in
values is legal because GitHub splits on the first "=".
- Update the docblock accordingly.
prerelease.ts:
- Remove the dead `?? getCurrentVersion(scope)` fallback. The empty-list
guard above makes packages[0] guaranteed, and the fallback would have
masked a package.json missing its version field by emitting a version
divergent from what the loop publishes. Fail loudly with an explicit
exit instead.
- Drop the now-unused getCurrentVersion import.
- Add a comment above the dry-run emitGithubOutputs call explaining that
emitting in dry-run is safe — the publish workflow gates publish + the
verify guard on inputs.dry-run != true, so the dry-run emission only
serves local/e2e contract verification.
publish-release.ts:
- Hoist getPackagesForScope + empty-list guard above the prerelease-suffix
and registry checks. A misconfigured scope now fails with the clear
"no packages found" error instead of a misleading "not greater than
published" one. Loop is unchanged.
github-output.test.ts:
- Loosen the key-newline assertion from the JSON.stringify-coupled
/bad\\nkey/ to the stable /alphanumeric/ phrase from the new message.
- Add tests: "=" in key throws, space in key throws, empty key throws,
and "=" in value is accepted and written verbatim (note=a=b).
- Move vi.restoreAllMocks() to the top of afterEach so spies cannot leak
into env restore + rmSync cleanup.
Call sites audited:
- emitGithubOutputs: only ever called with {version, scope} (prerelease,
publish-release) — all valid under the new charset.
- publishVersion derivation: only used inside prerelease.ts main().
- getCurrentVersion: still imported by publish-release.ts, bump-prerelease.ts,
prepare-release.ts; only the prerelease.ts import was removed.
- getPackagesForScope hoist in publish-release.ts: `packages` was only
read inside the publish loop below; nothing earlier depended on it.
Hardens the new GITHUB_OUTPUT emission path so a malformed value can't smuggle
extra `key=value` lines into the workflow's step outputs, and so the workflow's
"Verify publish step emitted version" guard can't be fooled by a publish that
did nothing.
emitGithubOutputs now validates every key/value for `\n`/`\r` BEFORE the
GITHUB_OUTPUT early-return — a malformed value is a caller bug and should fail
loudly even when running locally. A multi-line value would need the heredoc
form, which this helper deliberately does not support.
prerelease.ts and publish-release.ts now fail loud when getPackagesForScope
returns an empty list. Without this, the new GITHUB_OUTPUT emission would make
the workflow's "Verify publish step emitted version" guard pass on a run that
published nothing — previously the missing output made such a run fail. The
guard runs BEFORE the dry-run branch in prerelease.ts. In publish-release.ts,
the inline iteration of getPackagesForScope(scope) is hoisted to a `packages`
const so the same guard fires before the publish loop.
The "no-op when GITHUB_OUTPUT is unset" test now spies on fs.appendFileSync
and asserts it wasn't called (the previous read of the unrelated temp file
was vacuously true). New tests cover newline/CR in value and newline in key.
The prerelease.ts usage string previously advertised `[--suffix <label>]`,
but the script never parses --suffix (suffix handling lives in
bump-prerelease.ts per the header comment). Removed.
Call sites enumerated:
- emitGithubOutputs: prerelease.ts (dry-run + post-publish), publish-release.ts
- getPackagesForScope: prerelease.ts, publish-release.ts (this commit);
bump-prerelease.ts, prepare-release.ts, versions.ts (not changed — out of
scope for this hardening)
Verification:
- npx vitest run --config scripts/release/vitest.config.mts → 91 passed
- Red-green for the newline validation: temporarily removed the validation,
the 3 new newline/CR tests failed (assertion: expected fn to throw); restored,
back to green.
- E2E: GITHUB_OUTPUT="$OUT" pnpm release:prerelease:dry succeeded and the
output file contained `version=1.59.5` and `scope=monorepo`.
Note: Fix 2's empty-list guard fires only on a misconfigured scope (no unit
test reachable — prerelease.ts is outside the vitest include glob and the
guard is boundary validation against a misconfigured scope, not a behavior
worth contriving a test harness for).
prerelease.ts published canaries successfully but never wrote the
version output the publish-release workflow's "Verify publish step
emitted version" guard reads, so every canary dispatch ended red after
a successful publish. Extract the GITHUB_OUTPUT append (previously
inline in publish-release.ts) into a shared lib/github-output.ts helper
and call it from both publish scripts.
Call-site enumeration:
- emitGithubOutputs: declared lib/github-output.ts; called from
prerelease.ts (dry-run path + after publish) and publish-release.ts
(replaces the inline appendFileSync block, same version=/scope= keys).
- No symbols removed; fs import in publish-release.ts still used (3
remaining call sites).
The hero_command_copied event fired by the landing-hero command cards carried
no surface discriminator. HeroStartActions renders on both the home hero and
every framework landing hero; the "onboard" card's command is byte-identical
on every page, so onboard copies could not be attributed to a surface from the
event alone (only the "create" card embeds the framework in `command`).
Add `location: window.location.pathname` to the payload, mirroring the
`cli_command_copied` event the global <CopyTracker> already emits for the same
copy so the two paired events join on the same dimension. Guarded for SSR to
match the sibling.
Adds a source-assertion guard test in the shell-docs node-env convention.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## What
Replaces the landing-page CTA with **three entry points**, framed by
situation, and renders the **identical action block on the home hero and
every framework landing hero**:
| | action |
|---|---|
| **New project** | `npx copilotkit create` |
| **Existing project** | `npx copilotkit skills onboard` |
| **Guided walkthrough** | **Quickstart** button (preserved from the
previous hero) |
- **Unified `<HeroStartActions>` block**: two equal-weight command cards
plus a quickstart row beneath, shared verbatim by the home hero and the
framework landing heroes (per review: the two surfaces previously
diverged).
- **Quickstart preserved** in its original accent treatment. On the home
hero it is the framework-picker dropdown (`<HeroQuickstartDropdown>`,
restored); on framework pages it links straight to that framework's
quickstart guide. The home hero also keeps the "Learn more about
building with agents" link in the same row.
- **Framework landing heroes** (e.g. `/langgraph-typescript`): the
create command **pre-fills the framework** via the CLI's `--framework`
flag (e.g. `--framework langgraph-js`).
**Framework-flag mapping**: docs slug to CLI `--framework` value,
verified against the CLI's `AGENT_FRAMEWORKS` enum
(`langgraph-typescript`→`langgraph-js`,
`langgraph-python`→`langgraph-py`, `google-adk`→`adk`,
`strands`→`aws-strands-py`,
`ms-agent-dotnet`→`microsoft-agent-framework-dotnet`, identical for
`mastra`/`pydantic-ai`/`llamaindex`/`agno`/`ag2`). Slugs with **no** 1:1
CLI template fall back to a bare `npx copilotkit create`, notably
`crewai-crews` (the CLI ships *CrewAI Flows*, not Crews), plus
`langgraph-fastapi`, `claude-sdk-*`, `langroid`, `spring-ai`,
`agent-spec`, `deepagents`. `skills onboard` has no framework flag, so
it is identical everywhere. Frameworks with bespoke setup (`a2a` `git
clone`, `ms-agent-dotnet`) keep the pre-cards layout: quickstart button
plus their own copy-command chip.
**Responsive, with all text always visible.** Commands **wrap, never
truncate**:
- Wraps happen at spaces only; every token is non-breaking, so
`--framework` can never split into a dangling `-` at a line edge.
- `text-wrap: balance` splits multi-line commands evenly, typically
right at the flag boundary (`npx copilotkit@latest create` /
`--framework langgraph-js`).
- The block caps at 740px with 12px mono, the narrowest cap where both
home commands fit one line with enough headroom to survive platform
mono-font width differences.
- Cards sit two-up from `sm` and stack below it; the grid (`min-w-0`,
`items-stretch`) keeps long commands inside their track and the card
pair equal-height.
## Screenshots
**Home**: two cards, quickstart dropdown, learn-more link

**Home, quickstart dropdown open** (framework picker preserved)

**Framework landing (LangGraph)**: same block, framework pre-filled,
create command balanced across two lines, quickstart links to the guide

**Worst case (Microsoft Agent Framework, Python)**: longest CLI flag
value, three balanced lines, fully readable

**Bespoke setup (A2A)**: quickstart button plus own command chip
(pre-cards layout preserved)

**Mobile (375px)**: cards stack, quickstart goes full-width
| home | framework |
|---|---|
| 
| 
|
## Telemetry
Both hero copy buttons are now explicitly instrumented: each click
captures **`hero_command_copied`** (`command_id`: `create` | `onboard`,
full `command` string, `clipboard_blocked`), so create-vs-onboard
funnels are queryable per landing page. The pre-existing global
`cli_command_copied` (fired by `CopyTracker` on any clipboard copy)
still fires for volume metrics; the new event uses a different name so
that funnel is not double-counted. Validated locally against a live
PostHog client: each click POSTs both events (plus `$autocapture`) to
`/ingest/e` with HTTP 200.
## Notes
- Both cards equal weight; accent only on hover. Copy rows copy on click
with `aria-live` feedback plus a clipboard-blocked fallback; cursor is
`pointer`.
- Removes `agent-start-prompt.tsx` and `hero-command-copy.tsx`.
`hero-quickstart-dropdown.tsx` is back (restored unchanged after review
feedback).
Bolt's App constructor schedules a background auth.test that can't be
awaited or error-handled - in unit tests it phoned home to api.slack.com
with dummy tokens, leaving ~15 unhandled invalid_auth rejections racing
the run's end (the unit (20.x) flake). deferInitialization: true makes
construction genuinely side-effect-free; start() runs app.init() first,
so auth/config errors surface to the caller, followed by the existing
awaited auth.test. Test fake App grows the matching init() stub.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Summary
- Live staging redeploy evidence (2026-06-10 16:37Z): 2/6 workers
completed the full SIGTERM → abandon → deregister sequence in **under 1
second**, while 4/6 were SIGKILLed mid-browser-teardown because
Railway's ~10s stop grace is shorter than the old 25s drain budget —
leaving 4 stale roster rows and a reclaim splash on every deploy.
- This PR makes abandon + deregister the **guarded, sub-second critical
path** and demotes teardown to best-effort within a composed <10s
budget, so a platform kill mid-teardown is harmless.
## Design
- `drainFleetWorker` ordering: drain → `registration.stop` → bounded
deregister → graced `worker.stop` → always-run pool shutdown, with
stop-error precedence (a pool-shutdown failure can never mask the stop
error).
- `DRAIN_DEREGISTER_TIMEOUT_MS` (3s) bounds the **whole registration
write chain**, so a hung—not failing—PocketBase cannot consume the kill
window; timeout degrades to the documented crash-path reclaim.
- `safeLog` guards every loop/stop/drain-path log: a throwing logger can
neither reject the worker loop's done-promise nor skip the roster delete
or teardown (abort-before-log in `requestDrain`; structural-caller
guards in `drainFleetWorker`).
- Drain-aware lease renewal (an abandoned job's lease lapses instead of
being re-extended), mid-drain claim skip (a claim won after the drain
decision is never run), and mid-report precision (a run that began
reporting is never logged as abandoned).
- Never-throws loop closure: loop-crash logging via `done.catch` +
`/health` 503, heartbeat and idle-poll sleep hardening with a non-busy
pacing floor, aggregate-key protocol-violation wrap.
- `WORKER_DRAIN_GRACE_MS` default 25s → 6s; the composed 3+6 < 10s
budget is **pinned by a test**; present-but-invalid overrides warn;
overrides at/above ~7s are documented as forfeiting the composed budget.
- Boot-failure teardown catches now log (no silent chromium stranding).
## Review
- 6 unbiased 7-agent CR rounds + 5 fix rounds; every behavioral change
red-green or mutation-proven; `Promise.race` loser semantics empirically
pinned by test.
- ~30 pre-existing harness findings deferred to the flap-fix follow-up
backlog (top of the next fleet-robustness PR: lease-renew
retry-on-throw, empty-registry guard/dispatch mismatch,
`registered`-flag refresh, worker `/health` async bind race, queue fetch
timeouts).
## Test plan
- [x] 2176/2176 vitest (32 new tests)
- [x] `tsc --noEmit` both configs
- [x] oxfmt clean
- [ ] CI green on this PR
🤖 Generated with [Claude Code](https://claude.com/claude-code)
LicenseMode was removed from license-verifier 0.3.0 and has no consumers
anywhere in the repo; the prior re-export was already uncompilable, so no
external consumer could exist either.
The paths entries pointed at sibling package sources, so the Angular
package's tsc run typechecked core and shared sources under Angular's
compiler settings — surfacing errors in files outside any Angular
change (reported on #5321). Resolve to the built declarations first,
same pattern as the Vue package; the src fallback remains for cold
checkouts.
@copilotkit/license-verifier dropped LicenseContextValue and
LicenseMode from its public API in 0.3.0, leaving shared re-exporting
two nonexistent members. tsdown's dts rollup never validated the
re-export, so the broken types shipped silently and check-types fails
on main. Define both types here — shared already owns the context
shape via createLicenseContextValue — using the definitions from
license-verifier 0.2.0. Also annotate the merged telemetry properties
record so string indexing typechecks.
A2UIViewer.tsx and theme/viewer-theme.ts were left behind by the
0.8 -> 0.9 migration: nothing imports them, they import @a2ui/lit
(no longer a dependency) and files that no longer exist, so
check-types fails on files no PR touches. Remove them along with the
now-inert @a2ui/lit external/global entries in tsdown.config.ts, and
underscore the unused type params kept on deprecated aliases for
call-site compatibility.
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.
The reusable mechanics (streaming, chunking, markdown-to-mrkdwn,
conversation store) live on in @copilotkit/bot-slack; UI authoring
moved from A2UI/defineSlackComponent to JSX -> IR -> Block Kit.
JSX -> Block Kit rendering with per-element budgets and degradation,
Socket Mode ingress, opaque-id interactions (ack within 3s, run async),
chat.update message streaming with chunking, accent attachments, and
sender-profile resolution. Preserves the PoC's streaming, chunking,
and mrkdwn mechanics behind the PlatformAdapter boundary.
createBot with handler registration (onMention/onMessage/onInterrupt/
onCommand), the agent run/tool/interrupt loop, content-stable JSX
action binding with cold-path rehydration from a pluggable ActionStore,
the PlatformAdapter boundary, capability-gated thread methods, one
shared BotToolContext, defineBotTool / defineBotCommand, and typed
interaction/interrupt handlers. Includes fake-adapter/fake-agent
testing utilities.
Pure JSX runtime (no React, no Slack) producing a BotNode IR tree.
Statically typed component props via a package-owned JSX namespace:
unknown attributes, bad values, and bad children are compile errors.
Components: Message, Header, Section, Markdown, Field, Context,
Actions, Button, Select, Input, Image, Divider; bind() escape hatch
for non-serializable handler captures.
Every meaningful unit of work gets its own pushed commit; a draft PR
opens on the first commit of a branch and flips to ready only on the
developer's say-so.
Node 25 unflagged the experimental Web Storage API; vitest's jsdom env
does not replace the method-less stub, so localStorage-touching tests
crash. Install a functional stub before the environment boots.
A single unavailable MCP server (down, 5xx, timeout, bad auth) no longer
fails the whole run - it is skipped with an error log and the run
continues with healthy servers and the agent's own tools.
Squashed exploration: agent-agnostic Slack frontend with chat.update
streaming, mrkdwn translation, frontend tools, defineSlackComponent,
HITL pickers, interrupt handlers, and bridge-restart recovery.
Superseded in this PR by the bot/bot-ui/bot-slack rework, which
preserves its streaming, chunking, and conversation-store mechanics.
## Summary
Hardens the `--isolate` showcase verification flow across three areas:
**1. XDG state migration.** Isolate slot registry and per-run
rewritten-compose scratch dirs move off `/tmp` (wiped on reboot,
world-writable) to
`${XDG_STATE_HOME:-$HOME/.local/state}/copilotkit/showcase/` (`slots/` +
`runs/<name>/`). `/tmp` clearing silently destroyed a kept stack's
compose file and slot, making `--keep` unreliable. Run dirs are keyed by
the finalized project name (not PID) so a kept run is locatable for
manual teardown.
**2. Slot reaping + registry concurrency.** Since the state dir is now
persistent, slots are reaped by compose-project liveness (`docker ps
--filter label=com.docker.compose.project=<name>`), with PID/age
heuristics as fallback. The registry is made safe under concurrent
claimers: a sweep lock with heartbeat updates, own-pid lock release, and
tombstones; a claim-then-verify duplicate-name guard closing the TOCTOU
window; crash-safe reap ordering with compose-down of reap remnants and
a path-traversal guard. Failed `--isolate` setup no longer tears down
the default stack; half-initialized state is cleaned up on the way out.
Teardown uses `--volumes` everywhere, and a failed compose-down
preserves state for diagnosis. `--isolate` names are validated (must
start with lowercase letter/digit; `showcase` is reserved — it aliases
the default stack), and a fail-loud warning precedes pre-down of an
existing stack.
**3. `--keep` now actually persists an isolated stack.** Previously the
unconditional `trap restore_isolation EXIT` tore the stack down
regardless of `--keep`. Teardown is now gated on the keep flag
(`ISOLATE_KEEP` promoted to a global so it survives `cmd_test` return
into the trap scope): the slot + run dir are retained and a survival
notice prints the project name, the three offset host ports, and the
exact `docker compose -p <name> down` command — no silent port/slot
leak. A kept stack's live containers keep its slot from being reaped.
Shell-only — confined to `showcase/scripts/cli/_common.sh` +
`cmd-test.sh`; the harness TS only reads the env vars the shell exports
(unchanged). Follows up the `--keep` caveat documented in #5346.
## Review hardening
The branch went through an 8-round, 7-agent code-review loop with
red-green-verified fixes — that loop produced the state-machine
hardening commit (trap-scope fix, default-stack guards, registry
concurrency/teardown robustness, name validation) and grew the test
suite to pin every fix. A live end-to-end `--keep` verification run is
what surfaced the trap-scope bug (`--keep` silently not honored),
driving the `ISOLATE_KEEP` global fix.
## Test plan
- [x] `showcase/scripts/__tests__/isolate.bats` — 41 isolate tests
(red→green): XDG path resolution (+`XDG_STATE_HOME` override,
`~/.local/state` fallback, `runs/<name>`), liveness-based reaping (dead
project reaped/reclaimed, live project preserved), real-trap-path
`--keep` tests (no simulated-trap shortcuts), sweep/lock/tombstone race
pins (heartbeat resurrection, lock takeover, duplicate-name TOCTOU),
reap-order probe pinning live-slot protection, root/PID-reuse/DST
guards, and sentinel anti-vacuity discipline so trap tests cannot pass
vacuously.
- [x] Full `bats showcase/scripts/__tests__/` green, matching CI's Shell
script tests invocation.
- [x] shellcheck: no new warnings.
- [x] Live end-to-end: `bin/showcase test <slug> --d6 --isolate <name>
--keep` persists the stack under `~/.local/state/copilotkit/showcase`,
survival notice + manual teardown work, follow-up run reaps the stale
slot.
Follow-up to #5345.
## What changed
- All skills now recommend `CopilotKit` from `@copilotkit/react-core/v2`
instead of `CopilotKitProvider` (~140 mentions, 32 files). Safe rename:
`CopilotKitProps extends Omit<CopilotKitProviderProps, "children">`, so
every prop carries over.
- CopilotCloud / Copilot Cloud / CopilotKit Cloud mentions replaced with
CopilotKit Intelligence (or deleted). Grep returns zero hits.
- Bonus: react-core's provider-setup.md claimed `publicApiKey` was
canonical. Inverted to match #5345 (`publicLicenseKey` canonical,
`publicApiKey` deprecated alias).
## Review notes
- Half the diff is generated: react-core, runtime, and a2ui-renderer
live in `packages/*/skills/` and are mirrored into `skills/` by `pnpm
sync:plugin-skills`. Review the `packages/` side; the `skills/` side is
a copy. The sync also re-pins `.claude-plugin` versions to 1.59.5 (same
as #5347).
- Remaining `CopilotKitProvider` mentions are intentional: literal file
paths, "do not use" notes, and eval patterns that accept legacy code.
- copilotkit-upgrade tables distinguish old vs new by import path (root
= v1, `/v2` = target) since the component name is unchanged.
- Left alone: endpoint URLs (`api.cloud.copilotkit.ai`), real
identifiers (`MissingPublicApiKeyError`), generic infra terms
(Cloudflare, Google Cloud).
## Summary
- PR #5352's worker-side flap fixes never reached staging automatically:
`harness-workers` runs the same `showcase-harness` image as the
`harness` scheduler, but the SSOT's `ciBuilt: false` conflated "owns a
build slot" with "should be redeployed when its image is rebuilt" — so
main merges redeployed only the scheduler and the workers silently kept
running a stale image (a manual redeploy was required to ship the
fixes).
- This adds an `imageOf` field to the Railway SSOT so a rebuilt image
redeploys **all** of its consumers: the CI redeploy scope is now built
slots ∪ their `imageOf` consumers that declare the target env.
- Staging default scope becomes 27 (26 ciBuilt + `harness-workers` via
expansion); prod is unchanged at 26 (the worker is staging-only and the
expansion is env-aware).
## Design
- `imageOf: "<ssot-key>"` on consumer entries (`harness-workers` →
`harness`), enforced by a module-load invariant
`assertImageConsumersValid`: dangling targets, non-ciBuilt producers,
consumer chains, and consumer envs not a subset of the producer's all
fail loud at import; lookups are prototype-safe (`Object.hasOwn`).
- `expandImageConsumers` in `redeploy-env.ts` performs the env-aware,
single-level expansion and fails loud on unnormalized env names
(synonyms like `production` must go through `resolveEnv`) — the first
real consumer of `ENV_ID_BY_NAME`.
- Service-name resolution (`resolveTargetServices`/`runRedeploy`) now
rejects inherited `Object.prototype` keys with the proper
Unknown-service operator error.
- The explicit `--services` passthrough (a named service is attempted
even in an env it does not declare) is documented and contract-pinned by
a test.
## Review
- 5 unbiased 7-agent CR rounds plus a diff-attribution triage; every
diff-authored finding fixed with red-green proofs.
- ~30 pre-existing script-hygiene findings (env-registry consolidation,
accessor leniency, fetch timeout, parseArgs edges, coverage gaps in
`makeLiveRedeploy`/summary-JSON, etc.) deferred to the flap-fix
follow-up backlog.
## Test plan
- [x] 82/82 vitest (13 new tests: expansion, env-awareness, invariants
incl. prototype keys and env-subset, contract pins)
- [x] `tsc --noEmit -p showcase/scripts/tsconfig.json` clean
- [x] oxfmt clean on all changed files
- [ ] CI green on this PR
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The webhooks SSOT comment claimed the push-driven default scope is
"guaranteed" to leave webhooks untouched — false: a push touching the
build workflow files trips the workflow_config paths-filter disjunct,
which selects every matrix slot (webhooks included; its skip_build slot
still reports success and enters the redeploy CSV). Reworded to state
the actual behavior. imageOf doc now states the enforced NON-EMPTY
subset constraint; serviceEnvPairs doc now truthfully says it has no
consumers yet; file header notes the probe flag default and
bin/railway's Ruby-only "stage" synonym.
Test hygiene: drop the stale bin/railway line-number citation from a
test name, the _envConfigTypeAnchor (EnvironmentConfig is genuinely
referenced by the shape-compile test), a dead eslint-disable, and a
dead `as never` cast (env is an open string); align the webhooks
dispatch-name pin regex with the extraction regex's whitespace
tolerance.