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>
The docs-sync workflow flagged these two files as having showcase-local
modifications that blocked the automated transform pipeline:
- docs/content/docs/integrations/langgraph/generative-ui/your-components/interrupt-based.mdx
- docs/content/docs/integrations/langgraph/human-in-the-loop/interrupt-flow.mdx
Upstream docs/ changes pending sync to showcase/:
- Add useInterrupt `agentId: "starterAgent"` to all four examples per file
- Add <Callout type="warn"> explaining that a mismatched agentId silently
never fires the interrupt
- Update stale model name gpt-5.2 -> gpt-5.4 in code snippets
Showcase-local divergence present before this PR:
- Leftover import fragment at the top of each file (bytes from an
incomplete multi-line import strip by an older version of the sync
script, not deliberate content)
- Prettier-style reformatting applied ad hoc (line wrapping in the
Callout info block and the last useInterrupt handler example)
- Stale model name gpt-5.2 (pre-dates the upstream bump to gpt-5.4)
- interrupt-flow.mdx never received the agentId / Callout additions
that interrupt-based.mdx received in 63a6a2733 and 90d1869ea
Resolution: rebuild both showcase files as a clean transform of the
current upstream source. None of the showcase-local changes were
intentional additive content — they were stripping bugs, formatter
drift, and pre-sync staleness — so dropping them and taking the full
upstream reconciles both files cleanly.
Also bumps showcase/shell/.docs-sync-sha to the current main tip so the
next docs-sync run does not re-flag the same files.
## Summary
Adds 5 missing QA markdown files (gen-ui-agent,
shared-state-{read,write,streaming}, subagents) to bring
`showcase/packages/ag2/qa/` from 4 files to 9, matching
`langgraph-python/qa/`.
Part of the QA-parity blitz (Bundle 0 in the action inventory).
### Detail
- `gen-ui-agent.md` and `shared-state-read.md` describe the full
implementations — selectors (`task-progress`, `task-step-text`,
`recipe-card`, `add-ingredient-button`, `improve-button`), labels (`AI
Recipe Assistant`), and suggestion button names match the ag2 page
sources verbatim (they mirror LG-py's implementations word-for-word).
- `shared-state-write.md`, `shared-state-streaming.md`, and
`subagents.md` follow the "Note: Stub Demo" pattern from LG-py — the ag2
pages for these demos are stubs identical to LG-py's stubs, so the QA
coverage is identical.
## Test plan
- [ ] Verify `showcase/packages/ag2/qa/` now contains 9 files matching
the file set in `showcase/packages/langgraph-python/qa/`
- [ ] Spot-check selectors in `gen-ui-agent.md` and
`shared-state-read.md` against the ag2 page sources
## Summary
Follow-up hardening to #3971. aimock supports fixture schema validation
at startup via `--validate-on-load`, but the flag is **opt-in** and the
showcase Dockerfile was not passing it. That meant fixtures with
unrecognized response keys (e.g. `"text"` instead of `"content"`) loaded
silently and only surfaced as HTTP 500s at request time — which is
exactly what crashed crewai-crews and triggered #3971.
This PR wires up two independent safety nets so a broken fixture can't
ship again:
1. **Dockerfile (fail-fast at container boot)** —
`showcase/aimock/Dockerfile` now passes `--validate-on-load`. If any
fixture fails the aimock schema, the container exits non-zero instead of
starting and serving 500s. Railway will not promote a bad build.
2. **CI test (fail-fast in PR review)** — new vitest spec at
`showcase/scripts/__tests__/aimock-fixtures.test.ts` imports
`loadFixtureFile` + `validateFixtures` from `@copilotkit/aimock` and
asserts zero errors against both `feature-parity.json` and `smoke.json`.
Runs inside the existing ` Showcase: Validate` workflow
(`showcase/scripts` vitest suite) on every PR that touches
`showcase/**`.
## Verification
**Red-green on the vitest spec:**
- Rebased onto the tip of main *before* #3971 merged: the spec fails
with 5 errors — exactly the 5 broken `"text"` fixtures (`plan`, `steps`,
`mars`, `dashboard`, `report`) that #3971 repaired.
- Rebased forward onto main *after* #3971: spec passes with 0 errors,
all 549 showcase/scripts tests green.
**Red-green on the Dockerfile:**
- Current fixtures + `--validate-on-load`: container boots cleanly, logs
`Loaded 39 fixture(s) from /fixtures`.
- Injecting an intentionally broken fixture (`response: { "text": "..."
}`): container fails to start with `[aimock] Fixture 0: response is not
a recognized type (must have content, toolCalls, error, or embedding)` /
`Validation failed: 1 error(s), 0 warning(s)` and non-zero exit.
## Test plan
- [x] Local: full `showcase/scripts` vitest suite passes (549/549)
- [x] Local: `pnpm run test` (monorepo) passes
- [x] Docker: image builds and starts with `--validate-on-load` against
current fixtures
- [x] Docker red-green: broken fixture fails container start with
non-zero exit
- [ ] CI: ` Showcase: Validate` job runs the new test file on PR
## Summary
Updates LangGraph human-in-the-loop interrupt documentation so
`useInterrupt`
examples set `agentId: "starterAgent"` and readers see a warning that
`agentId`
must match a runtime-registered agent (default assumption is
`"default"`).
## Changes
-
**`docs/content/docs/integrations/langgraph/human-in-the-loop/interrupt-flow.mdx`**
— warning callout; `agentId` on basic, conditional (`enabled`), and
`handler` snippets.
-
**`docs/content/docs/integrations/langgraph/generative-ui/your-components/interrupt-based.mdx`**
— same.
-
**`showcase/shell/src/content/docs/integrations/langgraph/generative-ui/your-components/interrupt-based.mdx`**
— same content alignment; adds the `useInterrupt` import line in the
primary code block for consistency with main docs.
## Notes
- Production apps should replace `"starterAgent"` with the id their
runtime actually registers.
- If you also want showcase `human-in-the-loop/interrupt-flow.mdx`, hook
reference docs, or a longer callout (e.g. `/info`, example error text),
those are not part of this staged set—add them in a follow-up or stage
them before merge.
## Test plan
- [ ] Open the three MDX pages in the docs/showcase preview and confirm
the callout and snippets render as expected.
Brings showcase/packages/ag2/qa/ from 4 files to 9, matching langgraph-python.
Adds: gen-ui-agent, shared-state-read, shared-state-write, shared-state-streaming, subagents.
Content is adapted from the LG-py templates with titles changed to AG2. The
three stub demos (shared-state-write, shared-state-streaming, subagents) keep
the LG-py 'Note: Stub Demo' pattern since the ag2 implementations are stubs
identical in shape to LG-py's. gen-ui-agent and shared-state-read describe
the full implementations (which mirror LG-py's selectors and labels
verbatim).
## Summary
CrewAI's `ChatWithCrewFlow.__init__` (invoked from
`ag_ui_crewai.endpoint.add_crewai_crew_fastapi_endpoint` at module
import in `ag-ui-crewai <= 0.1.5`) makes synchronous blocking LLM calls
via `crewai.cli.crew_chat.generate_input_description_with_ai` and
`generate_crew_description_with_ai`. ANY LLM hiccup — aimock regression,
OpenAI outage, network blip, DNS failure — crashes the Python process
BEFORE uvicorn can bind its port, causing Railway/Kubernetes health
checks to fail and deploys to roll back.
This was the direct cause of the crewai-crews Railway crash fixed
server-side in #3971. That fix patched the aimock response schema, but
the underlying fragility in upstream CrewAI / ag-ui-crewai remained — a
future blip would crash us again.
This PR adds a defensive monkey-patch in `agent_server.py` that replaces
both generator functions with static-string returns BEFORE
`ag_ui_crewai` is imported. The AI-generated descriptions are only
surfaced in the CrewAI chat UI (which the CopilotKit runtime does not
use), so static defaults are functionally equivalent for our showcase.
Upstream issue filed: https://github.com/crewAIInc/crewAI/issues/5510
The long-term fix is deferred construction in `ag-ui-crewai`, which has
landed on ag-ui `main` but is not yet released. Remove this shim once
`ag-ui-crewai > 0.1.5` ships.
## Why a monkey-patch and not lazy-init
`add_crewai_crew_fastapi_endpoint` is the entry point and internally
constructs `ChatWithCrewFlow(crew)` synchronously in `ag-ui-crewai <=
0.1.5`. Deferring that call would require either vendoring the endpoint
function or reimplementing it. The monkey-patch is two lines and removes
cleanly when the upstream fix ships.
## Test plan
Verified locally via Docker build + run with an intentionally broken LLM
endpoint (`OPENAI_BASE_URL=http://invalid-host/v1`):
**Unhardened (negative control):**
```
File "/app/agent_server.py", line 27, in <module>
add_crewai_crew_fastapi_endpoint(app, LatestAiDevelopment(), "/")
File ".../ag_ui_crewai/endpoint.py", line 250, in add_crewai_crew_fastapi_endpoint
add_crewai_flow_fastapi_endpoint(app, ChatWithCrewFlow(crew=crew), path)
File ".../ag_ui_crewai/crews.py", line 56, in __init__
self.crew_chat_inputs = crew_chat_generate_crew_chat_inputs(...)
File ".../crewai/cli/crew_chat.py", line 387, in generate_crew_chat_inputs
description = generate_input_description_with_ai(input_name, crew, chat_llm)
File ".../crewai/cli/crew_chat.py", line 481, in generate_input_description_with_ai
response = chat_llm.call(...)
APIError
```
Container exits with code 1, never binds a port.
**Hardened (this PR):**
```
INFO: Started server process [7]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
```
`curl http://localhost:PORT/api/health` ->
`{"status":"ok","integration":"crewai-crews","agent":"ok","timestamp":"..."}`
(HTTP 200).
### Checklist
- [x] Docker build succeeds locally
- [x] Unhardened build crashes on import with broken LLM endpoint
(negative control)
- [x] Hardened build starts cleanly with broken LLM endpoint and
responds 200 on `/api/health`
- [x] Upstream issue filed on crewAIInc/crewAI
## Summary
- The Mastra showcase package registers only `weatherAgent` in its
Mastra config, but the demo pages request names like `agentic_chat`,
`human_in_the_loop`, `tool-rendering`, etc.
`MastraAgent.getLocalAgents({ mastra })` returns agents keyed by
registration name, so the runtime cannot resolve the demo-requested
names. This was causing the L3 mastra chat test to fail.
- Mirror the pattern used in
`showcase/packages/crewai-crews/src/app/api/copilotkit/route.ts`: wrap
`MastraAgent.getLocalAgents` and expose the underlying `weatherAgent`
under every name the demo pages ask for. `weatherAgent` itself is
preserved so the internal `/api/smoke` route (which calls `agentId:
"weatherAgent"`) keeps working.
## Agent names now exposed on the backend
- `weatherAgent` (original Mastra registration — preserved for smoke
test)
- `agentic_chat`
- `human_in_the_loop`
- `tool-rendering`
- `gen-ui-tool-based`
- `gen-ui-agent`
- `shared-state-read`
- `shared-state-write`
- `shared-state-streaming`
- `subagents`
The starter (`showcase/starters/mastra`) was checked and is not affected
— its route uses `LangGraphAgent` and already registers `sample_agent`,
which matches what the starter's page requests.
## Test plan
- [ ] CI L3 mastra chat test passes (the failure this PR targets)
- [ ] `/api/smoke` route still returns 200 (uses `weatherAgent` which is
preserved)
- [ ] Demo pages (`/demos/agentic-chat`, `/demos/hitl`,
`/demos/tool-rendering`, etc.) can reach the runtime without 404
Addresses CR R1 CRITICAL finding on PR #3974.
setattr() on a Python module always succeeds regardless of prior
attribute existence. Without a guard, an upstream rename of
generate_input_description_with_ai or generate_crew_description_with_ai
in a future crewai release would silently no-op the patch, leaving the
real functions in place. The pre-bind LLM crash bug would quietly
reappear in production with a green PR.
Changes:
- Add hasattr() guard in both agent_server.py files that raises
RuntimeError with an actionable drift message if either symbol
disappears upstream.
- Add post-assignment assert to defend against import-order weirdness
or module re-imports shadowing the reference.
- Add an info-level log line so operators can see the shim is active
and know to remove it after adoption.
- Add upstream issue link (crewAIInc/crewAI#5510) and explicit ag-ui-crewai
release status to the comment block.
- Pin ag-ui-crewai upper bound to <0.1.6 in both requirements.txt files
so the shim's applicability window is enforced by pip — upgrading past
0.1.5 forces the engineer to confront the version mismatch and remove
the shim.
Applied to both showcase/packages/crewai-crews/src/agent_server.py and
showcase/starters/crewai-crews/agent_server.py to keep the demo and
starter trees in sync.
Verified locally:
- Docker build succeeds (showcase/packages/crewai-crews).
- Hardened container starts cleanly with OPENAI_BASE_URL set to an
unreachable host; /health returns 200 on both agent (8000) and
Next.js (10000) ports.
- Negative case: deleting generate_input_description_with_ai from the
installed crewai module inside the container and re-importing
agent_server raises RuntimeError with the upstream-drift message, as
expected.
- Add --validate-on-load to all aimock invocations (4 workflows/scripts
+ 13 integration docker-compose files)
- Replace hardcoded 2-file fixture list with dynamic discovery across
showcase/, examples/integrations/*/, scripts/doc-tests/ (16 fixtures)
- Add sanity check to prevent silent zero-test pass when discovery fails
- Extend showcase_validate.yml path filter to trigger on
examples/integrations/**/fixtures/** and scripts/doc-tests/fixtures/**
- Import and use ValidationResult type for callback parameters
- Fix scripts/doc-tests/fixtures/default.json to use { fixtures: [...] }
envelope shape
Add a sentence to the warning callout explaining that if agentId doesn't
match a runtime-registered agent, the interrupt will silently never fire.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CrewAI's ChatWithCrewFlow.__init__ (called by ag_ui_crewai's
add_crewai_crew_fastapi_endpoint at module import) makes blocking
synchronous LLM calls via generate_input_description_with_ai and
generate_crew_description_with_ai in crewai/cli/crew_chat.py. Any LLM
hiccup (aimock regression, OpenAI outage, network blip) crashes the
Python process before uvicorn can bind, causing Railway healthcheck
failure and deploy rollback.
Patch both functions to return static strings before ag_ui_crewai is
imported. The AI-generated descriptions are only used by the CrewAI
chat UI (not the CopilotKit runtime), so static defaults are
functionally equivalent for our showcase.
Verified via docker build: unhardened image crashes on import with
APIError at crew_chat.py:481; hardened image starts cleanly with an
invalid OPENAI_BASE_URL and responds on /api/health.
Upstream fix (deferred construction) landed on ag-ui main but is not
yet released in ag-ui-crewai > 0.1.5. Remove shim when released.
Upstream issue: https://github.com/crewAIInc/crewAI/issues/5510
Follow-up to #3971. aimock supports fixture schema validation at startup via
--validate-on-load, but it's opt-in. The showcase Dockerfile did not pass
the flag, so fixtures with unrecognized response keys (e.g. "text" instead
of "content") loaded silently and only failed at request time with HTTP 500.
That's what crashed crewai-crews on startup.
Changes:
- showcase/aimock/Dockerfile: pass --validate-on-load so broken fixtures
fail the container boot, not individual requests.
- showcase/scripts/__tests__/aimock-fixtures.test.ts: new vitest spec that
loads feature-parity.json and smoke.json via @copilotkit/aimock's
loadFixtureFile + validateFixtures and asserts zero errors. Runs as part
of the existing showcase-validate CI workflow.
- showcase/scripts/package.json: add @copilotkit/aimock dependency for the
validator import.
Verified red-green: with the pre-#3971 broken "text" fixtures, validateFixtures
flags 5 errors; post-#3971 it returns zero. Docker red-green: container with
an intentionally broken fixture fails to start with "Validation failed: 1
error(s)" and non-zero exit.
The showcase aimock feature-parity fixture had five entries that used the
unrecognized field "text" instead of "content" in the response body.
aimock's /v1/chat/completions handler validates the response shape through
discriminators (isTextResponse, isToolCallResponse, etc.) and when none
match, returns 500 "Fixture response did not match any known type".
When CrewAI's ChatWithCrewFlow.__init__ calls generate_input_description_with_ai,
it sends a prompt containing "Reporting Analyst" / "reports" text (from the
LatestAiDevelopment crew definition). aimock substring-matches this against
the "report" fixture, which had the broken "text" shape, and returned 500.
CrewAI treats that as an InternalServerError, the crewai-crews container
crashes during module import (before uvicorn binds), Railway healthchecks
fail, and the deployment rolls back to the previous ACTIVE build.
Fix:
- Convert the 5 fixtures with "text" (plan, steps, mars, dashboard, report)
to the valid "content" field so they return 200 OK.
- Add a targeted fixture that matches CrewAI's exact startup prompt prefix
("Based on the following context, write a concise") and returns a clean
text description. This catches generate_input_description_with_ai and
generate_crew_description_with_ai before they can fall through to the
more generic fixtures, providing a more predictable response for CrewAI
startup.
Verified locally against @copilotkit/aimock@1.10.0:
- Before: the CrewAI-style prompt returns 500 with the exact error message
seen in Railway logs.
- After: returns 200 with a valid chat.completion envelope.
Note: CrewAI's blocking LLM call at module import remains fragile (any
aimock hiccup crashes the container before it can bind a port). That is
an upstream issue and will be tracked separately.
The Mastra showcase package registers a single local agent
(`weatherAgent`), but the demo pages request names like `agentic_chat`,
`human_in_the_loop`, `tool-rendering`, etc. `MastraAgent.getLocalAgents`
returns the agents under their registration keys, so the runtime could
not resolve the demo-requested names and the L3 mastra chat test
failed.
Mirror the crewai-crews pattern: wrap the local agents map and expose
`weatherAgent` under every demo-expected name. `weatherAgent` itself is
preserved so the internal smoke route keeps working.
## Summary
Addresses all findings from 7-agent MSAL code review of the starter
crash fix PRs (#3933, #3941, #3943).
**Agent server (claude-sdk-typescript):**
- Remove 6 unnecessary `as any` casts — use TypeScript discriminated
union narrowing on `msg.role`
- Add `ANTHROPIC_API_KEY` validation on startup (exit if missing)
- Add `console.warn` logging to empty catch blocks (tool args + schema
parse failures)
- Sanitize error messages sent to client — no raw `err.message` in SSE
stream
- Fix misleading comment about system/developer role handling
- Set explicit 2MB JSON body limit on Express
- Remove unused `assistantMsgId` variable
**Generation script:**
- Remove dead `_agentDir` parameter from `rewritePythonImports`
- Add warning when `PIN_OVERRIDES` dep not found in framework
dependencies
**Tests (3 new):**
- Context-aware import rewriting for files inside `tools/` directory
- `from agents.X import` relative import rewriting
- `PIN_OVERRIDES` integration test for mastra version pinning
## Test plan
- [x] 254 generate-starters tests pass (3 new)
- [x] Drift check passes
- [x] Pre-commit hooks pass
- Guard TOOL_CALL_ARGS emission with if(toolCallId) check, matching
the existing guard on TOOL_CALL_END
- Wrap catch-block emit() in nested try/catch to handle client
disconnect during error reporting
The Round 2 fixes made tools.py rename and langgraph import
rewriting recursive, changing output for langgraph-python and
langgraph-fastapi tools/ subdirectory files. Committed starters
were stale.
Generation script:
- Make tools.py rename and langgraph import rewrite recursive via
forEachPyFile helper (was only processing top-level .py files)
- Fix from-src.agents regex to use [\w.]+ for dotted sub-paths,
matching the from-agents regex
- Simplify isInsideToolsDir branch to computed prefix variable
- Safe import-os removal: keep if os is used elsewhere in file
- Fix extractUvicornModule JSDoc example to match actual output
Agent server (claude-sdk-typescript):
- Remove dead ternary branches after API key guard
- Remove unreachable ?? "" fallback
Agent server (claude-sdk-typescript):
- Remove 6 unnecessary as-any casts, use TS discriminated union narrowing
- Add ANTHROPIC_API_KEY validation on startup (exit if missing)
- Add logging to empty catch blocks (tool args + schema parse)
- Sanitize error messages sent to client (no raw err.message in SSE)
- Fix misleading comment about system/developer role handling
- Set explicit 2MB JSON body limit
- Remove unused assistantMsgId variable
Generation script:
- Remove dead _agentDir parameter from rewritePythonImports
- Add warning when PIN_OVERRIDES dep not found in dependencies
Tests:
- Add context-aware import rewriting test (files inside tools/)
- Add from-agents.X relative import rewriting test
- Add PIN_OVERRIDES integration test for mastra version pinning
## Summary
- Remove the Open GenUI rendering strategy from all 17 showcase starters
- Clean up all supporting files: feature registries, constraints, e2e
tests, Python middleware, docs
- Regenerate all starters from updated template
### What was removed
The "open-genui" render mode allowed agents to generate arbitrary HTML
rendered in a sandboxed iframe. This is being extricated from starters
into a separate project. The core SDK `OpenGenerativeUIRenderer`
component is **not** affected — only the showcase starter UI option.
### Files changed
- **Template**: Deleted `open-genui/` renderer, updated types.ts,
page.tsx, suggestions hook
- **Registry**: Removed `open-gen-ui` and `byoc-opengenui` features from
all registries (shell, shell-dojolike, shared)
- **Constraints**: Removed "open" constraint block and open-gen-ui
references
- **E2E tests**: Removed Open GenUI test cases from starter-e2e,
screenshots, renderer-selector specs
- **Python middleware**: Removed OPEN_GENUI_INSTRUCTION and open-genui
handling from render_mode.py
- **Starters**: Regenerated all 17 via generate-starters.ts
- **Docs**: Updated QA-COVERAGE.md pill count from 5 to 4
### Local verification
- Docker build of langgraph-python starter: success
- Rendered page shows exactly 4 render modes (Tool-Based, A2UI,
json-render, HashBrown)
- Zero "open-genui" references in rendered HTML
- 521 validation tests pass
## Test plan
- [x] Docker build succeeds locally
- [x] Rendered starter shows 4 modes, no Open GenUI
- [x] Zero grep matches for open-genui across showcase/
- [x] 521 vitest validation tests pass
- [ ] CI green
🤖 Generated with [Claude Code](https://claude.com/claude-code)