Commit Graph

3254 Commits

Author SHA1 Message Date
Atai Barkai 463a72e6b2 feat(showcase/langgraph-python): port beautiful-chat frontend tree from 4084 canonical
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>
2026-04-19 12:49:07 -07:00
Jordan Ritter 2087950eea fix(showcase/starters): restore langgraph-{fastapi,typescript} agent subprocess (#4101)
## 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
2026-04-19 12:41:14 -07:00
Atai Barkai 1c340b5644 docs(showcase/langgraph-python): restore @region markers for docs snippets + port shared-state-streaming frontend
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>
2026-04-19 12:34:47 -07:00
Atai Barkai 853966b987 feat(showcase/langgraph-python): port subagents + headless-complete frontends + reset open-gen-ui minimal
- 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>
2026-04-19 12:31:39 -07:00
Atai Barkai 0918c5a77c fix(showcase/langgraph-python): mcp-apps + a2ui-fixed-schema + prebuilt-chat wiring
- 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>
2026-04-19 12:26:47 -07:00
Atai Barkai 7c74646c5a feat(showcase/langgraph-python): dedicated graphs for basic cells instead of sample_agent fallthrough
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>
2026-04-19 12:25:28 -07:00
Atai Barkai fe0a1cfb09 feat(showcase/langgraph-python): port missing backends for stub cells
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>
2026-04-19 12:24:01 -07:00
Jordan Ritter c6b1617bef chore(showcase/starters): regenerate all starters post-generator fix 2026-04-19 12:17:50 -07:00
Jordan Ritter 70f8df05a4 chore(showcase/starters): regenerate langgraph-{fastapi,typescript} after generator fix
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.
2026-04-19 12:13:40 -07:00
Jordan Ritter 710da8cc30 fix(showcase/scripts): restore langgraph starter subprocess boot path
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.
2026-04-19 12:13:32 -07:00
Atai Barkai c3d105ae00 feat(showcase/langgraph-python): port 4084 cells into 4085 demos layout
Port 15+ cells from PR 4084 (per-demo workspace) into PR 4085's
shared-column layout (src/agents/*.py + src/app/demos/<cell>/*.tsx).

New cells:
- beautiful-chat (port scaffold — full component tree deferred)
- declarative-gen-ui-hardcoded (single-file catalog variant)
- interrupt-headless (useHeadlessInterrupt from primitives)
- open-gen-ui-advanced (sandbox functions + app-tool-calling)
- readonly-state-agent-context (useAgentContext one-way channel)
- reasoning-default-render (built-in CopilotChatReasoningMessage)
- shared-state-read-write (preferences write + notes read)
- tool-rendering-reasoning-chain (reasoning emitter + chaining loop)

Restructured cells:
- declarative-gen-ui: BYOC with 3-file a2ui/{definitions,renderers,catalog}
- a2ui-fixed-schema: compositional tree with Title/Airport/Arrow/
  AirlineBadge/PriceTag + 3-file catalog; search_flight renamed
  display_flight
- mcp-apps: simplified to mcpApps.servers runtime config targeting
  Excalidraw; drops custom MCPAppsStubMiddleware
- open-gen-ui: minimal provider + openGenerativeUI runtime flag
- tool-rendering + 3 variants: per-tool WeatherCard + FlightListCard
  plus useDefaultRenderTool wildcard

Runtime wiring:
- /api/copilotkit: registers new agents + a2ui.injectA2UITool for
  declarative + fixed-schema cells
- /api/copilotkit-ogui: adds open-gen-ui-advanced
- /api/copilotkit-mcp-apps: NEW dedicated endpoint for mcpApps

Backend graphs:
- shared_state_read_write.py (PreferencesInjectorMiddleware + set_notes)
- readonly_state_agent_context.py
- hitl_in_chat_agent.py (tools=[] + tailored prompt)
- a2ui_dynamic_hardcoded.py (secondary-LLM dynamic schema)
- open_gen_ui_advanced_agent.py
- tool_rendering_reasoning_chain_agent.py (reason→tool loop)

Flight fixed-schema JSON now assembles Card>Column>[Title,Row,Row,Button].

All `@region[...]` markers ported to drive docs <Snippet> resolution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:45:21 -07:00
Jordan Ritter 6e1aa87072 chore(showcase/starters): regenerate all starters after #4098
#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.
2026-04-19 11:41:50 -07:00
Jordan Ritter ede34dabc1 chore(showcase/langroid): regenerate starter after source+generator updates 2026-04-19 11:39:06 -07:00
Jordan Ritter e862feb63b feat(showcase/scripts): per-slug entrypointOverride + fail-loud on missing inputs
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.
2026-04-19 11:38:59 -07:00
Jordan Ritter 2dea75a5ab fix(showcase/langroid): non-root Dockerfile + pin direct Python deps
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.
2026-04-19 11:38:44 -07:00
Jordan Ritter c2234312b8 fix(showcase/langroid): provider-aware entrypoint credential guard + graceful shutdown
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.
2026-04-19 11:38:32 -07:00
Jordan Ritter 65f3790653 fix(showcase/langroid): route A2UI planner through langroid LLM abstraction
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).
2026-04-19 11:38:06 -07:00
Atai Barkai 5eb4da7db9 chore(showcase): regenerate shell/src/data bundles after port
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>
2026-04-19 11:25:29 -07:00
Atai Barkai 2a52cf05ba chore(showcase/scripts): drop variants mock-seeding from generate-status
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>
2026-04-19 11:25:08 -07:00
Atai Barkai 18ed9dbdbd chore(showcase/scripts): port bundle regions + parity dual-location
Port the 4084 scripts-layer enhancements so 4085's showcase toolchain
matches the new feature shape:

- lib/manifest.ts: ManifestDemo gains optional `command` field; parser
  accepts + validates it (non-empty string, frozen).
- bundle-demo-content.ts: inline `@region[name]` / `@endregion[name]`
  comment-marker extraction; informational-only demos (no route, e.g.
  cli-start) are skipped; markers stripped from bundled content;
  regions: { file, startLine, endLine, code, language } emitted per
  demo. External-highlight-file merging (4085-specific) preserved, so
  backend agents under src/agents/*.py still flow into the bundle.
- validate-parity.ts: accepts demos at BOTH demos/<cell>/ (4084 layout)
  and src/app/demos/<cell>/ (4085 layout); informational demos
  (command field) are excluded from the parity audit.
- tests: bundle-demo-content.test.ts expectedDemos updated for the
  shared-state rename; generate-registry.test.ts feature count 25→32;
  validate-parity.test.ts missing-demo-dir message updated to match
  the new dual-location wording.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:24:55 -07:00
Atai Barkai fb062d3750 chore(showcase): port shared schema + feature-registry + constraints
Port the 4084 internal-showcase-followups state of the shared showcase
schema to 4085 (no Docker restructure):

- feature-registry.json: new Dev Ex category, category reorder
  (Dev Ex → Chat & UI → Platform → Controlled → Declarative → Open →
  Operational → Interactivity → Agent State → Multi-Agent → BYOC);
  +8 new features (beautiful-chat, cli-start, interrupt-headless,
  reasoning-default-render, tool-rendering-reasoning-chain,
  open-gen-ui-advanced, declarative-gen-ui-hardcoded,
  readonly-state-agent-context); renames (shared-state-write →
  shared-state-read-write, shared-state-agent-readonly →
  readonly-state-agent-context); removals (shared-state-read,
  state-rendering, a2ui-*, shared-state-io, shared-state, readables,
  a2a-chat, deep-agents, vnext-chat, byoc-a2ui, byoc-tambo);
  gen-ui-agent → kind: testing.
- constraints.yaml: allowlists updated to reference the new IDs.
- manifest.schema.json: `route` is optional; `command` field added for
  informational demos (e.g. cli-start).
- All 17 column manifests: shared-state-read removed,
  shared-state-write renamed to shared-state-read-write.
- langgraph-python manifest: full 4084 features list + demos entries
  with 4085-appropriate highlight paths (src/agents/*.py +
  src/app/demos/<cell>/* rather than backend/ + frontend/).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:24:33 -07:00
Atai Barkai beb42019d4 docs(showcase/shell): port ~70 MDX pages from 4084 into shell
Port the MDX content delta between the 4085 base branch
(atai/2026-04-18/feature-port-no-docker-restructure) and the 4084
follow-ups branch (atai/2026-04-17/internal-showcase-followups):

- built-in-agent tree (ag-ui, coding-agents, copilot-runtime,
  frontend-tools, inspector, prebuilt-components, programmatic-control,
  custom-look-and-feel/{headless-ui,slots}, generative-ui/{a2ui,
  tool-rendering,your-components/{display-only,interactive}},
  premium/{headless-ui,observability,overview},
  troubleshooting/{common-issues,error-debugging,migrate-to-*})
- top-level feature docs (agentic-chat-ui, frontend-tools, headless,
  human-in-the-loop, inspector, shared-state, programmatic-control,
  ag-ui-middleware, coding-agents, coding-agent-setup)
- custom-look-and-feel (css, reasoning-messages, slots)
- generative-ui (a2ui, a2ui/dynamic-schema, a2ui/fixed-schema,
  mcp-apps, open-generative-ui, reasoning, tool-based, tool-rendering)
- human-in-the-loop (headless, useInterrupt)
- prebuilt-components (index, popup, sidebar) — replaces the single
  prebuilt-components.mdx
- shared-state (agent-readonly, streaming)
- troubleshooting (common-issues, debug-mode, error-debugging,
  migrate-to-*, observability-connectors)
- learn (connect-mcp-servers)
- multi-agent/subagents
- integrations/built-in-agent/custom-agent,
  integrations/langgraph/generative-ui/tool-rendering
- meta.json updates across backend, custom-look-and-feel,
  generative-ui, generative-ui/a2ui, prebuilt-components,
  troubleshooting, root docs

These pages use the new Snippet / InlineDemo / FrameworkTabs
components shipped in the preceding infrastructure commit, plus the
FrameworkGuardedContent pattern that only applies when a page has a
defaultCell frontmatter. Framework-agnostic pages (/learn/*,
/troubleshooting/*, /premium/*) keep rendering their body
unconditionally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:22:57 -07:00
Atai Barkai c1b595596e feat(showcase/shell): port /docs routing + framework-scoped routes from 4084
Port the full docs routing + rendering infrastructure from the 4084
branch (atai/2026-04-17/internal-showcase-followups):

- New [framework]/[[...slug]] catch-all that validates the first
  segment against the registry and renders framework-scoped docs
  with a "not available for this framework" banner when the page's
  defaultCell is not tagged in the framework's cells.
- FrameworkProvider + selector: framework is strictly URL-derived,
  storedFramework is an advisory localStorage signal. No
  auto-redirect from localStorage.
- /docs/[[...slug]] rewritten as a thin wrapper around DocsPageView
  with a new DocsOverview landing: framework picker + topic cards.
- New lib/docs-render.tsx centralising snippet inlining,
  frontmatter parsing, nav tree + breadcrumb builders.
- New lib/mdx-registry.tsx consolidating the ~700-line component
  shim used by both routes.
- Server <Snippet> component with demo-content regions + syntax
  highlighting via highlight.js.
- Per-feature <InlineDemo> iframe embedder pointing at the
  integration's backend (single-container shape
  http://localhost:3100/demos/<cell>).
- FrameworkTabs, docs-callout, docs-steps, docs-tabs, router-pivot,
  stored-framework-highlight components for the MDX registry.
- Global typography + color pivot in globals.css: Plus Jakarta
  Sans body, Spline Sans Mono code, cool-gray background, purple
  accent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:22:28 -07:00
Atai Barkai 218a356256 feat(showcase/shell-internal): port enriched cell chrome + command cell from 4084
- Drop the 5 experimental variant-presentation routes + SingleCell helper;
  the variant layouts no longer match the enriched cell chrome.
- Rename variant-pieces.tsx -> cell-pieces.tsx; expose a shared
  <CellStatus ctx={ctx} /> that renders the docs-og/docs-shell row +
  E2E/Smoke/QA/health badges so both the runnable-demo cell and the
  informational CommandCell share the same bottom section.
- Add CommandCell (client component): takes the full CellContext, renders
  <code> + Copy button for ctx.demo.command, then the same CellStatus row.
- page.tsx branches on ctx.demo.command so the CLI Start row renders a
  copy-pasteable command in place of Demo/Code links while keeping the
  matrix visually consistent.
- registry.ts: Feature gains kind + og_docs_url/shell_docs_url; Demo.route
  is optional and Demo.command is the new informational field. Feature-grid
  passes the full demo onto CellContext.
- status.ts: drop variant helpers / types (no longer used).
- Add docs-status.ts reader + a stub docs-status.json so the enriched
  DocsRow renders without blowing up the build before the schema agent
  lands the generated bundle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:22:02 -07:00
Jordan Ritter 98841aaba5 fix(showcase/starters): chown WORKDIR to app user in Dockerfiles
Root cause: `WORKDIR /app` at the top of the runner stage creates /app
owned by root BEFORE the unprivileged `app` user is created. The
`COPY --chown=app:app` lines added in #4092 only reassign the files
being copied, NOT the /app directory itself.

Symptom: starter-mastra CRASHED on Railway with
  EACCES: permission denied, mkdir '/app/.mastra'
when `npx mastra dev` tried to create its cache dir under CWD as user
`app`. Likely co-victim class: any starter whose runtime invokes a Node
CLI that writes to CWD (e.g. langgraph-typescript via @langchain/langgraph-cli dev).

Fix: add `RUN chown app:app /app` immediately before `USER app` in all
four Dockerfile templates (typescript, python, dotnet, java), applied
uniformly to all 17 generated starters via the generator.

Verified locally:
 - docker build + run starter-mastra → container stays up, /app owned
   by app:app, /app/.mastra created successfully, /api/health returns 200.
 - docker build + run starter-langgraph-typescript → container stays up
   (8+ min), write to /app succeeds, /api/health returns 503 degraded
   (expected on dummy OPENAI_API_KEY) — NOT a crash.

All 17 starter Dockerfiles regenerated via
  pnpm run generate-starters
and the existing showcase/scripts vitest suite stays green (1073 tests).
2026-04-19 10:40:12 -07:00
Jordan Ritter de586ececd chore(showcase): regenerate starters post-#4095 rebase
- Regenerate agno starter to pick up agno>=2.5.17 (from #4095)
- Ratchet validate-pins fail-baseline hash to match new FAIL set
  (count unchanged at 110; hash rotates because agno Dojo/showcase
  pair now reflects the SDK upgrade)
2026-04-19 09:00:11 -07:00
Jordan Ritter 689a4b8da7 fix(showcase/strands): teach _FakeFastAPI stub to accept add_middleware
agent_server.py now calls app.add_middleware(HealthMiddleware) to serve
/health above the routing layer (restores starter health accuracy after
create_strands_app's catch-all at "/" shadowed @app.get("/health")).

Both _FakeFastAPI stubs — in tests/python/conftest.py and
tests/python/test_instrumentor_patch.py::test_agent_server_module_installs_patch —
only covered the decorator surface (get/post/put/delete/patch). When the
test execs agent_server.py verbatim under the stub, add_middleware raises
AttributeError and fails Python unit tests (3.12). The 3.10 matrix skips
strands (ag_ui_strands requires >=3.12) so the regression only showed
under 3.12.

Mirror the stub's API with a no-op add_middleware so the module loads
cleanly; the patched ThreadingInstrumentor assertion is still the
invariant under test.
2026-04-19 08:57:44 -07:00
github-actions[bot] e45d86b8f3 style: auto-fix formatting 2026-04-19 08:57:44 -07:00
Jordan Ritter dfd8346bc1 fix(showcase/starters/langgraph-fastapi): correct src.agents.tools.* imports
generate-starters.ts rewrites relative imports to absolute for langgraph
starters because langgraph_cli loads modules standalone rather than as
packages. The rewrite was flat:

  from .X import ...  ->  from <agentDir>.X import ...

For a file at <agentDir>/tools/get_weather.py, `from .types import ...`
resolves to `tools.types` — the CURRENT package — not `<agentDir>.types`.
The flat rewrite dropped the `tools` segment and produced:

  ModuleNotFoundError: No module named 'src.agents.types'

at startup. The agent crashed during module import; the entrypoint
pipe swallowed the traceback (see previous commit for the pipe bug);
the 2-3s sleep guard happened to fire while `sed` was still alive; and
Railway's /api/health probe reported `agent: "error"`.

Make the rewrite subdir-aware: compute the file's containing Python
package from its relative path under agentDest, and prepend that to the
relative import target. So `from .types import ...` inside
`src/agents/tools/get_weather.py` becomes
`from src.agents.tools.types import ...`.

langgraph-python has the same broken imports in its tools/__init__.py
but doesn't crash at runtime because main.py doesn't import from tools
(dead path). Regenerating fixes the dead code too.
2026-04-19 08:57:44 -07:00
Jordan Ritter de815ae2d5 fix(showcase/starters): capture real agent PID in entrypoint.sh + unbuffer stderr
The entrypoint.sh template used `cmd 2>&1 | sed 's/^/[agent] /' &`
followed by `AGENT_PID=\$!`. After a pipeline, `\$!` points to the LAST
command in the pipe (the `sed` process), not the agent. Every subsequent
`kill -0 \$AGENT_PID` and `wait -n \$AGENT_PID` was therefore monitoring
`sed`, which stays alive until its stdin closes — long after the agent has
crashed. Railway restarts the container mid-loop; the health probe sees
`{status: "degraded", agent: "down"}` for a few seconds during each cycle.

A second, compounding bug: `sed` buffers by default, and Python agents
buffer their own stdout, so a stack trace emitted during module import
could sit in userspace memory until the pipe closed — by which point the
log was discarded and the real cause of the crash was lost.

Fix both by switching to bash process substitution:
  cmd &> >(awk '{print "[agent] " \$0; fflush()}') &
  AGENT_PID=\$!
Process substitution does not create a pipeline, so `\$!` remains the
agent's PID. `awk` with `fflush()` flushes each prefixed line to the
container log immediately. Also export `PYTHONUNBUFFERED=1` at the
entrypoint level so Python-based agents don't buffer before awk.

Applies to all 17 starters (python, langgraph-python, langgraph-fastapi,
langgraph-typescript, mastra, typescript, java/spring-ai, csharp/
ms-agent-dotnet). Done once in generate-starters.ts + the template +
regenerated entrypoint.sh files.
2026-04-19 08:57:44 -07:00
Jordan Ritter 20efc40960 fix(showcase/starters): serve /health via middleware to bypass FastAPI mount shadow
Python FastAPI showcase starters were registering an @app.get("/health")
handler BEFORE app.mount("/", ...) (or the adapter's equivalent catch-all).
Starlette's Mount at "/" is a prefix match that swallows every subsequent
request including /health, so the decorated handler never fired and the
Next.js /api/health probe saw a non-JSON agent response and flagged the
starter as "degraded".

Install a HealthMiddleware (BaseHTTPMiddleware) that short-circuits
GET /health above the routing layer, which runs before any Mount can
claim the path. The agent mount is kept intact so AG-UI traffic at / is
unaffected.

Affects 10 Python agent starters: ag2, agno, claude-sdk-python,
crewai-crews, google-adk, langroid, llamaindex, ms-agent-python,
pydantic-ai, strands.
2026-04-19 08:57:44 -07:00
Jordan Ritter 6092c8f888 fix(showcase/agno): upgrade agno SDK to 2.5.17 (#4095)
## Summary

Bumps the `agno` Python SDK pin from `>=1.7.8` to `>=2.5.17` (latest
stable on PyPI) in `showcase/packages/agno/requirements.txt`.

## Why

The agno 1.x AGUI interface had a known bug where the terminal SSE event
(`RUN_FINISHED` / `TEXT_MESSAGE_END`) was never emitted, causing
consumers that block on stream close (such as `/api/smoke` calling
`await res.text()`) to hang until AbortSignal fired (25-45s).

Agno 2.5.17 emits the terminal `RUN_FINISHED` frame and closes the
stream cleanly, even when the underlying LLM call fails (dummy API key
-> 401).

## Version

| | Before | After |
|---|---|---|
| Pin | `agno>=1.7.8` | `agno>=2.5.17` |
| Installed | 1.7.x | 2.5.17 |

## Breaking changes encountered

None in this package. All existing imports continue to resolve without
change:

- `from agno.os import AgentOS`
- `from agno.os.interfaces.agui import AGUI`
- `from agno.agent.agent import Agent`
- `from agno.models.openai import OpenAIChat`
- `from agno.tools import tool`

No API adaptation was required.

## Relationship to PR #4093

PR #4093 works around the 1.x SSE bug in the consumer (`/api/smoke`) by
reading the stream incrementally and bailing on `TEXT_MESSAGE_CONTENT
"OK"`. With this upstream fix, that workaround becomes defense-in-depth
rather than load-bearing.

**Recommendation:** leave #4093 in place after this merges. It's cheap,
it's already written, and it protects against future agno regressions in
stream termination.

## Local verification evidence

All tests ran against a local `docker build -t agno-test .` of this
package (`agno==2.5.17`, Python 3.12.13).

### Imports
```
$ docker exec agno-test python -c "import agno; print(agno.__version__)"
2.5.17

$ docker exec agno-test python -c "from agno.os.interfaces.agui import AGUI; ..."
all imports OK
```

### /api/smoke timing (three consecutive runs)
```
run 1: time=0.256s code=200
run 2: time=0.157s code=200
run 3: time=0.151s code=200

{"status":"ok","integration":"agno","latency_ms":12,"timestamp":"2026-04-19T15:03:36.057Z"}
```
Previously: 25-45s hang ending in `AbortError`.

### Raw SSE stream inspection (dummy OpenAI key -> 401)
```
data: {"type":"RUN_STARTED","threadId":"test-thread","runId":"test-run",...}

data: {"type":"RUN_FINISHED","threadId":"test-thread","runId":"test-run"}
```
Stream closes immediately after `RUN_FINISHED`. Terminal event is
present.

### Demos (homepage + 9 demo routes)
```
/                          200   0.008s
/demos/agentic-chat        200   0.010s
/demos/hitl                200   0.005s
/demos/tool-rendering      200   0.005s
/demos/gen-ui-tool-based   200   0.006s
/demos/subagents           200   0.004s
/demos/shared-state-read   200   0.005s
/demos/shared-state-write  200   0.006s
/demos/shared-state-streaming 200 0.005s
/demos/gen-ui-agent        200   0.005s
```

### Agent object
```
agent loaded: Agent
tool count: 8
model: gpt-4o
```

## Test plan

- [x] `docker build` succeeds with new pin
- [x] `import agno` reports 2.5.17
- [x] All existing `from agno...` imports resolve unchanged
- [x] `/api/health` returns 200
- [x] `/api/smoke` returns 200 in <1s (was 25-45s hang)
- [x] Raw SSE stream contains `RUN_FINISHED` terminal event
- [x] Homepage + all 9 demo routes return 200
- [x] Agent object loads with 8 tools
- [ ] CI green on PR
2026-04-19 08:41:20 -07:00
github-actions[bot] e3b9defb45 style: auto-fix formatting 2026-04-19 15:08:32 +00:00
Jordan Ritter 0aa95d7e15 chore(showcase): ratchet validate-pins baseline 109->110 for google-genai pin 2026-04-19 08:07:02 -07:00
github-actions[bot] f5b69285c3 style: auto-fix formatting 2026-04-19 15:05:54 +00:00
Jordan Ritter 420254cfdc fix(showcase/agno): upgrade agno SDK to 2.5.17 to fix SSE stream termination
Bumps agno from >=1.7.8 to >=2.5.17 (latest stable on PyPI).

The agno 1.x AGUI interface had a known bug where the terminal SSE
event (RUN_FINISHED) was never emitted, causing consumers that block
on stream close (such as /api/smoke reading via res.text()) to hang
until AbortSignal fired at ~25-45s.

Verified locally against a docker build of this package that 2.5.17
emits the terminal RUN_FINISHED frame and closes the stream cleanly,
even when the underlying LLM call fails (dummy API key -> 401).

Measured /api/smoke round-trip: ~150-400ms (was 25-45s timeout).

All existing imports (agno.os.AgentOS, agno.os.interfaces.agui.AGUI,
agno.agent.agent.Agent, agno.models.openai.OpenAIChat, agno.tools.tool)
continue to resolve without changes — no API adaptation required.
2026-04-19 08:03:56 -07:00
Jordan Ritter e851ea70e6 fix(showcase/starters): regenerate google-adk starter after Gemini rewrite 2026-04-19 07:57:13 -07:00
Jordan Ritter 5bc22a396d fix(showcase/google-adk): use Gemini for A2UI planner instead of OpenAI
The google-adk package's primary agent uses Gemini via google-adk's
LlmAgent, but the secondary `generate_a2ui` tool was calling OpenAI's
gpt-4.1 through openai.OpenAI(). Cross-provider dependency with no
architectural justification — the A2UI planner's function-calling +
forced tool-choice pattern works equally well via google.genai.

Rewrite to use google.genai.Client.models.generate_content with a
ToolConfig forcing the render_a2ui function call (mode="ANY" +
allowed_function_names). Drop the openai + httpx direct dependency
entirely; google-genai is already pulled in transitively via google-adk
and is now pinned explicitly at >=0.8.0.

Replaces short-term hotfix PR #4090 (which pinned openai in
requirements.txt after post-merge Railway deploy of #4083 crashed
with ModuleNotFoundError).

Changes:
- src/agents/main.py: `_get_openai_client` → `_get_genai_client`
  (lru_cached google.genai.Client). Rewrote generate_a2ui to build a
  `types.Tool(function_declarations=[FunctionDeclaration])`, force the
  call via `types.ToolConfig(function_calling_config=...)` and read
  the response from `candidates[0].content.parts[*].function_call.args`.
  Narrowed except clause to `genai_errors.APIError`/`ClientError`/
  `ServerError` + `ValueError` (for construction-time failures).
  A2UI_MODEL env var overrides the default gemini-2.5-flash model.
- entrypoint.sh: `OPENAI_API_KEY` guard → `GOOGLE_API_KEY` guard;
  `REQUIRE_OPENAI_API_KEY` → `REQUIRE_GOOGLE_API_KEY`.
- requirements.txt: explicit `google-genai>=0.8.0` pin; no openai, no
  httpx.
- src/app/api/health|debug/route.ts: report GOOGLE_API_KEY status, not
  OpenAI/Anthropic/LangSmith (this is a Gemini-only package).
- playwright.config.ts: inject GOOGLE_API_KEY to the dev server env, not
  OPENAI_API_KEY / OPENAI_BASE_URL.
- tests/python/test_generate_a2ui.py: rewrote 19 tests to mock
  google.genai.Client in place of openai.OpenAI. Preserves every error
  branch (a2ui_llm_error, a2ui_empty_response, a2ui_no_tool_call,
  a2ui_invalid_arguments) plus happy path. Added A2UI_MODEL override
  coverage.
- tests/python/test_entrypoint_env_guards.py: migrated to exercise the
  GOOGLE_API_KEY / REQUIRE_GOOGLE_API_KEY branch.

Verification:
- Unit tests: 53 passed locally against Python 3.12 (google-adk + google-
  genai + all package deps installed).
- Docker build: clean (showcase-google-adk:local) via
  scripts/dev-local.sh.
- Container startup: `docker compose up -d google-adk` launches cleanly
  (no ModuleNotFoundError); uvicorn reports healthy; entrypoint emits
  the new WARN when GOOGLE_API_KEY is unset.
- L1+L2 smoke against local compose: both pass for google-adk.
- `import agents.main` inside the container no longer pulls in any
  `openai.*` module.
2026-04-19 07:55:27 -07:00
Jordan Ritter a4be366e95 perf(showcase): replace recursive chown in starter Dockerfiles with COPY --chown (#4092)
## Summary

Replaces `RUN chown -R app:app /app` with `COPY --chown=app:app` across
all 17 starter Dockerfiles + 4 shared templates.

Every starter ended with a recursive chown over `/app`, which walks ~50k
files (Next.js `node_modules` dominates) to fix ownership after the
fact. Under the 23-way Depot runner fan-out used by
`.github/workflows/deploy-showcase-services.yml`, that step consistently
ran 5+ minutes under I/O contention — busting the **15-minute GH Actions
job budget** before images could finish pushing.

Failed run this fixes:
https://github.com/CopilotKit/CopilotKit/actions/runs/24621469277 (22/23
showcase services cancelled mid-push).

## What changed

- Create the `app` user right after `WORKDIR /app` in every runner stage
so `--chown=app:app` resolves by name.
- Add `--chown=app:app` to every runner-stage COPY (including
multi-stage `COPY --from=frontend` and `COPY --from=<agent-builder>`).
- Drop the trailing `RUN chown -R app:app /app` (or the tail of the
compound RUN in the TS starters).
- For langgraph starters, fold `chown app:app /app/.langgraph_api` into
the same RUN as the `mkdir`, so `langgraph_cli`'s scratch dir remains
writable by the runtime user.
- Update `showcase/scripts/generate-starters.ts` so the shared templates
(`Dockerfile.python/typescript/dotnet/java`) emit the new pattern.
Starters are regenerated from templates.

## Local timing (Docker Desktop, single starter, no contention)

| Starter | Pre-fix | Post-fix | `chown -R` step |
| --- | --- | --- | --- |
| ag2 (Python) | 3:03 | 1:39 | 50.1s → removed |
| langgraph-fastapi | n/a | 1:41 | replaced with 0.2s targeted chown on
`.langgraph_api` |
| mastra (TS) | n/a | 2:16 | removed |

The real-world win on the 23-way Depot fan-out is substantially larger
than the local 50s baseline — recursive chown degrades super-linearly
with concurrent I/O pressure, which is exactly what the 5+ min Depot
step demonstrated.

## Smoke tests

- ag2 image: container starts clean as `app` user, all files under
`/app` owned by `app:app`, `/api/health` returns 200.
- langgraph-fastapi image: `/app/.langgraph_api` exists and is owned by
`app:app`.
- mastra image: builds cleanly, ownership correct.

## Test plan

- [ ] CI green (existing showcase starter smoke suite covers startup)
- [ ] Watch the next showcase deploy workflow run — expect jobs to
finish well under 15m

## Not touched

No workflow files, no `examples/` Dockerfiles (none matched the problem
pattern), no `chmod -R` offenders (none found).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-19 07:47:17 -07:00
Jordan Ritter 2c73a3480b ci: close showcase deploy automation gap for starters (#4082)
## Summary

Two related defects in the showcase deploy pipeline let stale images sit
live on Railway while Slack stayed green. This PR fixes both.

### Defect 1 — Drift detector skipped all starter services

`.github/workflows/showcase_smoke-monitor.yml` listed only 19
**package** slugs in its `SERVICES=(...)` array (ag2, mastra,
llamaindex, ...). Zero **starter** slugs. As a result:

- GHCR `showcase-starter-<svc>` tags were never checked for drift.
- `gh workflow run showcase_deploy.yml -f service=starter-*` was never
auto-dispatched.
- Starter services could run with weeks-old images and no alert would
fire.

`showcase_deploy.yml` already supports `starter-*` dispatch names and
already calls `serviceInstanceRedeploy` for any service with a
`railway_id`, so no change is required there. The fix is extending
`SERVICES=(...)` to include all 17 starter slugs via a
sparse-checkout-driven filesystem enumeration (no more literal
duplication between workflow and `showcase/starters/`).

### Defect 2 — Silent deploy failures reported green

`.github/workflows/showcase_deploy.yml` emitted `::warning::` and exited
0 when a service never returned 200 on its health path within 360s. The
legacy justification (`# Don't fail — sleep-on-idle services take time
to wake`) no longer applies: Railway is on the Pro tier with no
sleep-on-idle, so a 6-minute failure to become healthy is a real
failure. Changed to `::error::` + `exit 1`.

## Round 2 fixes

Round 2 CR raised six findings against the original smoke-monitor +
validator changes. All fixed in this PR:

- **BLOCKING 1/2 — smoke-monitor guard.** Replaced the magic `-eq 19`
sentinel with `grep -c '^starter-'` so adds/removes to the literal
non-starter list can't silently disable the guard. Added `shopt -s
nullglob` around the `showcase/starters/*/` loop so an empty starters
tree no longer corrupts `SERVICES` with a `starter-*` literal.
- **BLOCKING 3 — GHCR stderr isolation.** Dropped `2>&1` on the `gh api
-i` call; captured stderr to a temp file and surfaced it only when `gh`
returns a non-zero RC with no HTTP status. Auth / rate-limit / network
noise can no longer splice into the HTTP header block and poison
`HTTP_STATUS` / `API_BODY` parsing.
- **BLOCKING 4 — validator tests.** Added
`showcase/scripts/__tests__/validate-workflow-starters.test.ts` (12
specs): happy path, missing-from-options-only, missing-from-matrix-only,
missing-from-both, empty starters dir (exit 3), template/ excluded,
substring-spoof (starter-ag2 vs starter-ag2-extended), missing workflow
file (exit 3). Also extended `VALIDATE_WORKFLOW_STARTERS_REPO_ROOT` to
re-home the starters dir for testability.
- **MEDIUM 1 — YAML parsing.** Replaced the fragile regex-over-YAML
options scanner with a real `yaml.parse()` + typed navigation down
`on.workflow_dispatch.inputs.service.options`. ALL_SERVICES stays
regex-scanned (embedded JSON in a bash heredoc, with `${{ ... }}`
interpolations that aren't valid JSON pre-execution), but the
surrounding step is now located via YAML.
- **MEDIUM 2 — Slack list truncation.** Replaced `cut -c1-200` with a
`truncate_csv` helper that drops whole comma-separated entries until
under budget and appends `…` when truncated. No more
`starter-claude-sdk-pyth` mid-slug corruption.
- **MEDIUM 3 — template/ exclusion cross-references.** Both the TS
validator's `EXCLUDED_DIRS` and `showcase_smoke-monitor.yml`'s `[
"$slug" = "template" ] && continue` now carry `# keep in sync with ...`
comments pointing at each other.
- **NIT 2 — entry-check simplification.** Dropped the
belt-and-suspenders `import.meta.url === \`file://${argv[1]}\`` branch;
kept only the canonical `fileURLToPath(import.meta.url)` form.
- **NIT 3 — jq pipeline collapse.** Single-pass `.jobs[]? | select |
"\(...)"` replaces the three-pass `map | map | .[]` chain in the notify
step.

## Files changed

- `.github/workflows/showcase_deploy.yml` — warning → error + exit 1 on
unhealthy deploy; `truncate_csv` replaces `cut -c1-200` (3 sites);
single-pass jq pipeline in notify step.
- `.github/workflows/showcase_smoke-monitor.yml` — filesystem-driven
`SERVICES=(...)`, starter-count guard, `nullglob` loop, stderr-isolated
`gh api` call, cross-reference comment.
- `.github/workflows/showcase_validate.yml` — wires
`validate-workflow-starters` into CI.
- `showcase/scripts/validate-workflow-starters.ts` — YAML-aware presence
checks; env-var override homes both starters dir and workflow path.
- `showcase/scripts/tsconfig.json` — scripts-local tsconfig for LSP type
resolution.
- `showcase/scripts/__tests__/validate-workflow-starters.test.ts` — 12
specs covering the full matrix of drift scenarios.

## Test plan

- [ ] Next scheduled `showcase_smoke-monitor` run includes starter
services in its drift scan.
- [ ] A deliberately-unhealthy deploy (simulate by pointing health_path
at a 404) fails the job and fires the Slack alert.
- [ ] `showcase_validate` CI job runs `validate-workflow-starters` and
`npx vitest run scripts/__tests__/validate-workflow-starters.test.ts`
green.
2026-04-19 07:43:42 -07:00
Jordan Ritter c591ad71b1 perf(showcase): replace recursive chown with COPY --chown in starter Dockerfiles
Every starter Dockerfile ended with `RUN chown -R app:app /app`, which
recursively walks the full `/app` tree (Next.js `node_modules` dominates
— ~50k files) to fix ownership after the fact. On the 23-way Depot
runner fan-out used by .github/workflows/deploy-showcase-services.yml
this step consistently took 5+ minutes under I/O contention, busting
the 15-minute GH Actions job budget before the image could even finish
pushing (run 24621469277 — 22/23 services cancelled).

The standard Docker idiom is `COPY --chown=<user>:<group>` which applies
ownership during the copy step itself — no extra layer, no full-tree
traversal, zero runtime cost.

Changes:
  - Create the `app` user right after `WORKDIR /app` in every runner
    stage so `--chown=app:app` resolves by name.
  - Add `--chown=app:app` to every COPY instruction that lands files
    under `/app` in the runner stage (including `COPY --from=frontend`
    and `COPY --from=<agent-builder>` multi-stage copies).
  - Drop the trailing `RUN chown -R app:app /app` (or the tail of the
    compound RUN in the TS starters).
  - For langgraph starters, fold `chown app:app /app/.langgraph_api`
    into the mkdir RUN so langgraph_cli's scratch dir is still writable
    by the runtime user.
  - Update showcase/scripts/generate-starters.ts so the shared templates
    (Dockerfile.python/typescript/dotnet/java) emit the new pattern and
    the framework-specific COPY lines (`COPY ${dest} ./` for extra files
    and `COPY agent_server.py ./` for Python starters) include `--chown`.

Local verification (Docker Desktop, single starter, no contention):
  ag2 pre-fix:  3:03 total, `RUN chown -R app:app /app` = 50.1s
  ag2 post-fix: 1:39 total, no chown step
  langgraph-fastapi post-fix: 1:41 (langgraph_api chown is 0.2s)
  mastra post-fix: 2:16

Smoke test on ag2 image: container starts clean, all files under /app
owned by app:app, /api/health returns 200.

Under the 23-way Depot fan-out the real-world win is substantially
larger than the local 50s baseline because recursive chown degrades
super-linearly with concurrent I/O pressure.

Failed run that motivated this: https://github.com/CopilotKit/CopilotKit/actions/runs/24621469277
2026-04-19 07:41:55 -07:00
Jordan Ritter da487b7e8c ci: probe only /api/health in starter smoke test (#4088)
## Summary

The Deployed Starters smoke test is falsely reporting `status=404
path=/health` for every starter whose agent process is degraded. The
reported path is misleading — it's just the last path probed before the
test gave up. The real upstream is a 503 on `/api/health`.

## Root cause

`checkHealth()` in `showcase/tests/e2e/helpers.ts` defaulted to probing
`[\"/api/health\", \"/health\"]` in sequence. All deployed starters and
showcase backends serve their health endpoint at `/api/health` (standard
Next.js convention). `/health` returns a Next.js 404 HTML page.

When `/api/health` returns a legitimate 5xx (e.g. 503 `\"agent
degraded\"`), the helper's `res.ok()` check is false, so it falls
through to probe `/health`, gets a 404, and reports that as
`lastResult`. The Slack alert then reads `path=/health` and implies a
route-missing bug, hiding the real 503.

Verified with live probes:
- Healthy starter `/api/health` → `200 {\"status\":\"ok\",...}`
- Degraded starter `/api/health` → `503
{\"status\":\"degraded\",\"agent\":\"error\",...}`
- Either starter `/health` → `404` (Next.js not-found page)

## Fix (Shape A — remove the fallback)

Change the default `paths` argument from `[\"/api/health\",
\"/health\"]` to `[\"/api/health\"]`. No fallback. The reported status
and body are the actual `/api/health` response.

- `starter-smoke.spec.ts` passes an explicit `paths` argument and is
unaffected.
- `integration-smoke.spec.ts` L1 health check targets `/api/health` on
all deployed backends (verified live on langgraph-python and mastra).

## Out of scope

15/17 starters currently return 503 because their agent processes are
failing. That's a separate investigation — this PR is purely about
making the smoke alert accurate so that investigation can proceed
per-service.

## Evidence runs (all same failure mode tonight)

- https://github.com/CopilotKit/CopilotKit/actions/runs/24619147643
- https://github.com/CopilotKit/CopilotKit/actions/runs/24618944101
- https://github.com/CopilotKit/CopilotKit/actions/runs/24617315522

## Test plan

- [ ] Next scheduled smoke run surfaces `status=503 path=/api/health`
for the 15 degraded starters (agent-down state, clearly attributable)
- [ ] Healthy starters (langgraph-python, one other) continue to pass L1
- [ ] `starter-smoke.spec.ts` (local Docker starters) unaffected — still
uses explicit `starter.healthPaths`
2026-04-19 07:23:38 -07:00
Jordan Ritter 35a7e0038a fix(showcase): raise /api/smoke upstream timeout from 25s to 45s (#4089)
## Summary

Cold-start 502s on showcase package smoke probes (notably `agno`)
consistently land just over the 25s upstream budget. The Next.js
`/api/smoke` route uses `AbortSignal.timeout(25000)` to call its own
`/api/copilotkit` which proxies to the Python agent which calls the LLM.
On cold start, the end-to-end round-trip takes >25s and the probe aborts
with `latency_ms: 25001, stage: "timeout"`, returning 502.

This PR raises the upstream budget from **25s -> 45s** across all
showcase packages that exercise a full agent round-trip on smoke, and
bumps Next.js `maxDuration` from 30s -> 60s so the route can actually
run that long. The `create-integration` template is bumped too so new
packages inherit the new budget.

The 3 `langgraph-*` routes have a different (lightweight health probe)
pattern and `ms-agent-dotnet` is already at 50s — their `maxDuration` is
bumped for consistency but their abort budgets are left alone.

**Evidence — agno 502 alerts tonight (PDT):**
- 18:40 PDT attempt 1 — HTTP 502 on `/api/smoke`, self-recovered
- 19:42 PDT attempt 2 — HTTP 502 on `/api/smoke`, `/api/health` already
200

Both probes had `latency_ms: 25001, stage: "timeout"` — a textbook
cold-start boundary trip.

## Why 45s (not 60s or 90s)

The alert tells us the cold path lands **just over** 25s. 45s gives ~20s
of headroom — enough to absorb cold starts without making slow failures
look like slow successes. Tail latency beyond 45s would still alert,
which is the intent. If we see tail probes >45s, we'll revisit with a
post-deploy warmup ping.

## Files changed (17)

- 16 `showcase/packages/*/src/app/api/smoke/route.ts` (13 with both
abort + maxDuration bumped, 3 langgraph-* with only maxDuration bumped,
ms-agent-dotnet untouched)
- `showcase/scripts/create-integration/index.ts` template (new packages
inherit the new budget)

## Test plan

- [ ] Deploy to Railway, observe agno `/api/smoke` no longer 502s on
cold start
- [ ] Confirm other showcase services still smoke-pass
- [ ] If cold path drifts beyond 45s for any service, add a post-deploy
warmup step (deferred)

Do not merge yet — pending user review.
2026-04-19 07:23:26 -07:00
Jordan Ritter df83668bd0 fix(showcase): raise /api/smoke upstream timeout from 25s to 45s
Cold-start on showcase packages with heavy Python agents (agno in particular)
consistently lands just over the 25s budget, producing 502s on first probe
with latency_ms: 25001 and stage: "timeout". Raise the upstream
AbortSignal.timeout on /api/smoke from 25s to 45s across all 16 showcase
packages that exercise the full agent round-trip, and bump Next.js
maxDuration from 30s to 60s so the route can actually run that long.

Also bumps the create-integration template so new packages inherit the
new budget.

Tail-latency beyond 45s will still alert — which is the intent.

Evidence: agno 502 alerts tonight at 18:40 PDT and 19:42 PDT, both with
latency_ms: 25001, stage: "timeout" on /api/smoke. /api/health already
200 on both probes — pure cold-start boundary issue.
2026-04-18 20:56:30 -07:00
Jordan Ritter 31a7d23d1e ci: probe only /api/health in starter smoke test
The Deployed Starters smoke test in integration-smoke.spec.ts uses
checkHealth() with its default paths list ["/api/health", "/health"].
All deployed starters serve their health endpoint at /api/health
(standard Next.js convention); /health returns a Next.js 404 HTML
page. When /api/health returns a legitimate 5xx (e.g. 503
"agent degraded"), the fallback silently probes /health and the
resulting 404 becomes the reported failure — so the Slack alert
reads "status=404 path=/health" and implies a route-missing bug,
hiding the real upstream 503.

Shape A fix: change the default to ["/api/health"] only. No
fallback. The reported status and body are now the actual
/api/health response.

starter-smoke.spec.ts passes an explicit paths argument and is
unaffected. The integration backend L1 health check also targets
/api/health on all deployed backends (verified).

Evidence runs (all same failure mode):
- https://github.com/CopilotKit/CopilotKit/actions/runs/24619147643
- https://github.com/CopilotKit/CopilotKit/actions/runs/24618944101
- https://github.com/CopilotKit/CopilotKit/actions/runs/24617315522
2026-04-18 20:56:10 -07:00
Jordan Ritter de27e9453f fix(showcase/starters): add .NET build outputs to template .gitignore
ms-agent-dotnet starter was hand-patched with bin/, obj/, *.user, *.suo
but the template wasn't updated — drift-check kept flagging it on regen.
Add the entries to the template so all 17 starters pick them up uniformly
(harmless for non-.NET starters).
2026-04-18 20:47:51 -07:00
Jordan Ritter 51e6f4869d fix(showcase/starters): regenerate after R5 spring-ai template changes
R5 added BoundedToolCallingManagerConfig, OpenAiApiKeyValidator, and
WebClientConfig to showcase/packages/spring-ai/agent/ plus updated
application.properties and pom.xml. Starters were out of sync so
drift-check failed. Ran generate-starters.ts to sync.

Also add bin/ and obj/ .NET build artifacts to ms-agent-dotnet starter
.gitignore so generator output stays clean.
2026-04-18 20:44:39 -07:00
Jordan Ritter b426bcfad7 docs(showcase/spring-ai): clarify OPENAI_BASE_URL /v1 convention and consolidate JVM-arg rationale 2026-04-18 20:44:39 -07:00
Jordan Ritter abbf74d9eb docs(showcase/ms-agent-dotnet): replace line-number refs with symbolic names + rename SingleUseMessages 2026-04-18 20:44:39 -07:00
Jordan Ritter 61c3d8f354 docs(showcase/strands): fix comment accuracy for cross-package sync and dict internals 2026-04-18 20:44:39 -07:00