M9: `ENV NODE_ENV=production` at the Dockerfile level propagates to every
process spawned by the entrypoint — including the Python agent. The
aimock toggle (aimock_toggle.py) refuses to apply when `NODE_ENV=production`
is set, so a Docker-based smoke test with `AIMOCK_URL` exported would
silently get a no-op toggle and hit real OpenAI instead of aimock.
Move `NODE_ENV=production` out of the global Dockerfile ENV blocks and
inline it on the `npx next start` invocation in entrypoint.sh. Next.js
still treats the server as production (that's all Next looks at), but the
Python sibling process is no longer subject to a production-guard that
only ever made sense for the JS runtime.
Affected: crewai-crews package Dockerfile + entrypoint.sh, plus the
starter template (Dockerfile.python + entrypoint.template.sh) — the
starter regen in a follow-up commit propagates this to all 17 starters.
M7: Every execution path (package `pnpm dev`, starter `pnpm dev`, Docker
entrypoint, CI workflow) invokes `python -m uvicorn agent_server:app ...`
directly via CLI. The `main()` wrapper reading PORT/RELOAD from env was
dead code that:
- was never exercised by any CI path;
- defaulted PORT=8000 while starter dev scripts bound :8123 (drift);
- carried a RELOAD gate whose only purpose was to guard `reload=True`
in a function that never ran.
Remove `main()` and the `if __name__ == "__main__"` dispatch entirely, plus
the now-unused `os` import. A comment at the bottom of the module documents
why no CLI entrypoint lives here so a future contributor doesn't add one
"for consistency" with other showcase packages.
M6: The prod-guard (`NODE_ENV=production` / `ENV=production` refuses to apply
the toggle) was entering its refusal branch even when AIMOCK_URL was unset
— returning a misleading `"aimock toggle refused"` reason when in fact
nothing was being applied. Move the `aimock_url` non-empty check BEFORE the
prod-guard block; the unset-URL path now falls through to the normal "not
configured" diagnostic, reserving the refusal message for actual refusals.
Regression pinned: `test_production_env_without_aimock_url_is_silent_no_op`
now asserts (a) no WARNING is logged, (b) reason text does not contain
"refused", and (c) neither OPENAI_BASE_URL nor LITELLM_API_BASE is mutated.
LOW: Rename `_AIMOCK_DUMMY_KEY` from `"sk-aimock-DO-NOT-SET-AIMOCK-URL-IN-PROD"`
to `"sk-aimock-dev-ci-only"`. The old name embedded a directive aimed at
operators but the value is never user-provided — the toggle itself injects
it when AIMOCK_URL is set and the prod-guard refuses production anyway.
Short, honest, still satisfies SDK versions that validate `sk-` prefix.
Test `test_dummy_key_does_not_mislead_with_replace_in_prod_suffix` extended
to also reject the old "DO-NOT-SET" directive.
LOW: Pin `LITELLM_API_BASE not in env` in
`test_production_env_refuses_toggle_even_with_aimock_url` so a future
regression that mutates LITELLM_API_BASE in the refusal branch (symmetric
with the existing OPENAI_BASE_URL check) fails the test.
The package's own `pnpm dev` script binds langgraph_cli on port 8123
(via package.json "dev"), but the .env.example documented port 8000.
A contributor running `cp .env.example .env && pnpm dev` would hit
ECONNREFUSED out of the box because nothing binds :8000 in this package.
Generated starters (showcase/starters/langgraph-fastapi/.env.example)
already point at :8123; this aligns the package-side .env.example with
both its own dev script and the scaffolded starter output.
Regenerated from showcase/scripts/generate-starters.ts with the updated
propagation rules. Every starter whose package ships .env.example now carries
one (port-rewritten to :8123 where AGENT_URL points at localhost), and the
crewai-crews starter additionally carries aimock_toggle.py alongside the
agent_server.
OPENAI_API_KEY placeholder text in every package + starter .env.example now
reads 'replace-with-your-key' (not a fake sk-... value), so users who skip
editing get a loud 401 from real OpenAI instead of a silent aimock success
masking misconfig.
Extends generate-starters.ts so every starter whose package ships .env.example
gets the file copied through (not just non-langgraph Python), and ships the
aimock_toggle.py alongside agent_server.py for any Python package that has one.
The .env.example copy rewrites AGENT_URL=http://(localhost|127.0.0.1):8000 to
:8123 during propagation because starter dev scripts bind the agent on 8123
while package dev scripts bind on 8000.
The port-rewrite regex is now exported as AGENT_URL_LOCALHOST_8000_RE and
imported by the starter-consistency test, so the two sides cannot drift.
Previously the test used a broader [^:\/]+ host pattern while the generator
correctly narrowed to localhost/127.0.0.1 — a future package documenting
AGENT_URL=https://api.corp.example:8000 would have tripped the test while the
generator correctly preserved the non-localhost hostname.
Adds an env-controlled aimock toggle for the CrewAI Python agent so dev/CI
traffic can be redirected to a local aimock server while production (AIMOCK_URL
unset) is unchanged. When AIMOCK_URL is set, the toggle populates
OPENAI_BASE_URL + LITELLM_API_BASE and injects a dummy OPENAI_API_KEY if one
isn't already set.
- aimock_toggle.py: configure_aimock() with production-env guard — NODE_ENV /
ENV set to prod/production refuses to apply the toggle (fail-safe WARNING)
so a misconfigured deploy can't silently redirect real traffic through a
mock. Dummy key renamed 'sk-aimock-DO-NOT-SET-AIMOCK-URL-IN-PROD' (was
'sk-aimock-dev-only-REPLACE-IN-PROD' — the old name implied operators
should substitute, but the value is never user-facing). Override warning
now symmetric across OPENAI_BASE_URL and LITELLM_API_BASE (both are
overwritten; both must report overrides).
- agent_server.py: uvicorn reload flag gated on RELOAD env (defaults false)
so 'python agent_server.py' in prod doesn't spawn the reloader.
- requirements.txt: typing_extensions>=4.6; python_version < "3.11" so the
NotRequired fallback only installs on older Pythons.
- Dockerfile: updated COPY comment to match reality (two COPY lines, not
three — agents/ is kept separate because it churns independently).
- pytest.ini + conftest.py cleanup: src/ now on the import path via
pythonpath= declarative config (pytest 7+), removing the sys.path.insert
mutation that leaked past test collection.
- 55 pytest cases (was 38), including red-green proofs for: prod-env guard,
LITELLM override warning, dummy-key rename, whitespace-wrapped falsy
USE_AIMOCK values, and the typing_extensions fallback import path.
## Showcase validation tooling (Bundle 3)
Ships three CLI validators, a shared parsing lib, and two CI workflows
that enforce consistency across the 17 showcase packages and detect
drift before it lands on main. Consolidates four earlier tooling PRs
(#3985, #3987, #3995, #3996).
## What's in the box
### `showcase/scripts/` — three validators
| Tool | Purpose | Exit codes |
|------|---------|-----------|
| `audit.ts` | Cross-checks manifest-declared demos against
`tests/e2e/*.spec.ts` and `qa/*.md`, plus `examples/integrations/`
provenance via `SLUG_TO_EXAMPLES` / `FALLBACK_MAP` | 0 ok, 1 anomalies,
2 invalid-input, 3 unreadable, 4 internal, 5 strict-warnings |
| `validate-pins.ts` | Framework-dep pin-drift between
`showcase/packages/*/` and their dojo `examples/integrations/*/`
counterparts. Parses package.json, requirements.txt, pyproject.toml
(Poetry + PEP 621) | 0 ok, 1 drift, 2 internal, 3 unreadable |
| `validate-parity.ts` | Enforces demo ↔ spec ↔ qa coverage per package
with a monotonic demo-count floor | 0 ok, 1 warnings, 2 invalid-input, 3
unreadable, 4 internal, 5 must-failure |
### `showcase/scripts/lib/` — shared primitives
- **`slug-map.ts`** — single-source-of-truth `ENTRIES` for the showcase
slug taxonomy;
`BORN_IN_SHOWCASE`/`SLUG_MAP`/`SLUG_TO_EXAMPLES`/`FALLBACK_MAP` derived
and frozen at module load. `SlugEntry` is a discriminated union that
makes illegal states (born-in-showcase with non-empty examples)
unrepresentable. `freezeSet`/`freezeMap` helpers install throwing
replacements via `Object.defineProperty({writable:false,
configurable:false})` so `Set.add` / `Map.set` truly fail at runtime.
- **`manifest.ts`** — `parseManifest` returns a tagged `ParsedManifest`
union (`ok` | `missing` | `malformed{subkind: "syntax"|"shape"}` |
`unreadable`) with a never-throws content contract. Uses `statSync` +
errno inspection (not `existsSync`, which conflates ENOENT with EACCES).
`DemoId` is a branded string minted only via `createDemoId`.
### `.github/workflows/` — CI enforcement
- **`showcase_validate.yml`** — runs on PR and push-to-main. Enforces
the e2e-spec floor, runs the validators, and drives the pin-drift
ratchet.
- **`showcase_drift-report.yml`** — weekly Monday 10:00 UTC +
workflow_dispatch. Computes `set_status` (OK / SET DRIFTED / COUNT
DRIFTED) and posts to Slack.
Both workflows run on `depot-ubuntu-24.04-4` (Startup plan, unlimited)
for persistent pnpm/npm cache across runs.
## The pin-drift ratchet
`validate-pins.ts` currently finds **111 existing pin-drift failures**
across 12 showcase packages. Rather than block the PR on those, we
baseline them in `showcase/scripts/fail-baseline.json` and ratchet:
- `validatePinsFailCount` must not increase; CI tells you to ratchet
down when it decreases.
- `validatePinsFailHash` is SHA-256 of the sorted-uniqued `[FAIL]` set.
When the count is equal but the hash differs, a fail healed AND a new
one regressed — CI prints the diff and fails.
- `baselineDemoCount` (9) is the single source of truth for the e2e-spec
floor; consumed by both the workflow and `validate-parity.ts` with sync
enforced by a dedicated regression test.
Tracked in #4047. The 111 failures are mostly showcase packages pinning
`@copilotkit/*` to the `next` dist-tag while dojo pins concrete
versions; direction of fix (align showcase → dojo vs. bump dojo →
showcase) is a separate versioning decision outside this PR.
## Correctness posture
- **977 tests**, 13 files, covering every `Anomaly` / `PackageIssue` /
`ParsedManifest` variant in-process and via subprocess CLI for every
exit code. EACCES/ENOTDIR/TOCTOU paths are exercised via chmod probes
(with `it.skipIf` fallback when CI runs as root) and path-filtered
`vi.spyOn` fall-throughs.
- **`fs.statSync` + errno everywhere** — `fs.existsSync` silently
collapses ENOENT with EACCES and is a known anti-pattern in validation
tooling; the codebase uses structured errno discrimination throughout.
- **Tagged discriminated unions with exhaustive `switch` + `never`
guards** — `bucketFor` in `audit.ts`, `deriveMessage` in
`validate-parity.ts`. Adding a new variant without wiring every site is
a compile error.
- **Partial-report preservation** — when an infra error hits
mid-slug-loop, `UnreadableInputError.partialReport` carries
already-collected drift findings so the top-level catch prints them
before exiting 3. One bad package never orphans signal for the rest.
- **Per-slug isolation** — in `validate-parity.ts runParityImpl`, each
slug's audit is wrapped; a crash surfaces as a `crashed` `PackageIssue`
and forces `EXIT_INTERNAL` without aborting siblings.
- **Pipefail + scoped `|| true`** — every workflow step uses `set -euo
pipefail` with grep's no-match tolerance wrapped in `{ grep || true; }`
so producer failures (sort, shasum, cut) still surface.
## Diff
+16,455 / −18 across 34 files (26 source + 5 fixture trees + 2 workflows
+ 1 baseline).
Commits grouped by purpose:
1. `chore(showcase/scripts)`: vitest config + test deps
2. `feat(showcase/scripts)`: shared slug-map and manifest parsing lib
3. `feat(showcase/scripts)`: audit.ts coverage auditor
4. `feat(showcase/scripts)`: validate-pins.ts pin-drift validator
5. `feat(showcase/scripts)`: validate-parity.ts demo/spec/qa parity
validator
6. `ci(showcase)`: validation + weekly drift-report workflows (Depot
runners)
## Test plan
- [x] `pnpm vitest run` in `showcase/scripts/` — 977/977 green
- [x] Exit-code taxonomy verified end-to-end via subprocess tests for
every documented code
- [x] EACCES/ENOENT/ENOTDIR routing verified in all three validators
- [x] Partial-report preservation verified in both in-process and
subprocess paths
- [x] Per-slug crash isolation verified (one broken slug does not orphan
siblings)
- [x] Baseline sync contract (`BASELINE_DEMO_COUNT` ↔
`fail-baseline.json.baselineDemoCount`) pinned by test
- [ ] First CI run on Depot to confirm cold-cache timing (expected 5–8m
vs. 18–20m on ubuntu-latest)
Refs: [Full Action
Inventory](https://www.notion.so/3443aa38185281b5a1dfc6e0890264e1),
#4047
Audits each showcase package's manifest-declared demos for matching
spec (tests/e2e/*.spec.ts) and qa/*.md coverage, and enforces a
monotonic demo-count baseline via fail-baseline.json.
Key design:
- PackageIssue tagged union (13 variants) cleanly separates MUST
errors from warnings; deriveMessage is the single renderer so new
variants cannot emit mismatched prose.
- ProbeResult tagged union (missing | ok | unreadable) driven by
statSync + errno inspection, distinguishing ENOENT from EACCES and
surfacing ENOTDIR as a misconfiguration rather than a silent miss.
- runParityImpl isolates each slug's audit in try/catch; a crash in
one slug surfaces as a crashed PackageIssue variant and forces
EXIT_INTERNAL without aborting siblings.
- runParity never throws for content errors. InvalidBaselineError
covers coerceBaseline failures; unknown errors route to
EXIT_INTERNAL via formatErrorChain (walks .cause with cycle
guard + depth cap).
- parseMainArgs rejects unrecognised flags and duplicate --baseline
with EXIT_INVALID_INPUT (2), mirroring audit.ts parseArgs
discipline.
- BASELINE_DEMO_COUNT default must match
fail-baseline.json.baselineDemoCount; enforced by
__tests__/baseline-sync.test.ts.
- Exit codes: 0 ok, 1 should-warnings-only, 2 invalid-input, 3
unreadable, 4 internal, 5 must-failure.
Tests cover every PackageIssue variant, every exit code
(in-process + subprocess), per-slug crash isolation, EACCES routing,
ENOTDIR classification, cascade suppression when tests/e2e or qa
dirs are unreadable, and baseline coercion edge cases (leading
zeros, negative, float, hex, non-numeric).
Compares framework dependency pins across showcase/packages/*/ and
the corresponding dojo examples/integrations/* trees, flagging drift
between the two and rejecting non-exact specs on the showcase side.
Key design:
- Parses package.json, requirements.txt, and pyproject.toml
(including Poetry's [tool.poetry.dependencies] and PEP 621
[project.dependencies] / optional-dependencies). Separate jsDeps
and pythonDeps maps prevent cross-ecosystem name collisions.
- isExactSpec enforces exact-version pins per ecosystem (npm: no
operators, workspace refs, or ranges; Python: ==X / ===X / ~=X
with PEP 440 body). Symmetric rejection of bare MAJOR-only forms.
- parseRequirementsTxt and parsePyprojectToml thin wrappers throw
when the detailed form produced skipped[] or dropped[] entries,
preventing silent data loss in simpler callers.
- canonicalizeDepMap canonicalises names per PEP 503 and surfaces
same-file collisions with differing specs as warnings.
- First-writer-wins at both file and package levels.
- UnreadableInputError carries an optional partialReport so an
infra failure mid-slug-loop preserves already-collected drift
findings for other slugs.
- Exit codes: 0 ok, 1 drift, 2 internal, 3 unreadable.
fail-baseline.json is the single source of truth for the CI ratchet
(validatePinsFailCount + validatePinsFailHash) and the demo-count
floor (baselineDemoCount, cross-checked against validate-parity.ts
in a dedicated sync test).
Test coverage spans every parser variant, EACCES routing via chmod
probe + fs spies, exit-code taxonomy subprocess tests, partial-report
preservation on mid-loop infra throws, and Poetry/PEP 503 edge cases
via committed fixture files under __tests__/fixtures/pins/.
Cross-checks each showcase package's manifest-declared demos against
the spec (tests/e2e/) and qa/ directory contents, plus
examples/integrations provenance via SLUG_TO_EXAMPLES / FALLBACK_MAP.
Key design:
- Discriminated Anomaly union with nine variants
(count-mismatch, not-deployed, missing-examples, missing-manifest,
malformed-manifest, unreadable-dir, unreadable-manifest,
unreadable-examples, mapped-candidate-not-directory). bucketFor uses
an exhaustive switch with a never guard so a new variant cannot
silently escape routing.
- CountState tagged union separates known-count, legitimate-missing,
and unreadable cases so an EACCES on tests/e2e/ cannot be
misclassified as a real zero count.
- ExamplesSourceResult carries structured unreadableForSlug /
nonDirectoryForSlug flags; classification never substring-matches
the human-readable warning text.
- SHOWCASE_AUDIT_ROOT env var is validated with statSync + distinct
error messages for ENOENT vs ENOTDIR vs EACCES.
- Text and --json output modes. Exit-code taxonomy: 0 ok, 1 anomalies,
2 invalid-input, 3 unreadable, 4 internal, 5 strict-warnings.
- Deep-freezes AuditReport.packages and anomalies before return.
Tests cover every Anomaly variant, every exit code (in-process + CLI
subprocess), buildReport bucket exhaustiveness, EACCES routing via
path-filtered fs spies, TOCTOU ENOENT races, and the --columns filter
surface.
Two foundational modules consumed by all three validators:
- lib/slug-map.ts: single source of truth for the showcase slug
taxonomy. ENTRIES array is the sole declaration; BORN_IN_SHOWCASE,
SLUG_MAP, SLUG_TO_EXAMPLES, and FALLBACK_MAP are derived at module
load and frozen via freezeSet/freezeMap/freezeMap2D helpers
(defineProperty-based to block Set.add / Map.set at runtime).
SlugEntry is a tagged union: born-in-showcase variants have empty
examples and no fallback; non-born variants carry a non-empty
tuple. Each slug passes isShowcaseSlug at module load.
- lib/manifest.ts: parseManifest returns a tagged ParsedManifest
union (ok | missing | malformed | unreadable) with never-throws
content contract. Uses statSync + errno inspection rather than
existsSync to distinguish ENOENT from EACCES/ENOTDIR. DemoId is a
branded string minted only through createDemoId. Empty-string
dirSlug is rejected as a caller bug; undefined opts out of the
slug-match check. Deep-freezes the returned Manifest.
Configure file-level isolation (fileParallelism: false) to prevent
cross-test env-var contamination when the three validators mutate
process.env.VALIDATE_PARITY_REPO_ROOT / VALIDATE_PINS_REPO_ROOT /
SHOWCASE_AUDIT_ROOT for fixture tmpdirs. Adds scripts test deps to
showcase/scripts/package.json.
## Summary
Two showcase MDX files reference `<TailoredContent>` +
`<TailoredContentOption>` from
`@/components/react/tailored-content.tsx`, but the component file was
missing in showcase. This PR:
- Adds `showcase/shell/src/components/react/tailored-content.tsx`
(copied from upstream `docs/components/`)
- Inlines a tiny `cn()` helper to avoid pulling `classnames` into
showcase/shell deps
- Hardens BOTH copies (docs + showcase, kept byte-identical) against
real bugs found in CR review
## Bugs fixed in TailoredContent (both copies)
- **Build-breaker:** `useSearchParams()` in Next.js 14+ App Router
requires `<Suspense>` wrapper or `next build` fails. Added internal
Suspense wrapper so consumers don't need to add one.
- **State/URL desync:** `selectedIndex` was stored in useState
initialized once — back/forward nav didn't update. Now derived from
`searchParams` each render.
- **Keyboard a11y broken:** `role="tab"` + `tabIndex={0}` with no
keyboard handler. Added standard ARIA tab pattern: `role="tablist"`,
`role="tabpanel"`, Enter/Space to select, ArrowLeft/Right + Home/End to
navigate, roving tabindex.
- **`cloneElement` clobbered caller's icon className:** now merges via
`cn()`.
- **`TailoredContentOption` rendered `<div>` despite JSDoc saying "won't
render":** now returns `null`.
- **Unvalidated `defaultOptionIndex`:** clamped to `[0, options.length -
1]`.
- **Empty options silently produced broken UI:** now `return null`
(hooks-rules-safe; no throw mid-render).
- **Duplicate option IDs silently collided:** `console.warn` in dev mode
(via `useEffect`, not render body).
- **Hooks rules violation:** conditional hook ordering under state
changes (fixed by running all hooks unconditionally).
- **Side effects during render:** `console.warn` mutation moved to
`useEffect`.
- **`options`/`optionIds` unstable identities:** memoized via `useMemo`.
## Known remaining (non-blocking)
- The 2 copies of `tailored-content.tsx` must be kept in sync manually.
Proper fix is a shared package — out of scope for this PR.
- Stale `searchParams` race on rapid concurrent clicks across multiple
TailoredContent widgets on the same page (pre-existing upstream).
- `useMemo([children])` is ineffective since React.Children identity
changes per parent render (minor perf).
## Test plan
- [ ] CI green
- [ ] showcase-shell builds without Suspense errors
- [ ] Visit docs pages with `<TailoredContent>` — tabs render, keyboard
nav works, URL reflects selection
## Summary
Adds a new internal-facing showcase app — `showcase/shell-internal` —
that renders a **feature × integration grid**. Each cell links to one of
two new **canonical standalone routes** on the main `shell` app, or
shows a red ✗ when the feature isn't supported.
## What's new
### 1. Two canonical standalone routes in `showcase/shell`
These give every (integration × feature) pair a single, embeddable URL
for each artifact — useful for docs, marketing, and tooling.
- **`/integrations/[slug]/[demo]/preview`** — iframe-only hosted demo,
no chrome
- **`/integrations/[slug]/[demo]/code`** — code viewer only. Supports
URL params for future refinements:
- `?file=<filename>` — which file tab to show
- `?lines=10-20` or `?lines=10-20,35` — highlight specific line ranges
Example:
`/integrations/langgraph-python/agentic-chat/code?file=page.tsx&lines=15-22`
### 2. `showcase/shell-internal` — a new Next.js app on port 3002
- Single grid page: **rows = features**, **columns =
integrations/frameworks** (transpose of shell's existing `/matrix` page,
which has integrations as rows)
- Each cell has **two mini-links** — green `▶ demo` and blue `</> code`
— pointing at the canonical `shell` routes, or a red `✗` if not
supported
- Reads `showcase/shell/src/data/registry.json` directly via relative
import — single source of truth, no duplicate data
- `NEXT_PUBLIC_SHELL_URL` env var (default `http://localhost:3000`) to
point the cells at a deployed `shell` in non-local environments
### 3. Small fix: drop `--turbopack` from shell's dev script
`showcase/shell`'s Next.js 15.4.10 turbopack panics (`"Next.js package
not found"`) on this repo's multi-lockfile layout. Switching to webpack
dev resolves it; production builds (which don't use turbopack) are
unaffected.
## Why two apps instead of one
`shell-internal` could have hosted the demo and code pages itself, but
keeping them in `shell`:
- Makes the canonical URLs reusable outside internal ops (docs,
marketing, linking into product)
- Avoids duplicating the demo-rendering and code-viewer plumbing across
two apps
Internal shell stays a pure overview.
## Test plan
- [ ] `cd showcase/shell && npm run dev` — confirm shell starts on :3000
(webpack, no turbopack panic)
- [ ] `cd showcase/shell-internal && npm install && npm run dev` —
confirm shell-internal starts on :3002
- [ ] Open http://localhost:3002 — verify the feature × integration grid
renders
- [ ] Click a `▶ demo` cell — verify it opens
`http://localhost:3000/integrations/<slug>/<feature>/preview` with only
the iframe demo
- [ ] Click a `</> code` cell — verify it opens
`http://localhost:3000/integrations/<slug>/<feature>/code` with the code
viewer
- [ ] In the code route, try `?file=<name>` and `?lines=10-20` URL
params — verify file switches and lines highlight
- [ ] Verify red ✗ shows for unsupported (integration × feature)
combinations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
⚠️ **Docs sync — MANUAL REVIEW REQUIRED**
This PR was auto-opened because the docs-sync script detected
showcase-local modifications overlapping with upstream changes.
The script attempted a best-effort 3-way merge:
- Where `git merge-file` produced a clean merge, the merged content was
written.
- Where `git merge-file` produced conflict markers, **upstream content
was written as-is** and showcase-local modifications were overridden.
**Manual review required.**
### Review items
```
Files where 3-way merge FAILED — upstream content written as-is, local modifications overridden. Manual review REQUIRED before merging this PR:
- docs/content/docs/integrations/agent-spec/quickstart.mdx
- docs/content/docs/integrations/aws-strands/generative-ui/state-rendering.mdx
Files auto-merged via 3-way merge (clean, no conflict markers — still worth a glance):
- docs/content/docs/integrations/aws-strands/shared-state/in-app-agent-read.mdx
```
### Source
- Upstream ref:
[`3b8e457a1`](https://github.com/CopilotKit/CopilotKit/commit/3b8e457a1)
- Workflow run:
https://github.com/CopilotKit/CopilotKit/actions/runs/24593085026
**Review before merging.** Auto-merge is intentionally disabled
for `needs-review` PRs — confirm the upstream-wins sections
preserve any intentional showcase-local divergence you want to
keep, then merge manually.
Mirror of #3868 by @Abubakar-01.
Wraps `agent.runAgent(...)` in a `useEffect` with an empty dependency
array in both the docs and showcase MDX files, and adds a prose warning
explaining why calling `runAgent` directly in the component body causes
"thread is already processing" errors.
## Summary
Resolves the recurring `docs-sync` manual-review flag on
`integrations/mastra/agent-app-context.mdx` by applying the sync
script's clean-transform to bring the showcase copy in line with
upstream, plus a small typo fix.
- Typo: `useCopilotReadble` → `useCopilotReadable` (both files)
- Clean-transform sync: restores 2 code-block imports
(`useAgentContext`, `useState` — both actually used in the example on
lines 40 + 32), normalizes whitespace
- Bumps `showcase/shell/.docs-sync-sha` from `41b6eb93` to `0e7811402`
## Note
There is an existing auto-generated PR #4044 covering this same file
(plus 2 aws-strands files). Close that PR after merging this one; the
aws-strands files will be handled by a separate future sync run once the
updated workflow (see branch `ci/docs-sync-auto-pr`) is merged.
## Test plan
- [ ] CI green
- [ ] Post-merge: `npx tsx showcase/scripts/sync-docs-from-main.ts
--dry-run` no longer flags this file for review
Feature matrix (generative-ui category):
- Order: Controlled Gen-UI (Display) -> (Interactive) / In-Chat HITL
-> (Interrupt) -> Declarative Generative UI (A2UI) -> MCP Apps
-> Fully Open-Ended Generative UI -> Agentic Generative UI
(In-Chat State Rendering) -> Tool Rendering.
- Merge `hitl-in-chat` with the Controlled Gen-UI (Interactive) idea;
drop the unused `gen-ui-interactive` placeholder.
- Merge standalone `a2ui` entry into Declarative Generative UI (A2UI);
drop the duplicate.
- Rename `gen-ui-agent` display to "Agentic Generative UI (In-Chat
State Rendering)". Moved `tool-rendering` next to it.
- Rename `gen-ui-tool-based` display from "Controlled Generative UI"
to "Controlled Gen-UI (Display)". Dropped the placeholder
`gen-ui-display` entry (was zero-use).
Agentic chat demo (langgraph-python):
- Removed the "Weather in SF" suggestion and the associated
`useRenderTool`/`get_weather` code. The slimmed agent has no backend
tools, so this demo is now pure chat -- just `CopilotChat`, a user
context tag, and one sonnet suggestion.
Regenerated registry + demo-content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rework the Controlled Generative UI demo to be minimal, self-contained,
and render charts via `useComponent`.
Frontend (`src/app/demos/gen-ui-tool-based/`):
- `page.tsx` -- `CopilotKit` + full-screen `CopilotChat` (no sidebar),
two `useComponent` registrations (`render_bar_chart`,
`render_pie_chart`), three chart-oriented suggestions.
- `bar-chart.tsx`, `pie-chart.tsx` -- ported from
`examples/integrations/langgraph-python`. Each file reads top-to-
bottom as imports -> schema -> props type -> component; colors and
animation helpers are inlined inside the component. No shared
chart-config module.
- Haiku card + schema removed.
Agent (`src/agents/main.py`):
- Rewritten using `create_agent` + `CopilotKitMiddleware()` so the
LangGraph middleware injects the frontend `render_*_chart` tools into
the model request at runtime.
- Deleted all backend-tool modules (`tools.py`, `todos.py`,
`a2ui_dynamic_schema.py`, `a2ui_fixed_schema.py`). System prompt
trimmed to a data-viz assistant.
Peripheral cleanups:
- `demos/agentic-chat/page.tsx` -- `change_background` tool + its
suggestion removed; it had nothing to do with chat.
- New `demos/frontend-tools/` (In-App Actions) hosts
`change_background` as its own demo with a `frontend_tools` agent
name registered in `api/copilotkit/route.ts`.
- Stub `gen-ui-tool-based/agent.py` removed.
- `generate-starters.test.ts` no longer requires every python package
to have backend tool imports -- a package with only frontend tools
(like this controlled-gen-ui demo) is now valid.
- Whitelist generated shell data (`demo-content.json`,
`search-index.json`, `starter-content.json`) in check-binaries hook
-- they're generated artifacts like `package-lock.json`.
- Regenerated `demo-content.json` + `registry.json`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Grid polish:
- Light-mode palette (white surface, slate borders, dark text); dropped
the dark-theme tokens.
- Rows are every declared feature, even ones with zero current support.
Empty rows surface feature gaps we want to fill -- hiding them hides
the TODO.
- Cells: `demo · code` as tight blue text links, `-` for supported but
no demo, red `✗` for unsupported. No colored pill buttons.
- Sticky first column + sticky header row. Hover highlight replaces
striped rows.
Internal sort order:
- New `src/lib/sort-order.ts` -- slug -> rank map used only by this app
(intentionally not in the public registry). Orders columns LangChain
first, then Google/MS/AWS, then the rest. Explicitly internal.
- `getIntegrations()` in lib/registry.ts sorts by this map with a
fallback to the registry's `sort_order`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a single command to spin up the exact image Railway deploys,
for any of the 17 showcase packages, with a single shared .env:
./showcase/scripts/dev-local.sh up [<slug> ...] # all if empty
./showcase/scripts/dev-local.sh down|build|logs|ps|ports
Pieces:
- `docker-compose.local.yml` with a service per package. Ports come
from `shared/local-ports.json` (langgraph-python -> 3100, ...).
- `.env.example` as a commit-safe template. Real `.env` is gitignored
and fed to every container via `env_file`, so keys (OPENAI_API_KEY,
etc.) live in one place.
- `dev-local.sh` wraps `docker compose` and handles the
`shared_python/` / `shared_typescript/` staging step that CI does
before `docker build` (see showcase_deploy.yml).
- Staged `shared_*` dirs added to .gitignore.
Shell wiring:
- `shell/next.config.ts` reads `shared/local-ports.json` when
`SHOWCASE_LOCAL=1` is set and injects it as a public env.
- `/integrations/[slug]/[demo]/preview` uses that map to iframe
`http://localhost:<port>` instead of `integration.backend_url`.
Per-slug; any slug not running locally falls back to Railway.
Unset SHOWCASE_LOCAL -> prod behavior, unchanged.
Full workflow + prerequisites (Colima / Docker Desktop) documented in
showcase/README.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
langgraph-python, langgraph-fastapi, and langgraph-typescript starters run
langgraph_cli dev which exposes /ok (not /health). The template now probes
/health, so the generator must rewrite this back to /ok for langgraph-* only,
otherwise drift-check fails and regenerating would break those starters'
health routes.
Next.js 15.4.10's turbopack panics ("Next.js package not found") on the
repo's multi-lockfile layout. Webpack dev works; production builds are
unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces a new internal-facing showcase app (`showcase/shell-internal`)
that renders a feature × integration grid. Each cell links to one of two
new canonical standalone routes on the main `shell` app:
- `/integrations/[slug]/[demo]/preview` — iframe-only hosted demo
- `/integrations/[slug]/[demo]/code` — code viewer; supports `?file=<name>`
and `?lines=<start>-<end>` URL params for future line-highlighting
Keeping the canonical pages in `shell` means they can also be embedded in
docs/marketing; `shell-internal` is a pure overview with no duplicated
content (reads `shell/src/data/registry.json` directly).
Also drops `--turbopack` from `shell`'s dev script: Next.js 15.4.10's
turbopack panics on the repo's multi-lockfile layout. Webpack dev works
fine and this doesn't affect production builds.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>