Introduces the "In-App Human in the Loop" cell, demonstrating the
canonical async useFrontendTool HITL pattern where the approval UI
lives OUTSIDE the chat surface.
Flow:
- Agent calls `request_user_approval` (frontend-registered)
- The async handler returns a Promise and stashes its `resolve` in
component state
- An app-level modal dialog (portal'd to <body>, grid-sibling of
CopilotChat — NOT inside the chat bubble tree) opens with the
agent's message + optional context
- User clicks Approve / Reject (with optional note) → completion
callback fires `resolve({ approved, reason })`
- Handler returns the value to the agent, which continues based on
approval outcome
Files:
- src/agents/hitl_in_app.py — support-ops agent with tools=[] and a
system prompt that requires calling `request_user_approval` before
any customer-affecting action
- src/app/demos/hitl-in-app/page.tsx — layout (support-inbox panel +
CopilotChat + app-level modal), frontend-tool registration,
resolve-capture pattern, suggestions
- src/app/demos/hitl-in-app/approval-dialog.tsx — modal (createPortal
to document.body) with Approve/Reject + optional reason textarea
- langgraph.json — register `hitl_in_app` graph
Starter mirror auto-generated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a compact ✓N · ✗M tally directly under each integration's name + language
sublabel in the Feature Matrix dashboard header. The tally aggregates the same
binary signals each cell already renders (docs-og, docs-shell, E2E, Smoke,
health) using DocsRow / CellStatus's resolution rules — including per-column
docs-links overrides — so the totals stay consistent with what's visible in
the body. QA age and ?/unknown/stale signals are intentionally skipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port the working beautiful-chat A2UI topology to the standalone A2UI cells.
Both previously mounted on the main /api/copilotkit with `injectA2UITool: true`,
which left the dynamic cell blank (no rendered surface) and crashed the
fixed-schema cell with React error #31 on unresolved `{ path }` bindings.
- Split each cell onto its own runtime endpoint
(/api/copilotkit-declarative-gen-ui, /api/copilotkit-a2ui-fixed-schema),
each with `a2ui.injectA2UITool: false`, mirroring beautiful-chat and the
canonical examples/integrations/langgraph-python reference
- Rewrite src/agents/a2ui_dynamic.py to own an explicit `generate_a2ui`
tool that binds `render_a2ui` via a secondary LLM (beautiful-chat pattern)
- Fix a2ui-fixed-schema catalog definitions to use a `DynString` union
(string | { path: string }) so the GenericBinder resolves path bindings
before handing props to renderers
- Drop the scoped `a2ui: { injectA2UITool: true, agents: [...] }` block
from the main /api/copilotkit route since both cells now own their own
runtimes
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The advanced variant's distinguishing feature is that the agent-authored,
sandboxed UI can invoke frontend-registered sandbox functions via
Websandbox.connection.remote.<name>(args). The old label "(Advanced)"
did not convey this. Rename to "Open-Ended Gen UI (Advanced: with
frontend function calling)" in both the feature registry and the
langgraph-python manifest, and update descriptions to match. The id
stays `open-gen-ui-advanced` so URLs remain stable.
Regenerate derived bundles (registry.json, demo-content.json,
docs-status.json).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the hardcoded copilotkit_manually_emit_tool_call stubs with
proper LLM-driven agents (create_agent + CopilotKitMiddleware) matching
the beautiful-chat reference. The runtime's openGenerativeUI flag
auto-registers generateSandboxedUi as a frontend tool; the middleware
merges it into the agent's tool list, so the LLM can generate real,
user-specific sandboxed UIs instead of emitting a canned "Hello!" card.
For the advanced cell, strengthen the sandbox-function bridge: swap the
trivial notifyHost for an evaluateExpression function that the UI
visibly round-trips via Websandbox.connection.remote, and update the
suggestion prompts to explicitly exercise the bridge.
Also wire starter suggestions on the minimal cell so users have a
one-click entry point.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `tool-rendering-frontend-tools` cell rendered its WeatherCard stuck at
the `--° F` / `--%` / `-- mph` placeholders even after the tool completed
and the chat text showed the real values. `useFrontendTool`'s render
callback receives `result` as a JSON-stringified string (core's run-handler
`JSON.stringify`s any non-string handler return before delivery), but the
render was just type-casting the string to an object — so every
`parsed.temperature` / `parsed.humidity` / `parsed.wind_speed` read was
`undefined` and each field fell through to the `??"--"` placeholder.
Parse `result` via a `parseJsonResult` helper (mirroring the primary
`tool-rendering` cell) so `status === "complete"` actually populates the
card with the handler's real values.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "Declarative Generative UI (A2UI — Custom Catalog)" single-file
prototype variant was a testing-only companion to the canonical
declarative-gen-ui 3-file BYOC pattern. Drop it everywhere:
- Delete src/app/demos/declarative-gen-ui-hardcoded/ cell directory
- Delete src/agents/a2ui_dynamic_hardcoded.py
- Remove from langgraph-python manifest.yaml (features + demos) and
langgraph.json graphs
- Remove from docs-links.json, route.ts (agent entry + a2ui agents
list), shared/feature-registry.json, shared/constraints.yaml
- Drop the Callout promoting it from a2ui/dynamic-schema.mdx
- Bump expected langgraph-python count 32 → 31 in generate-registry
test; regenerate all bundles
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-Built: Chat was redundant with Pre-Built: CopilotChat — both rows
referred to the same CopilotChat surface with slightly different
framing. Drop prebuilt-chat everywhere:
- Delete src/app/demos/prebuilt-chat/ cell directory
- Remove from langgraph-python manifest.yaml features + demos
- Remove from shared/feature-registry.json features + chat-ui allowlist
- Remove from shared/constraints.yaml constrained-explicit
- Remove from src/app/api/copilotkit/route.ts neutral-fallthrough list
- Update expected counts in bundle-demo-content + generate-registry
tests (langgraph now exposes 32 features, down from 33)
- Fix stale src/agents/main.py test expectation to
src/agents/agentic_chat.py (main.py was split into a neutral assistant
by an earlier Phase-1 fix commit)
- Regenerate shell/src/data/{registry,constraints,demo-content,
docs-status}.json
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regenerated via `pnpm --filter @copilotkit/showcase-scripts run bundle-content`
after trimming the `highlight:` lists on langgraph-python cells so the
"show all files" toggle in the code viewer exposes materially more files
than the default "core" view.
Spot-check (core / all):
beautiful-chat 9 / 31
headless-complete 5 / 13
tool-rendering 5 / 7
tool-rendering-reasoning-chain 5 / 7
chat-slots 4 / 6
gen-ui-tool-based 4 / 5
open-gen-ui-advanced 4 / 5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the `highlight:` list on many cells had been expanded to include
nearly every file in the demo folder — which made the code viewer's
"show all files" toggle meaningless because the default "core" view already
contained everything.
Reduce each large cell's highlight list to the pedagogically-core files
(page.tsx, main backend agent .py, the cell-specific runtime route, plus
1-2 signature named components) while leaving the rest of the demo folder
to show up in the "show all" view:
- beautiful-chat: 32/33 -> 9/31 (dropped ui/* primitives, theme css,
lib/utils, hooks/index, use-theme, secondary canvas/layout pieces,
todo-column/list, charts/config, meeting-time-picker, headless-chat,
tool-rendering, declarative-generative-ui, + db.csv and schema json)
- headless-complete: 13/13 -> 5/13 (dropped per-role bubbles, cards,
input-bar, typing-indicator, tool-renderers, highlight-note, weather/
stock cards — they stay bundled as they sit in the demo folder)
- tool-rendering: 6/7 -> 5/7 (dropped custom-catchall-renderer)
- tool-rendering-reasoning-chain: 7/7 -> 5/7 (dropped reasoning-block and
custom-catchall-renderer)
- chat-slots: 6/6 -> 4/6 (dropped custom-assistant-message and
custom-disclaimer)
- gen-ui-tool-based: 5/5 -> 4/5 (dropped pie-chart)
- open-gen-ui-advanced: 5/5 -> 4/5 (dropped suggestions.ts)
Cells where every file is genuinely core (declarative-gen-ui 6/6,
a2ui-fixed-schema 8/8, shared-state-read-write 5/5, subagents 4/4) and
small cells (3-4 files) are untouched — task brief called these out as
acceptable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-runs `pnpm --filter @copilotkit/showcase-scripts run bundle-content`
to repopulate `showcase/shell/src/data/demo-content.json` with the
expanded `highlight:` file lists from the previous commit. Affects only
`langgraph-python::*` entries; all other framework demos are unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The langgraph-python manifest's `highlight:` lists were skimpy — most cells
listed only `page.tsx` even when the cell pulled in supporting components,
hooks, A2UI catalog files, dedicated runtime routes, or split per-cell agent
graphs. The code-viewer's left file tree at
`/integrations/langgraph-python/<cell>/code` therefore showed users a single
file when the actual relevant code lived across 5–30 files.
This commit walks every cell in the manifest and rebuilds each `highlight:`
list to cover:
- All `.tsx`/`.ts` files in `src/app/demos/<cell>/**` (recursively)
- The actual backend agent `.py` file (e.g. `agentic_chat.py`,
`hitl_in_chat_agent.py`, `tool_rendering_frontend_tools.py`,
`headless_complete.py`) — many cells were pointing at `main.py` even
though they have their own dedicated graph
- The runtime `route.ts` the cell registers against
(`copilotkit`, `copilotkit-ogui`, `copilotkit-mcp-apps`, or
`copilotkit-beautiful-chat`)
- A2UI/data assets where applicable (`a2ui_schemas/*.json`,
`beautiful_chat_data/*`)
The flagship `beautiful-chat` cell now surfaces all 32 supporting files
(components/example-canvas/*, components/example-layout/*,
components/generative-ui/*, components/ui/*, hooks/*, lib/*,
declarative-generative-ui/*, etc.) plus the backend agent + dedicated
runtime route.
No source code is touched — only the manifest's `highlight:` references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The oneLight SyntaxHighlighter theme sets a near-white background on
both <pre> and the inner <code>. customStyle overrides the <pre>, but
the <code> child keeps its own background — and since <code> renders
inline, that bg hugs each line of text and shows as a pale per-line
rectangle against the dark page chrome, making untouched code look
like it's text-selected. Unset the <code> background so the chosen
<pre> background reads as a flat block.
Same fix 4084 already landed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A framework-scoped URL asserting "unselected" in its path is
incoherent — you can't be both on langgraph-python AND in the
no-framework state. Collapse the redundant segment: if the framework
catch-all receives a slug that starts with unselected/, permanent-
redirect to the same path without that prefix (the framework-scoped
version serves the same content, just with Snippets resolved against
the selected framework's cells).
/docs/unselected/<path> remains a valid framework-agnostic URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The /built-in-agent/<page> URL prefix in 4085's docs was a holdover from
an early misnomer in the original 4084 work. 4084 has since renamed the
content directory to unselected/ to reflect what the route actually is:
the no-framework / "no agentic backend selected" docs view. "Built-in
Agent" is reserved for an upcoming integration COLUMN in the showcase
matrix and shouldn't share a name with this docs route.
- Rename content/docs/built-in-agent/ → content/docs/unselected/
- Update docs-render.tsx subpath fallback (built-in-agent/ → unselected/)
- Update seo-redirects.ts wildcards
- Update internal MDX link references in learn/, backend/, faq.mdx,
meta.json, snippets/shared/
The integrations/built-in-agent/ legacy directory is preserved
unchanged (it's a separate concept).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The built-in-agent tree exists only in 4085, but 4084's top-level
prebuilt-components.mdx had richer content (per-variant code examples
for CopilotChat/Sidebar/Popup with labels, customization block with
slot props, Setup section). Merge those into the 4085 page while
keeping its modern Snippet-based provider-setup + suggestions pulls
from the live cell.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundler picks up the new region markers plus the extended manifest
highlights — each of the 7 affected docs pages (subagents,
shared-state-streaming, open-generative-ui, mcp-apps, a2ui dynamic and
built-in) now resolves its <Snippet> tags to a real region.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps unified-runtime config blocks with @region[runtime-inject-tool],
@region[minimal-runtime-flag], and @region[advanced-runtime-config] so
the A2UI dynamic-schema, open-generative-ui, and built-in a2ui docs pages
resolve their <Snippet> tags.
Also extends manifest highlights for subagents, shared-state-streaming,
declarative-gen-ui, mcp-apps, open-gen-ui, and open-gen-ui-advanced so
the bundler scans the agent + route.ts files holding these regions.
Subagents and shared-state-streaming highlights previously pointed at
src/agents/main.py even though the graph lives in dedicated files,
hiding the subagent-setup, supervisor-delegation-tools, and
state-streaming-middleware regions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Matches 4084's script topology so docs-status.json and the demo-content
bundle refresh automatically on every dev start and build. Previously
required manual invocation of the scripts after the docs-links pipeline
port.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Regenerates shell/src/data/registry.json and shell/src/data/docs-status.json
after the docs-links pipeline port so the shell-internal dashboard picks up
per-column docs overrides and live probe state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports probe-docs.ts from 4084. For each entry in shared/feature-registry.json
it HEADs the feature's og_docs_url and checks whether a matching .mdx exists
under shell/src/content/docs/<shell_docs_url>, then writes the results to
shell/src/data/docs-status.json. shell-internal's DocsRow falls back to this
probed state for (integration, feature) cells that don't have a per-column
docs-links.json override. Exposes the script as `probe-docs` in
showcase/scripts/package.json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports the loadDocsLinks() helper from 4084 into generate-registry.ts. After
schema validation, each integration gets a docs_links field merged in from its
sibling packages/<slug>/docs-links.json (best-effort: missing file or stale
shell_docs_url shape is tolerated). The shell-internal DocsRow reads this via
integration.docs_links.features[<id>] to prefer curated per-column overrides
over the per-feature defaults.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports 16 per-package docs-links.json files (one per integration column) from
4084. These provide per-(integration, feature) curated docs overrides that the
shell-internal DocsRow component reads via `integration.docs_links.features[id]`
to render ✓ instead of ✗ in the docs-og/docs-shell badge columns. Consumed by
the loadDocsLinks() helper in generate-registry.ts which merges them into the
shell's registry.json bundle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports 4084's per-column docs link overrides into shell-internal:
- DocsRow now reads integration.docs_links.features[id] with fallback to
feature.og_docs_url; shell URLs are built from shell_docs_path via
${shellUrl}/${slug}/unselected${path}.
- Integration interface gets optional docs_links map (sourced from
packages/<slug>/docs-links.json once the 4085 registry bundler wires
it up; falls through gracefully until then).
- a:hover underline restored in globals.css to match 4084.
No runtime behavior change in 4085 yet since docs_links is absent from
the registry bundle; once the bundler pipeline catches up, per-column
overrides will light up the docs-og/docs-shell links automatically.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rerun generate-registry + bundle-content so shell/src/data/*.json reflects
the prebuilt-chat feature addition and the langgraph-python manifest
copy-alignment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Align demo-row display names, descriptions, and tag sets with 4084 so the
shell surfaces matching copy across the two workspaces. Preserves 4085-
specific divergences (single-container src/agents + src/app/demos highlight
paths, per-demo routes, cli-start command field, prebuilt-chat row).
- Add prebuilt-chat feature to the registry and constrained-explicit
allowlist so the 4085 manifest's intentional prebuilt-chat wiring
validates cleanly (4084 omits it; 4085 committed the cell earlier)
- Bump expected langgraph-python feature count in generate-registry
test from 32 to 33 to reflect the new row
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two small wiring fixes discovered during smoke testing:
1. Drop `useSingleEndpoint={false}` from the `<CopilotKit>` provider.
The canonical reference sets this to opt into the v2 multi-endpoint
protocol (GET /info + POST /agent/{name}/connect) which requires a
Hono-based endpoint via `createCopilotEndpoint`. 4085 uses
`copilotRuntimeNextJSAppRouterEndpoint` (single POST), matching every
other cell in this package. Without this change the client 404s on
/agent/beautiful-chat/connect.
2. Alias the `default` agent to the same graph in the route's agents map.
`ExampleCanvas` and `HeadlessChat` call `useAgent()` with no args, which
resolves to agentId "default". Previously this threw "Agent 'default'
not found after runtime sync". Aliasing matches the canonical's
`agents: { default: defaultAgent }` shape.
Smoke-tested: Pie Chart pill click now renders a real styled doughnut chart
from query_data + controlled generative UI; zero console / page errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add `@radix-ui/react-checkbox` and `@radix-ui/react-separator` to
package.json (ported frontend's ui/checkbox.tsx + ui/separator.tsx import
these; the showcase package didn't pin them yet).
- Place `showcase.json` inside the cell directory (where it semantically
belongs — the canonical had it at the project root alongside `src/hooks/`
at depth 2, so `../../showcase.json` resolved to the root; in 4085 the
hooks live two levels deeper, so we colocate the tiny config with the
cell and adjust the import path to `../showcase.json`).
Without these, `next build` errors with "Module not found: Can't resolve
'@radix-ui/react-checkbox'" and "Can't resolve '../../showcase.json'".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #4101 updated both the resolution-aware import rewriter and the
entrypoint `$!` -> agent PID fix, but the regeneration in the merged
commit didn't include all affected files. `origin/main` at 2087950ee
still contained:
- langgraph-fastapi + langgraph-python: `src/agents/tools/*.py` importing
`from src.agents.types`, `from src.agents.get_weather`, etc. — the
old flat-rewrite output, which crashes with
`ModuleNotFoundError: No module named 'src.agents.types'` at agent
import time. langgraph-fastapi on Railway was failing its health probe
with `agent: down` for this reason.
- All 15 generated entrypoint.sh files still had the old
`2>&1 | sed 's/^/[agent] /' &` pattern, meaning `$!` pointed at `sed`
instead of the agent subprocess. Every `kill -0 $AGENT_PID` /
`wait -n` check was monitoring the wrong process, and Python crash
output was also line-buffered inside the pipe so tracebacks never hit
the container log.
Re-running `pnpm -C showcase/scripts generate-starters` with #4101's
resolution-aware rewriter produces the correct `src.agents.tools.*`
imports for both langgraph starters and the intended
`&> >(awk '{print "[agent] " $0; fflush()}')` process-substitution form
for every entrypoint.sh. No generator changes — purely regenerated
output catching up to the script that's already on main.
Inlines the canonical starter's agent/main.py + agent/src/{todos,query,
a2ui_fixed_schema,a2ui_dynamic_schema}.py into a single `beautiful_chat.py`
graph (same shape as the 4084 clone), registers it in langgraph.json, and
colocates its data files (db.csv + flight schema JSON) under
`beautiful_chat_data/` so the shared `a2ui_schemas/` dir stays owned by
a2ui_fixed.py.
Adds a dedicated `/api/copilotkit-beautiful-chat` runtime endpoint that
mirrors the canonical's combined config (openGenerativeUI: true,
a2ui.injectA2UITool: false, mcpApps) — scoped to this one cell so the other
cells on `/api/copilotkit` keep their isolated per-demo `useFrontendTool` /
`useComponent` registrations. Removes `beautiful-chat` from the main route's
neutral-assistant fallthrough list now that it has a dedicated graph.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports the full 28-file Beautiful Chat cell frontend tree (components, hooks,
A2UI declarative catalog, UI primitives, theme CSS) from the 4084 demos clone
into the unified 4085 showcase layout. Imports rewired to relative paths.
Provider wrappers (CopilotKit + ThemeProvider) moved from the canonical's
root layout.tsx into the cell's own page.tsx since 4085 has no per-cell
layout. runtimeUrl points to a dedicated `/api/copilotkit-beautiful-chat`
endpoint (wiring follow-up).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
After #4099 both `starter-langgraph-fastapi` and
`starter-langgraph-typescript` returned `/api/health` =
`{"status":"degraded","agent":"down"}` on Railway. Three interlocking
regressions in `generate-starters.ts`:
- **Import rewrite was flat, not resolution-aware.** langgraph-fastapi's
`agent.py` lives at `<agentDir>/src/agent.py` and imports `.tools`, but
`tools/` lives at `<agentDir>/tools/` (one level up). Both the flat
`<agentDir>.tools` rewrite and the file-dir-based `<agentDir>.src.tools`
rewrite pointed at nonexistent paths. langgraph_cli crashed with
`ModuleNotFoundError: No module named 'src.agents.src.tools'` before
binding 8123.
- **langgraph-typescript root `package.json` missing agent deps.** The
TS Dockerfile deletes `agent/package.json` to collapse the ESM package
boundary, but there was no mechanism to lift the agent's runtime deps
(`@langchain/openai` et al.) into the root `package.json`. `npx
@langchain/langgraph-cli dev` died with `Cannot find module
'@langchain/openai'`.
- **`AGENT_LOG_PREFIX` was accidentally reverted** to `cmd 2>&1 | sed
…`. After a pipeline `$!` points at `sed`, so `kill -0 $AGENT_PID` /
`wait -n $AGENT_PID` monitor the prefixer, not the agent. `sed` also
line-buffers so crash output can be lost.
Fix all three in the generator and regenerate the two affected starters.
Committed starter entrypoint.sh files across the fleet are already on
`awk &> >(…)` (they were regenerated at some earlier point) — this PR
re-aligns the generator with their committed state, so future regens
stay consistent.
## Verification
- `docker build` + `docker run` on both starters locally:
- fastapi:
`{"status":"ok","integration":"langgraph-fastapi","agent":"ok"}`
- typescript:
`{"status":"ok","integration":"langgraph-typescript","agent":"ok"}`
- `entrypoint` logs show `Agent server started (PID: N)` with N
belonging to the agent, not an awk/sed prefixer.
- `pnpm test` in `showcase/scripts/`: 1075/1075 pass.
- `tsc --noEmit` on the generator: clean.
- No changes to `langgraph-python` output (resolution-aware rewriter is
a superset of the prior subdir-aware behavior when all imports already
resolve).
## Test plan
- [ ] Merge + wait for showcase deploy workflow to redeploy both
services
- [ ] `curl https://starter-langgraph-fastapi.up.railway.app/api/health`
returns `agent: "ok"`
- [ ] `curl
https://starter-langgraph-typescript.up.railway.app/api/health` returns
`agent: "ok"`
- [ ] Smoke-test a chat turn on each starter to confirm the agent
actually responds
Regions were stripped during the initial port. Restore them verbatim
from the 4084 source so MDX snippet extraction continues to work:
- agentic-chat: provider-setup, configure-suggestions, render-chat
- agentic-chat-reasoning: reasoning-block-render
- frontend-tools: frontend-tool, frontend-tool-registration,
frontend-tool-handler
- gen-ui-tool-based: bar-chart-renderer, pie-chart-renderer
- chat-customization-css (+ theme.css): theme-css-import, css-variables
- chat-slots: register-welcome-slot, register-disclaimer-slot,
register-assistant-message-slot (with the named-locals pattern)
- prebuilt-sidebar: sidebar-basic-setup, sidebar-configuration
- prebuilt-popup: popup-basic-setup
- headless-simple: use-agent-simple, headless-hooks, message-list-simple
- interrupt_agent.py: backend-interrupt-tool
Also ports the shared-state-streaming frontend (page.tsx +
document-view.tsx), replacing the TODO stub. Includes the
frontend-use-coagent-state region marker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- subagents: port 4084 delegation-log.tsx + page.tsx (reads
agent.state.delegations, renders a live delegation log alongside
CopilotChat). Drops the TODO stub that previously lived under
demos/subagents/.
- headless-complete: port the full 4084 frontend — tool-renderers.tsx
(useRenderTool for get_weather + get_stock_price, useComponent for
highlight_note, useDefaultRenderTool catch-all), weather-card.tsx,
stock-card.tsx, highlight-note.tsx, use-rendered-messages.tsx
(manual render-tool-call / render-activity-message / render-custom-
messages composition mirroring CopilotChatMessageView), plus chrome
updates to assistant-bubble.tsx, user-bubble.tsx, message-list.tsx,
page.tsx. Routed through /api/copilotkit-mcp-apps so the headless
cell exercises Excalidraw MCP activity rendering too.
- open-gen-ui: reset to 4084's minimal version (plain <CopilotChat />
against /api/copilotkit-ogui). Drops sandbox-functions.ts + suggestions.ts
which belonged to open-gen-ui-advanced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- mcp-apps page.tsx: route through /api/copilotkit-mcp-apps (split runtime
that has Excalidraw MCP wired) and drop the manual
useRenderActivityMessage + renderActivityMessages registration —
CopilotKitProvider auto-registers MCPAppsActivityRenderer; plain
<CopilotChat /> is enough. Replace generic suggestions with 4084's
Excalidraw-specific ones.
- a2ui-fixed-schema page.tsx: fix the stale `./catalog` import to
`./a2ui/catalog`, and delete the orphaned top-level catalog.ts +
flight-card.tsx (the authoritative versions live under ./a2ui/).
- prebuilt-chat: add to features list + demos list in manifest.yaml so
the shell surfaces it (runtime wiring already landed in the previous
commit as a neutral-assistant cell).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before this commit, main.py carried the Tool-Based Generative UI chart
system prompt, and a long list of cells (agentic-chat, frontend-tools,
gen-ui-agent, tool-rendering-frontend-tools, gen-ui-tool-based) fell
through to that graph — so they all inherited the wrong "data
visualization assistant" prompt.
Split each cell into its own dedicated graph, mirroring 4084's per-cell
backends. main.py is now a neutral "helpful, concise assistant" used
only by UI/chrome demos (prebuilt-sidebar, chat-slots, headless-simple,
beautiful-chat, prebuilt-chat, etc.).
- agentic_chat, frontend_tools, gen_ui_agent: neutral variants with
minor system-prompt tweaks from 4084
- tool_rendering_frontend_tools: weather-assistant prompt; frontend owns
the tool via useFrontendTool
- gen_ui_tool_based: chart-viz system prompt (moved out of main.py)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports 4084 backend agents for shared-state-streaming, subagents, and
headless-complete into 4085's consolidated src/agents/ layout, registers
the new graphs in langgraph.json, wires them into the main and
copilotkit-mcp-apps routes.
- shared_state_streaming: StateStreamingMiddleware write_document tool
- subagents: supervisor + research/writing/critique sub-agents with
delegation log shared state
- headless-complete: get_weather + get_stock_price + MCP Apps routing;
routed through /api/copilotkit-mcp-apps to match 4084 pedagogy
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
langgraph-fastapi/src/agents/src/agent.py now imports
`src.agents.tools.*` (module exists) rather than `src.agents.src.tools.*`
(nonexistent path) -- fixes the ModuleNotFoundError crash that held
/api/health at agent: "down".
langgraph-typescript/package.json now includes @langchain/core,
@langchain/langgraph, @langchain/langgraph-checkpoint,
@langchain/langgraph-cli, @langchain/openai, and @copilotkit/sdk-js so
the agent subtree can resolve its runtime imports after the Dockerfile
deletes agent/package.json.
Local docker build + run verified: both /api/health return
{"status":"ok","agent":"ok"} and their entrypoint logs show
`Agent server started` with the captured PID belonging to the agent,
not an awk/sed prefixer.
Three interlocking fixes to generate-starters.ts that together restore
langgraph-fastapi and langgraph-typescript /api/health to "ok" after
#4099 shipped an entrypointOverride feature that accidentally reverted
earlier fixes and left gaps for the TS starter.
1. Resolution-aware python import rewrite for langgraph starters.
langgraph_cli loads agent modules standalone (not as packages), so the
generator rewrites `from .X import ...` to absolute form. The previous
flat rewrite produced `from <agentDir>.X import ...`, and the
subsequent subdir-aware variant used the file's OWN dir as the
package. Both assume sibling targets sit alongside the importing file.
langgraph-fastapi violates that: agent.py lives at
`src/agents/src/agent.py` but the `tools/` it imports lives at
`src/agents/tools/` -- one level up, not inside `src/`. Either prior
rewrite produced `from src.agents.src.tools import ...`, a path that
doesn't exist, and langgraph_cli crashed on module import with
`ModuleNotFoundError: No module named 'src.agents.src.tools'` before
ever binding 8123.
Walk UP from the file's own dir toward agentDest and rebase the
absolute import on the shallowest directory that actually contains
`<firstSeg>/` or `<firstSeg>.py`. Correct both for co-located imports
(`tools/get_weather.py` importing `.types`) and for sibling-directory
imports (`src/agent.py` importing `.tools` from `../tools`).
2. Merge langgraph-typescript agent runtime deps into root package.json.
The TS Dockerfile deliberately deletes `agent/package.json` to
collapse the ESM package boundary between the Next.js frontend and
the agent subtree. Without merging the agent's runtime deps up into
the root package.json, the langgraph-cli's runtime import of
graph.ts fails with `Cannot find module '@langchain/openai'` and
the agent never binds 8123.
Add `extraDependencies` to the langgraph-typescript framework def
with @langchain/core, @langchain/langgraph,
@langchain/langgraph-checkpoint, @langchain/langgraph-cli,
@langchain/openai, and @copilotkit/sdk-js.
3. Restore AGENT_LOG_PREFIX process-substitution helper.
#4099 inadvertently replaced `cmd &> >(awk … fflush …)` with
`cmd 2>&1 | sed …` across every getEntrypointBlock branch. After a
pipeline `$!` points at `sed`, not the agent, so `kill -0 $AGENT_PID`
and `wait -n $AGENT_PID` monitor the wrong process and mask real
crashes; `sed` also line-buffers so crash output can be lost.
Restore the helper so the committed starter entrypoint.sh files and
regenerated output stay consistent.
#4098 landed two generator-level changes that affect every starter slug but only
langroid was regenerated in that PR:
1. rewritePythonImports now strips `import sys` when sys.path.insert is removed
and no other `sys` reference survives in the file (paired with existing
osUsed detection).
2. starters/template/entrypoint.template.sh switched from pipeline
(`2>&1 | sed 's/^/[agent] /'`) to process substitution
(`&> >(awk '{print "[agent] " $0; fflush()}')`) so $! captures the real
agent PID, not sed's — prior pattern masked uvicorn crashes behind sed's
exit code.
Regenerate the remaining 16 starters so drift-check passes on main.
No source logic change.
generate-starters.ts:
- Add entrypointOverride?: boolean to FrameworkDef so slugs whose boot sequence
diverges from the shared template (e.g. langroid's provider-aware credential
guard) can preserve their committed entrypoint.sh through regeneration.
- Snapshot the override from STARTERS_DIR/<slug>/entrypoint.sh (canonical
committed path), NOT outDir — so --check mode (which passes a temp outDir)
doesn't false-flag the override as drift.
- Force mode: 0o755 on the restored file regardless of source mode so editors
that strip the executable bit can't silently ship a broken starter.
- Throw (not warn) when an entrypointOverride slug is missing the override
file, when declared extraFiles / agent_server.py / Dockerfile are missing,
and when PIN_OVERRIDES references a stale dep. These are repo-integrity
failures — silent-warn-and-continue ships broken starters.
- Strip unused `import sys` after sys.path.insert removal (pair with the
existing osUsed detection).
Opt langroid in via entrypointOverride: true.
Also adds a regression test in __tests__/generate-starters.test.ts that
calls generateStarterToDir against a tmp dir and asserts the emitted
entrypoint.sh byte-equals the committed langroid starter entrypoint,
plus a negative test that a non-override slug does NOT preserve its file.
Package Dockerfile:
- Create unprivileged app:app user (UID/GID 1001) matching starter Dockerfile
- USER app drop with --chown=app:app on COPY for security parity
- ENV PORT=10000 / ENV HOSTNAME=0.0.0.0 so child processes inherit defaults
- Preserves PYTHONPATH=/app/shared/python for shared-tools imports
requirements.txt (package + starter): pin httpx, pydantic, openai directly
rather than relying on transitive resolution from langroid / fastapi.
agui_adapter.py imports these symbols directly — the module's contract
shouldn't depend on upstream pin discipline we don't control.
Replace the OPENAI_API_KEY-hardcoded credential guard with a provider-aware
_check_key that inspects LANGROID_MODEL's prefix and probes the correct env
var for that provider:
- openai/ → WARN (not a langroid-native prefix; use bare model names)
- anthropic/ → WARN (not routable; suggest litellm/anthropic/<model>)
- litellm/anthropic/ → ANTHROPIC_API_KEY
- gemini/ → GEMINI_API_KEY
- openrouter/, groq/, cerebras/, glhf/, minimax/, portkey/, deepseek/ → dedicated keys
- ollama/, local/, vllm/, llamacpp/ → NO_KEY_REQUIRED (local inference)
- langdb/, litellm-proxy/, litellm/<non-anthropic>/ → NO_KEY_REQUIRED_* (credentials
resolved via config, not env)
Under REQUIRE_LANGROID_API_KEY=1 a missing credential FATALs the boot; the
default is warn-and-continue so dev containers boot cleanly.
Process lifecycle hardening:
- Use process substitution `> >(sed ...)` instead of pipelines so $! captures
the real python/next PID (not sed's) — prior pipeline pattern would mask
uvicorn crashes behind sed's exit code
- Trap cleanup with `set +e` at entry (trap may fire from FATAL `exit 1` path
where set -e would otherwise abort the cleanup loop)
- 5s SIGTERM→SIGKILL grace window on the surviving child
- Extended exit-code case statement (0, 1, 2, 126, 127, 130, 137, 139, 143, 255)
- Survivor-kill diagnostics distinguish EPERM from ESRCH
- wait -n with explicit positional PIDs so unrelated subshell reaps can't
satisfy the wait
.env.example updates document the multi-provider options so operators
discover them from the file itself, not entrypoint.sh.
.gitattributes enforces LF on *.sh so Windows contributors regenerating
the starter don't silently ship CRLF entrypoints.
The secondary GenerateA2UITool previously hardcoded openai.OpenAI() + gpt-4.1,
forcing non-OpenAI users to keep an OPENAI_API_KEY around just for the A2UI
dashboard demo even when LANGROID_MODEL pointed at another provider.
Route the planner through lm.OpenAIGPT (langroid's universal LLM abstraction)
keyed on A2UI_MODEL (override) → LANGROID_MODEL (inherit) → gpt-4.1 (default).
Default uses the bare model name because `openai/` is NOT a langroid-native
prefix — langroid passes it literally to the OpenAI SDK which rejects it.
Additional hardening:
- _A2uiError TypedDict with _A2uiErrorKind enum, factory with non-empty-string
validation that raises ValueError (not assert, so python -O can't strip it)
- _ARGS_MISSING sentinel distinguishes "no tool call" from "degraded shape"
so the frontend gets correct remediation
- Narrow re-raise tuple propagates programmer errors (AttributeError, TypeError,
NameError, ImportError, ModuleNotFoundError, AssertionError, NotImplementedError,
pydantic.ValidationError) while wrapping recoverable SDK errors into _a2ui_error
- _ToolErrorKind enum for backend tool error codes — typo-safe parity with _A2uiErrorKind
- Boundary validation after build_a2ui_operations_from_tool_call return
- Backend tool handlers wrap impls in try/except so exceptions don't escape
to langroid's tool machinery
- @lru_cache(maxsize=4) on _get_a2ui_llm keyed on resolved model string
- FRONTEND_TOOL_NAMES canary raises RuntimeError at import time on drift
- No top-level import of openai (AST walker test pins this contract)
Covered by 109 tests (integration test gated by LANGROID_INTEGRATION_TESTS=1).
Regenerate registry.json (41 features, 11 categories, 17 integrations),
demo-content.json (152 demos bundled with region markers support +
dual-path highlight resolution), constraints.json (new allowlists),
and status.json (no variants field) so the shell + shell-internal
dashboards render the ported state without stale cached inputs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Port the 4084 simplification of the status generator: remove the
MOCK_VARIANTS seeding table + mockVariantTest / mockVariantQA /
mockVariantHealth helpers + Variant type. Informational demos (no
route) are skipped from health probing. The status JSON no longer
carries per-demo `variants[]` — the shell-internal dashboard renders
one row per demo without variant expansion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>