Commit Graph

209 Commits

Author SHA1 Message Date
Alem Tuzlak 06d24fd8ba Merge remote-tracking branch 'origin/main' into fix/scrub-open-gen-ui
The scrub and #4084 touched the same surface: #4084 re-added an `open:`
generative_ui profile listing `open-gen-ui`/`open-gen-ui-advanced`, and
re-added both features to `constrained-explicit.allowed`. Extending the
branch's scrub to both re-additions keeps the semantic consistent with
the schema (which already dropped `open` from the approaches enum).

- `showcase/shared/constraints.yaml`: drop `open-gen-ui` +
  `open-gen-ui-advanced` from `constrained-explicit.allowed`; drop main's
  re-added `open:` profile entirely.
- `showcase/packages/langgraph-python/manifest.yaml`: drop the now-orphan
  `open-gen-ui` + `open-gen-ui-advanced` feature and demo entries
  (validator confirmed they had no allowed approach left).
- Regenerated `showcase/shell/src/data/registry.json` + sibling
  `shell-docs`/`shell-dojo` registries and `constraints.json` via
  `pnpm --dir showcase/scripts generate-registry`. All 17 integrations
  validate.

`feature-registry.json` intentionally still defines both features — the
original scrub commits (2b996c54d, 27f886e59) left it untouched, so the
demo source files on disk also stay. Follow-up deletion if desired is
out of scope for this merge.
2026-04-22 13:17:34 +02:00
Jordan Ritter d708419911 fix(showcase/langgraph-typescript): bind liveness probe before heavy module imports
PR #4140 placed the liveness probe inside server.mjs under the belief it
would bind "synchronously before any await." That is not how ES modules
work — all top-level `import` statements resolve before any module-body
code runs. `@langchain/langgraph-api/server` takes ~4m30s to cold-import
on Railway (graph eval + tsx transpile), but the watchdog's 180s grace +
3x30s strikes = 270s kills the container ~4s before the probe would have
bound. Kill-loop forever.

Split into:
- `src/agent/liveness.mjs` (new entry): binds :8124/ok immediately using
  only `node:http`, then dynamic-imports server.mjs so the heavy langgraph
  bootstrap runs AFTER :8124 is already serving 200.
- `src/agent/server.mjs`: removed the now-dead sibling-probe code; comment
  block explains why.
- `src/agent/package.json`: `start` script now runs `node --import tsx
  liveness.mjs` instead of `server.mjs`.
- `entrypoint.sh`: comments updated to describe the new ordering.

Local smoke (docker build + run on linux/amd64): /ok returns 200 within
1.4s of container launch vs the previous ~4m34s.
2026-04-21 22:19:12 -07:00
Jordan Ritter ba578c5a29 feat(showcase): multi-stage Docker refactor across template, 17 starters, 17 packages (#4147)
## Summary
- Rewrite starter Dockerfile templates (`typescript`, `python`, `java`,
`dotnet`) as true multi-stage: builder has dev tools, runner is minimal.
No `pip`/`npm`/`pnpm`/`tsx`/`*-cli dev` in runtime stages.
- Encode prod-mode behaviors for `claude-sdk-typescript`, `mastra`,
`langgraph-typescript` directly in the TS template + generator
conditionals (folds in the pattern from #4132 for langgraph-ts; adds
parallel migration for claude-sdk-ts + mastra).
- Regenerate all 17 starter Dockerfiles.
- Rewrite all 17 package Dockerfiles to match the template shape
(multi-stage, venv builder for Python, 3-stage Python runtime, `USER
app`, etc.).
- Cold-start instrumentation on `claude-sdk-typescript` package
(absorbed from closed PR #4133).
- Pin `platforms: linux/amd64` in the showcase deploy workflow
(`docker/build-push-action` step) to match what Railway + GHCR require.
- Template `README.md` documenting multi-stage conventions, size target,
platform mandate, and the `dockerfile_hygiene` probe.
- `generate-starters.ts` strips the `langgraph-typescript` starter's
dead `server.mjs` + its `start` script (both reference deps only needed
in the package's prod-mode path).

## Why
Railway cold starts exceed the 180s watchdog grace when the runtime
stage boots via `langgraph-cli dev` / `npx mastra dev` / `npx tsx` — the
dev-mode work (TS compile, Rollup bundle, native libsql init) blocks the
health probe. #4132 proved the fix is to move this to image build time
for langgraph-ts. This PR generalizes that shape to all TS starters that
need it and reshapes every Dockerfile in the tree so runners no longer
carry dev toolchains.

Secondary outcomes:
- Smaller runtime images (pip/npm toolchain moved to builder only).
- Consistent `--platform linux/amd64` pinning end-to-end.
- No `pnpm install --frozen-lockfile` in the TS starter template
(starters are no-lockfile by design).
- `validate-pins` ratchet re-baselined to match the regenerated spec
strings.

## Test plan
- [ ] `docker build --platform linux/amd64` green for every starter (17)
and package (17) locally
- [ ] `pnpm -C showcase/scripts` vitest: 1085/1085 pass
- [ ] `tsc --noEmit -p showcase/scripts/tsconfig.json` clean
- [ ] `oxfmt --check` + `oxlint` clean on touched TS files
- [ ] validate-pins: count 110, hash matches updated baseline
- [ ] CI Showcase Validate green on PR HEAD
- [ ] Post-merge: showcase deploy for mastra + claude-sdk-typescript
goes green (currently flapping)
2026-04-21 20:14:57 -07:00
Jordan Ritter 98a6167de2 fix(showcase/packages): remove image-scope NODE_ENV=production from mastra/spring-ai, scope to next-start in langgraph-python entrypoint
Completes the NODE_ENV scope cleanup started in the prior Python
packages commit. Image-scope ENV NODE_ENV=production leaks into every
child process (agent subprocesses, healthchecks, shell scripts) —
several frameworks change behavior under NODE_ENV and should NOT see
'production' when running as an agent backend.

- mastra: Dockerfile drops image ENV; entrypoint.sh now invokes
  'env NODE_ENV=production npx next start' (was relying on image ENV).
- spring-ai: Dockerfile drops image ENV; entrypoint.sh now scopes
  NODE_ENV=production to the Next.js child only (Java agent never
  interpreted NODE_ENV but the scoping matches every other package).
- langgraph-python entrypoint: 'npx next start' now inherits
  NODE_ENV=production via 'env' wrapper (Dockerfile already removed
  image ENV in prior commit).
2026-04-21 19:05:45 -07:00
Jordan Ritter b9bcf2e6b6 fix(showcase/packages-python): drop to USER app across 11 Python packages, add missing /app/public COPY to langgraph-python
All 11 Python runners previously executed as root. Bring them into parity
with langroid (the one Python package that already had the pattern):

- Create uid/gid 1001 'app' user/group before any COPY
- Add --chown=app:app to runner-stage COPY directives
- USER app before EXPOSE/CMD
- langgraph-python: add the missing COPY --from=frontend /app/public line
  (every other Python package has it) plus an empty public/.gitkeep so
  the copy resolves

Packages touched: ag2, agno, claude-sdk-python, crewai-crews, google-adk,
langgraph-fastapi, langgraph-python, llamaindex, ms-agent-python,
pydantic-ai, strands.
2026-04-21 19:05:23 -07:00
Jordan Ritter 7febc5cf5d chore(showcase/package-claude-sdk-typescript): add cold-start instrumentation
Package-level service claims to listen on :8000 but Railway health probes
fail. Narrow the cold-start window by timestamping each phase from
entrypoint exec to first /health-serving moment:

- entrypoint.sh: print `[entrypoint] pre-node <ISO>` immediately before
  `node /app/agent_server.js`, pairing with the agent_server.ts prints
  below to chain timestamps across the exec boundary.
- agent_server.ts: print `[agent_server] module loaded <ISO>` before any
  side-effect imports (so import-time hangs surface distinctly from
  init-time hangs), `[agent_server] pre-Anthropic <ISO>` right before
  `new Anthropic(...)`, and include an ISO timestamp in the existing
  app.listen() callback so we can read the delta directly.

No behavior change — only logs added. Next deploy cycle should reveal
which phase consumes the budget and whether the failure mode is
listen-but-unreachable vs. listen-never-reached.
2026-04-21 18:39:48 -07:00
Jordan Ritter 8da71c4a0c merge: Task 3 Lane D — llamaindex/ms-agent-py/pydantic-ai/strands Dockerfiles 2026-04-21 18:38:44 -07:00
Jordan Ritter 46e0571b4c merge: Task 3 Lane C — google-adk/langgraph-fastapi/langgraph-python/langroid Dockerfiles 2026-04-21 18:38:43 -07:00
Jordan Ritter 23f4759ff3 merge: Task 3 Lane B — ag2/agno/claude-sdk-py/crewai-crews Dockerfiles 2026-04-21 18:38:43 -07:00
Jordan Ritter 6f5578d647 fix(showcase/package-ms-agent-dotnet): multi-stage Dockerfile with unprivileged user and scoped NODE_ENV 2026-04-21 18:37:59 -07:00
Jordan Ritter 95db0b8cfc fix(showcase/package-spring-ai): multi-stage Dockerfile with unprivileged user and Temurin 21 2026-04-21 18:34:46 -07:00
Jordan Ritter 67882698ea fix(showcase/package-mastra): multi-stage Dockerfile with unprivileged user 2026-04-21 18:30:52 -07:00
Jordan Ritter 55cf628341 fix(showcase/package-strands): multi-stage Dockerfile with venv builder
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
  pip and no build-essential

Mirrors the pattern already used by showcase/starters/strands/Dockerfile.
2026-04-21 18:28:57 -07:00
Jordan Ritter accfb49036 fix(showcase/package-pydantic-ai): multi-stage Dockerfile with venv builder
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
  pip and no build-essential

Mirrors the pattern already used by showcase/starters/pydantic-ai/Dockerfile.
2026-04-21 18:28:53 -07:00
Jordan Ritter a2736034c0 fix(showcase/package-ms-agent-python): multi-stage Dockerfile with venv builder
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
  pip and no build-essential

Mirrors the pattern already used by showcase/starters/ms-agent-python/Dockerfile.
2026-04-21 18:28:50 -07:00
Jordan Ritter 3fa53a5d35 fix(showcase/package-llamaindex): multi-stage Dockerfile with venv builder
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
  pip and no build-essential

Mirrors the pattern already used by showcase/starters/llamaindex/Dockerfile.
2026-04-21 18:28:47 -07:00
Jordan Ritter 64421cde1f fix(showcase/package-langgraph-typescript): multi-stage Dockerfile with unprivileged user and scoped NODE_ENV 2026-04-21 18:26:36 -07:00
Jordan Ritter e0db520506 fix(showcase/package-crewai-crews): multi-stage Dockerfile with venv builder
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.

Preserves behavior: entrypoint.sh, port 10000, src/aimock_toggle.py,
shared_python/ -> /app/shared/python/ + PYTHONPATH. No image-level
NODE_ENV (entrypoint.sh scopes it to Next.js only).
2026-04-21 18:26:24 -07:00
Jordan Ritter e97096c397 fix(showcase/package-claude-sdk-python): multi-stage Dockerfile with venv builder
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.

Preserves behavior: entrypoint.sh, port 10000, ENV NODE_ENV=production,
shared_python/ -> /app/shared/python/ + PYTHONPATH.
2026-04-21 18:24:15 -07:00
Jordan Ritter a94bb8bfc0 fix(showcase/package-claude-sdk-typescript): multi-stage Dockerfile with unprivileged user and scoped NODE_ENV 2026-04-21 18:22:35 -07:00
Jordan Ritter e4c567bf15 fix(showcase/package-agno): multi-stage Dockerfile with venv builder
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.

Preserves behavior: entrypoint.sh, port 10000, ENV NODE_ENV=production,
shared_python/ -> /app/shared/python/ + PYTHONPATH.
2026-04-21 18:22:11 -07:00
Jordan Ritter 35dedcb961 fix(showcase/package-langroid): multi-stage Dockerfile with venv builder 2026-04-21 18:20:20 -07:00
Jordan Ritter bc264aee95 fix(showcase/package-langgraph-python): multi-stage Dockerfile with venv builder 2026-04-21 18:20:17 -07:00
Jordan Ritter ebeb6d7069 fix(showcase/package-langgraph-fastapi): multi-stage Dockerfile with venv builder 2026-04-21 18:20:11 -07:00
Jordan Ritter f527a4ae38 fix(showcase/package-google-adk): multi-stage Dockerfile with venv builder 2026-04-21 18:20:09 -07:00
Jordan Ritter 28cdb4d19b fix(showcase/package-ag2): multi-stage Dockerfile with venv builder
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.

Preserves behavior: entrypoint.sh, port 10000, ENV NODE_ENV=production,
shared_python/ -> /app/shared/python/ + PYTHONPATH.
2026-04-21 18:20:00 -07:00
Jordan Ritter 229b3e1da9 fix(showcase/langgraph-typescript): liveness probe on sibling port to break watchdog restart loop
PR #4132 migrated away from `langgraph-cli dev` to the @langchain/langgraph-api
`startServer` Hono server. That server does bind `/ok` via its meta routes, but
only AFTER `registerFromEnv` imports the graph and spawns the N workers — on
Railway that window has been exceeding the watchdog's 180s startup grace plus
the 90s strike budget, producing an infinite restart loop (deploy ddc88ef9 is
currently flapping ~every 4m30s).

Move liveness off the heavy Hono path entirely:

- server.mjs now spins up a bare node:http server on port 8124 (configurable
  via HEALTH_PORT) *synchronously before any await*, answering GET /ok with
  {"status":"ok"}. Always live within ms of `node` boot.
- entrypoint.sh watchdog now probes 127.0.0.1:8124/ok instead of :8123/ok.

The liveness port is independent of whether :8123 has finished graph import,
so the probe reflects what the watchdog actually cares about: is the Node
process alive and its event loop responsive. A true hang (event loop pinned)
still fails the probe and triggers container restart, so the existing
hang-detection behavior is preserved.

Local Docker smoke (amd64): curl /ok returned 200 within 20s of container
start; LangGraph API logged "listening on 0.0.0.0:8123" ~15s later as normal.
2026-04-21 17:12:23 -07:00
Jordan Ritter dce445e6bc fix(showcase/langgraph-typescript): replace langgraph-cli dev with direct @langchain/langgraph-api server
Root cause of the 4+ day langgraph-typescript Railway flap: `npx @langchain/langgraph-cli dev` is a dev-mode CLI that compiles TypeScript on-demand per HTTP request, blocking the single-thread event loop ~5s per /schemas call. On Railway, Studio IPC handshake + langgraph-api JIT spawn + graph compile routinely exceeded the 90s watchdog budget; PR #4123's 180s grace papered over cold-start but didn't fix steady-state event-loop blocking.

Replace the CLI with a direct import of startServer from @langchain/langgraph-api/server (the same Hono server dev wraps, minus the tsx watcher, Studio handshake, and LangSmith tenant lookup). Schema extraction runs in a worker_thread and is pre-warmed in parallel with server startup.

Container-tested on node:22-slim:
- /ok live in <3s (vs. 5-30s dev)
- /ok steady-state <10ms
- /assistants/search returns the registered graph
- Pre-warm log: [server] Pre-warmed schemas in ~1.3-3.4s

Caveats:
- Uses --legacy-peer-deps for @langchain/langgraph-sdk version skew.
- @langchain/langgraph-api exports ./server but it's not officially public API — exact version pin (1.1.17) required.
- /runs streaming not end-to-end tested (dummy key); /assistants/search and schema extraction verified.
2026-04-21 14:34:22 -07:00
Jordan Ritter c3ddd92be9 fix(showcase): extend watchdog 180s grace to claude-sdk-typescript + mastra
Two more starters are restart-looping with the same pattern #4123 fixed
for langgraph-*: the 90s (3-strike) watchdog budget is shorter than the
cold-start path on a fresh Railway container, so the agent gets killed
before it ever reports healthy, and the loop never breaks.

Railway evidence:
- claude-sdk-typescript (package, :8000): restart-looping since 04-20
  16:54 UTC. Runs compiled `node /app/agent_server.js` which spins up
  the full @anthropic-ai/claude-agent-sdk. Package entrypoint is hand-
  written (not generator-emitted), so add the same grace block inline
  matching #4123's shape.
- mastra (starter, :8123/api): restart-looping since 04-20 18:18 UTC.
  Runs `mastra dev` on :8123 alongside Next.js on ${PORT:-10000}.
  `mastra dev` performs a tsx build + Mastra server boot on first
  request — legitimate supervised process, grace is the right fix.

Package-level mastra has no watchdog (the entire package IS the Next.js
app; no separate agent to watchdog) — PR #4116's classification of
"N/A" was correct for the package. The starter has a separate Mastra
dev server, so the generator-emitted watchdog is legitimate there.

Per-framework grace (not universal) preserves the #4123 design: uvicorn
and express agents are responsive within the 2-3s sleep before
AGENT_HEALTH_CHECK, so adding grace elsewhere would only delay
legitimate restart on true hangs.

This commit covers the generator mapping + the hand-written package
entrypoint. The regenerated starter entrypoints land in the next commit.
2026-04-21 11:47:57 -07:00
Jordan Ritter f5b7a65b3d fix(showcase): langgraph watchdog regression — add startup grace period
PR #4116 introduced a generalized silent-hang watchdog that polls the
agent health endpoint every 30s and kills the agent after 3 consecutive
failures (~90s). This worked for fast-starting agents (uvicorn,
express) but regressed langgraph-typescript on Railway: `langgraph-cli
dev` does a heavy cold-start (Studio IPC setup + @langchain/langgraph-api
JIT spawn + graph compile) that routinely exceeds 90s on a fresh
container, so the watchdog was killing the process before /ok ever
became reachable — producing the 04-20 17:05 UTC restart loop on
deployment 58bbebe8-7a94-4f99-b6e4-ffcbb4eb78b9.

Phase 1 verification:
  - `@langchain/langgraph-cli@1.1.17/dist/cli/up.mjs:27` uses /ok as
    its own health probe, and `@langchain/langgraph-api@1.1.17/dist/
    api/meta.mjs:64` registers `api.get("/ok", ...)` — so the watchdog
    path is correct. The regression is purely timing.
  - Railway logs show no successful /ok probe before kill-loop starts.

Fix (Option B — startup grace): add a per-framework startup-grace
window in getWatchdogGraceSeconds(). For langgraph-* starters, the
watchdog now waits up to 180s for the first healthy /ok probe before
arming the strike counter. If /ok comes up sooner, fall through
immediately. If 180s elapses without success, arm the counter anyway —
the steady-state watchdog will then handle a true hang on the normal
90s schedule.

Non-langgraph starters (crewai-crews, ag2, agno, etc.) have grace=0 and
behave exactly as before PR #4116 — the 2–3s `sleep` before the health
check is sufficient for uvicorn-based agents.

Files changed:
  - showcase/scripts/generate-starters.ts — new getWatchdogGraceSeconds()
    + grace block in getWatchdogBlock()
  - showcase/starters/*/entrypoint.sh — regenerated (grace block for
    langgraph-*, comment-only for all others)
  - showcase/packages/langgraph-typescript/entrypoint.sh — hand-edited
    grace block (this package uses its own hand-edited entrypoint, not
    the template)
  - showcase/packages/langgraph-fastapi/entrypoint.sh — same
  - showcase/packages/langgraph-python/entrypoint.sh — same

Not a revert of #4116: the silent-hang vulnerability class remains
covered (the watchdog still kills after 90s of steady-state failures;
the grace only defers the first strike).

Validated:
  - bash -n on all 19 edited entrypoint.sh files
  - 1079/1079 showcase-scripts vitest pass
2026-04-21 10:48:07 -07:00
Jordan Ritter 6f7dd1b8b1 fix(showcase/packages): extend silent-hang watchdog across Bucket-B + spring-ai
Hand-applied watchdog + unbuffered-stdout block for every Bucket-B package
entrypoint where the shape couldn't come from the starter generator:

  * ag2, agno, claude-sdk-python, claude-sdk-typescript, google-adk,
    langgraph-fastapi, langgraph-python, langgraph-typescript, langroid,
    llamaindex, ms-agent-dotnet, ms-agent-python, pydantic-ai, strands
    -> watchdog polls the framework-native health endpoint; Python variants
       also get `python -u` + awk fflush log prefixing.

  * spring-ai (Bucket C) -> watchdog extends the existing startup probe;
    polls :8000/health every 30s and kills PID on 3 consecutive failures.
    Added WATCHDOG_PID cleanup to the post-wait block.

  * mastra is single-`exec next start` on the package side — no agent
    process to probe, so no watchdog needed (N/A).

Reference shape: showcase/packages/crewai-crews/entrypoint.sh (unchanged
from origin/main; proven in production via PRs #4114 + #4115).
2026-04-21 09:20:58 -07:00
Jordan Ritter 9379b8855a fix(showcase/crewai-crews): upgrade ag-ui-crewai to 0.2.0 + drop shim + -u flag
Root-cause fix for the 04-21 silent-hang incident on the crewai-crews
Railway deploy. Three tightly-coupled changes:

1. Bump ag-ui-crewai pin from `>=0.1.4,<0.1.6` to `>=0.2.0,<0.3.0`.
   0.1.5 had three defects that wedged the agent: unguarded
   `source.state.messages` access, an orphan `asyncio.create_task` with
   no cancel ref, and a sync `completion()` call that pinned the event
   loop. All three are fixed in ag-ui PR #1550, released as 0.2.0 on
   2026-04-18. Our upper ceiling was blocking the fix.

2. Remove the pre-bind LLM crash hardening shim in `agent_server.py`.
   The shim monkey-patched `crewai.cli.crew_chat.generate_*_description_with_ai`
   to static strings so that `ChatWithCrewFlow.__init__` — which
   ag-ui-crewai <= 0.1.5 invoked at endpoint-registration time, BEFORE
   uvicorn bound its port — could not crash the process before the HTTP
   server was listening. 0.2.0 defers `ChatWithCrewFlow` construction to
   first request via a module-scoped `_cached_flow` + `asyncio.Lock`
   inside `add_crewai_crew_fastapi_endpoint`. Any LLM hiccup now
   surfaces as a 5xx on the first request instead of a startup crash,
   which is what the shim was reaching for. The shim is dead code on
   0.2.0 and has been removed (with `logging` import dropped as it was
   only used by the shim).

3. Add `python -u` to the uvicorn invocation in `entrypoint.sh` as a
   belt-and-suspenders complement to the existing `PYTHONUNBUFFERED=1`
   export. The env var can in principle be un-exported by a child;
   `-u` forces unbuffered stdout/stderr at the interpreter level and
   is not overridable by user code. Combined with `awk '{...; fflush()}'`
   in the pipe (already in place), this guarantees uvicorn request
   lines reach Railway's log stream line-at-a-time. During the 04-21
   incident Railway saw only ~15 log lines over 9h of uptime because
   of buffering through a previous `sed` formulation.

Also updates `showcase/scripts/fail-baseline.json`'s `validatePinsFailHash`
to match the new `ag-ui-crewai` spec string. Pin-drift FAIL count is
unchanged (110); the hash changed only because the `ag-ui-crewai` line
in the FAIL set went from `>=0.1.4,<0.1.6` to `>=0.2.0,<0.3.0`.

Verified locally:
- `pip install -r requirements.txt` resolves `ag-ui-crewai-0.2.0` cleanly.
- `python -u -m uvicorn agent_server:app` starts; `/health` returns
  200 `{"status":"ok"}`; request lines appear in real-time logs.
- `pytest tests/python/` — 94/94 pass.
- `pnpm -C showcase/scripts test` (vitest) — 1079/1079 pass.
- `validate-pins.ts` — count=110 matches baseline; hash updated.

Upstream refs:
- crewAI issue: https://github.com/crewAIInc/crewAI/issues/5510
- ag-ui PR #1550: https://github.com/ag-ui-protocol/ag-ui/pull/1550

Intentionally NOT in this PR:
- `showcase/starters/crewai-crews/` parity backport (the starter still
  carries the 0.1.5 pin and the shim).
- The 14-starter watchdog generalisation.
Both belong to the silent-hang vulnerability-class work tracked
separately.
2026-04-21 08:49:21 -07:00
Jordan Ritter 9ce6513306 fix(showcase/crewai-crews): watchdog agent process + decouple smoke upstream timeout
Production deploy of showcase-crewai-crews was stuck in agent:"down" for
~4-5h on 2026-04-21. Railway deployment logs show the FastAPI agent on
:8000 was reachable at startup and handled hundreds of requests, then
stopped responding at ~06:56-07:01 UTC after an
unhandledRejection: UND_ERR_BODY_TIMEOUT from the Next.js runtime. The
Python process did not exit — it hung — so bash's wait -n never fired,
the container never restarted, and every subsequent request stacked up
on undici's default 5-minute headers timeout (UND_ERR_HEADERS_TIMEOUT).

Two surgical fixes:

1. entrypoint.sh: add a watchdog goroutine that polls
   http://127.0.0.1:8000/health every 30s with a 5s curl timeout. After
   3 consecutive failures (~90s unreachable) it SIGKILLs the agent PID,
   causing wait -n to return and the container to restart. This converts
   a silent agent hang into a fast, loggable restart. Also adds structured
   [entrypoint] / [agent] / [nextjs] / [watchdog] log prefixes, a startup
   PID-is-alive check, and PYTHONUNBUFFERED=1 so crashes surface before
   the log pipe closes — mirroring the starter entrypoint pattern.

2. src/app/api/smoke/route.ts: split the upstream fetch timeout
   (UPSTREAM_TIMEOUT_MS = 25_000) from the route's overall budget
   (maxDuration = 60). Previously the inner fetch had AbortSignal.timeout(45000)
   which, on a hung agent, burned nearly all of the 60s before Next.js
   killed the request — producing HTTP 000 at the caller instead of a
   structured stage: "timeout" JSON response. With the shorter inner
   timeout the route can report a clean timeout within ~25s even when
   the agent is wedged.

Both fixes are defensive against upstream CrewAI ChatWithCrewFlow
deadlocks (which the logs show happening repeatedly via the pre-existing
"Cannot send 'RUN_FINISHED' while steps are still active" errors). The
watchdog is scoped to this package only; the starter already has a
richer entrypoint with similar intent.
2026-04-21 05:39:18 -07:00
Atai Barkai 7644f180a6 fix(showcase): restore shared-state-read-write scaffolds + regen starters
Post-merge CI caught two regressions:

1. shared-state-read-write demo directories were on disk but never
   committed — 16 packages' manifests reference the demo but the
   page.tsx scaffolds weren't tracked. This failed validate-parity,
   bundle-demo-content tests, and drift-check transitively.

2. langgraph-python starter templates drifted after main added
   docstring region markers to weather-tool-backend; regenerating
   via `npx tsx generate-starters.ts` syncs the 17 starters.

Bundle (showcase/shell/src/data/demo-content.json) regenerated to
pick up the new demo content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:34:13 -07:00
Atai Barkai 62a05003c1 Merge remote-tracking branch 'origin/main' into atai/2026-04-18/feature-port-no-docker-restructure
# Conflicts:
#	.github/workflows/showcase_deploy.yml
2026-04-19 21:29:07 -07:00
Atai Barkai 151ccb9ad8 perf(showcase/langgraph-python): mcp-apps — speed-biased system prompt (target <30s generation)
Drops the aspirational 185-line prompt (read_me preflight, multi-call
iteration, palette discipline, zone backgrounds, label/fixedPoint rules,
opening+mid+final cameraUpdates) in favor of a ~35-line shape that biases
the agent toward exactly one create_view call with 3-5 elements and a
single framing cameraUpdate. Downgrades gpt-4.1 → gpt-4o-mini since the
element schema is simple JSON and model latency dominates end-to-end time.

Measured end-to-end (agent/run SSE to RUN_FINISHED) on the showcase
runtime: 5-6s per prompt across two prompts × two runs, all producing a
single create_view call with 8 elements (clean 3-step flowchart;
client/server/db diagram). Previously 3-5 minutes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:14:48 -07:00
Atai Barkai 8e07f479c5 refactor(showcase/langgraph-python): interrupt-headless — side-by-side chat + empty-app-pane popup
Restructure the headless-interrupt cell so the app surface is empty by
default and the popup appears there (not inside the chat or as a global
modal) when the backend calls `interrupt()`. The user triggers the run
from a pre-built suggestion chip in the right-hand `CopilotChat`, which
mirrors the other in-app-action demos (hitl-in-app, gen-ui-interrupt).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:12:57 -07:00
Jordan Ritter 4d1147d05f fix(showcase/agno): read SSE incrementally in smoke route to work around agno SDK success-path hang
Reimplements the approach from the now-closed PR #4093 (which I closed
after #4095 merged the agno 2.5.17 upgrade, on the belief that the SDK
bump had fixed the underlying SSE termination bug).

Follow-up evidence collected 2026-04-19 showed the SDK upgrade only
addressed the error path — production /api/smoke with real API keys
still hangs because the agno SDK does not reliably emit TEXT_MESSAGE_END
/ RUN_FINISHED on the success path. Documented in Notion page
3473aa381852815b9765fd28fa53275a, item #5.

Change: instead of `await res.text()` (which only resolves when the
server closes the stream), read res.body incrementally with
getReader() + TextDecoder. Short-circuit as soon as the buffer contains
`"type":"TEXT_MESSAGE_CONTENT"` with `"OK"` — the agent has produced
the expected output, so we cancel the reader and return a 200 success
envelope without awaiting a terminal event that may never arrive. If
the stream closes cleanly without confirmation, return a diagnostic
502 with stage="response_incomplete" so regressions surface clearly.
Keep the existing 45s AbortSignal timeout as a final safety net.

Response envelope shape is preserved for the smoke monitor parser.

This is a client-side mitigation, not a fix. The real fix belongs
upstream in the agno SDK's AG-UI interface. Keep this PR open until
upstream is addressed and verified in production with real keys.
2026-04-19 21:07:21 -07:00
Atai Barkai 0b171a5d12 feat(showcase/langgraph-python): mcp-apps — rich Excalidraw prompting guidance
Rewrites the mcp-apps agent system prompt from a minimal "call a tool and
populate the canvas" instruction into a detailed Excalidraw-aware
playbook. Adoptions:

- Workflow: instructs the agent to call `read_me` first (the Excalidraw
  MCP server's own format reference) before `create_view`, and to use
  `restoreCheckpoint` for follow-up edits.
- Layout principles: left-to-right vs. top-down vs. hub-and-spoke,
  3-7 node target, consistent sizing (160x70 rectangles, 120x80
  diamonds) and spacing (40-80px gutters, 60-120px viewport padding).
- Camera framing: opening `cameraUpdate` with a 4:3 size (400x300 →
  1600x1200), optional mid-diagram pans, and a REQUIRED final
  cameraUpdate that re-frames the whole diagram (otherwise the Excalidraw
  animation camera ends zoomed on the last-drawn element).
- Labels: prefer `label: {text, fontSize}` on shapes/arrows (auto-centers,
  auto-resizes) over bare `text:` fields (which Excalidraw silently
  ignores on rectangles).
- Arrow bindings via `fixedPoint` edge anchors rather than hand-placed
  coordinates; dashed stroke for async/optional paths; short label
  annotations on decision transitions.
- Palette discipline: draws from the read_me palette (pastel fills +
  primary strokes), caps at 3-4 background colors per diagram, assigns
  them semantically (blue=frontend, green=success, yellow=decision,
  red=error).
- Progressive emission order so the draw-on animation tells a story
  (zone → shape → its label → its outgoing arrows → next shape), with
  decorations last.

Also upgrades the model from gpt-4o-mini to gpt-4.1 because Excalidraw
element emission is structured/coordinate-heavy and a smarter model
produces visibly cleaner layouts (aligned nodes, edge-bound arrows,
labeled shapes) in practice.

Before: 3 colored boxes with no text (bare `text:` field silently
dropped), arrows ending on shape centers, no camera framing, no title.
After: 3 labeled boxes with consistent palette, edge-bound arrows with
transition labels, title text, cameraUpdate framing the diagram, and
the agent correctly consults `read_me` once per conversation for the
authoritative format reference.

Progressive streaming (Part 2 of the ask) was investigated and flagged
as shared-package-bound: `@ag-ui/mcp-apps-middleware` emits a single
`ACTIVITY_SNAPSHOT` only AFTER `RUN_FINISHED`, so there are no partial
tool-input events to consume in the cell. The Excalidraw MCP server does
animate elements in order once the full input arrives, which gives the
visual impression of progressive drawing without runtime changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:05:00 -07:00
Atai Barkai ac361eb853 feat(showcase/langgraph-python): open-gen-ui — educational visualization suggestions + rich prompt guidance
Replaces the generic greeting-card / Pomodoro / revenue-chart suggestions
on the Fully Open-Ended Generative UI (minimal) cell with a set tuned for
intricate, educational visualisations: 3D airplane pitch/yaw/roll, a
feed-forward neural network's forward pass, quicksort, and a Fourier
series square-wave build-up.

Uses the provider's `openGenerativeUI.designSkill` prop (which injects
agent context for the built-in `generateSandboxedUi` tool) to swap in a
visualisation-tuned authoring prompt in place of the default shadcn
design language — covering SVG/canvas geometry, CSS keyframe motion,
mandatory labels + legends + callouts, a semantic palette (indigo /
emerald / amber / rose / slate), typography scale, and the self-running
constraint for the minimal cell. Agent system prompt updated in parallel
so the LLM knows the cell's focus and contract up-front.

Advanced variant untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:01:21 -07:00
Atai Barkai 420bc802b6 fix(showcase/langgraph-python): interrupt-headless cell re-run flow
The per-kickoff `agent.setMessages([])` call was racing with the
subsequent `addMessage` + `runAgent` pair, causing the second run
attempt to silently not dispatch (connection-refused on resume in some
timings). Drop the reset — rely on the fresh langgraph thread that each
`runAgent` implicitly produces, and on the existing `runId` marker to
gate ResultCard visibility so the stale result doesn't flash under the
button mid-run.

Validated: initial → modal → result → re-click → modal appears again,
all without a page refresh.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:01:06 -07:00
Atai Barkai 4bc5f0f179 refactor(showcase/langgraph-python): gen-ui-agent uses CopilotKitMiddleware + direct state_schema
`create_deep_agent` does not accept `state_schema=` as a kwarg (confirmed
against the installed signature; state schema is only pluggable via
middleware). The minimal state-extending middleware is kept but renamed
to `_GenUiStateMiddleware` with a comment pinning its one purpose, and
`CopilotKitMiddleware` is added so this cell matches every other
langgraph-python cell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:57:10 -07:00
Atai Barkai 9a737b6c8d fix(showcase/langgraph-python): open-gen-ui-advanced sandbox-function bridge round-trip
The sandboxed iframe runs with `sandbox="allow-scripts"` only, so a
`<form>` with `<button type="submit">` had its submit blocked by the
browser BEFORE the onsubmit handler could call `e.preventDefault()` —
which meant `Websandbox.connection.remote.evaluateExpression(...)`
never fired. The agent also read `res.result` while the host handler
returns `{ ok, value }`.

Tightened the system prompt and per-suggestion prompts to explicitly
forbid `<form>` / `type="submit"` and to steer the LLM toward the
handler's exact return shape (`res.value` / `res.error`). Validated
end-to-end via Playwright: both `evaluateExpression` and `notifyHost`
now round-trip with visible results in the sandbox UI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:53:45 -07:00
Atai Barkai d8e9092e99 feat(showcase/langgraph-python): declarative-gen-ui — advertise PieChart + BarChart in a2ui_dynamic system prompt
The PieChart + BarChart renderers were added in 8be81821e as part of the
brand-polish pass, but the a2ui_dynamic agent's system prompt was still
only mentioning dashboards/status/KPIs. Expand the prompt to enumerate
the registered catalog (Card, StatusBadge, Metric, InfoRow, PrimaryButton,
PieChart, BarChart) and name when each chart fits — so the LLM reliably
reaches for PieChart on part-of-whole breakdowns and BarChart on
cross-category comparisons.

Validated on port 4100 with Playwright: "Show a pie chart of sales by
region" renders a brand-coloured donut with legend + percentages, and
"Render a bar chart of quarterly revenue" renders a polished Recharts bar
chart with tooltip. No "Unknown component" errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:50:54 -07:00
Atai Barkai 8be81821e2 refactor(showcase/langgraph-python): polish custom cell components to CopilotKit brand
Apply the verified CopilotKit brand palette (lilac #BEC2FF, mint #85ECCE,
near-black #010507, neutral greys) across every custom-defined component in
the langgraph-python cells. Replaces ad-hoc indigo/purple/amber/slate
Tailwind classes with brand tokens, softens corners (rounded-xl/2xl),
switches shadow-lg to shadow-sm, and normalises pill/label styling to
10-11px uppercase tracking-wider.

Preserves all component props, data-testid attributes, and @region markers.
Beautiful-chat (reference) is untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:38:36 -07:00
Atai Barkai 0cb9cd13ba refactor(showcase): rename In-Chat HITL rows to name the hook API
gen-ui-interrupt and hitl-in-chat both render an interactive time-picker
inline in the chat, but via different API layers:

- gen-ui-interrupt -> useInterrupt (low-level primitive)
- hitl-in-chat -> useHumanInTheLoop (ergonomic wrapper)

Rename to make the dashboard row label reflect the API distinction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 20:32:23 -07:00
Atai Barkai 63a0c19de7 refactor(showcase/langgraph-python): gen-ui-agent as deep agent with explicit state + tool
Rewrites the gen-ui-agent demo as a minimal deepagents-powered agent that
defines its own `GenUiAgentState` schema (extending langchain's AgentState)
and exposes a custom `set_steps` tool. The tool returns Command(update=...)
to mutate the typed `steps: list[Step]` field, and a one-line middleware
registers the extended schema with deepagents. A last-wins reducer on
`steps` tolerates concurrent tool calls.

No StateGraph, no copilotkit_emit_state, no TodoListMiddleware - the pattern
mirrors how real applications extend deepagents with domain state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:02:21 -07:00
Atai Barkai a1ed347faa Merge remote-tracking branch 'origin/main' into atai/2026-04-18/feature-port-no-docker-restructure
# Conflicts:
#	showcase/scripts/__tests__/bundle-demo-content.test.ts
#	showcase/scripts/lib/manifest.ts
#	showcase/scripts/validate-parity.ts
2026-04-19 16:40:48 -07:00
Atai Barkai d32e780696 refactor(showcase/langgraph-python): rewrite reasoning agents as minimal deep agents
Replace the synthetic _ReasoningEmitterModel + hand-rolled StateGraph
scaffolding in reasoning_agent.py and tool_rendering_reasoning_chain_agent.py
with minimal create_deep_agent graphs (mirrors gen_ui_agent). Drops ~340
lines of custom graph wrappers / synthetic reasoning emitters while
keeping the four mock tools (get_weather, search_flights, get_stock_price,
roll_dice) intact for the tool-rendering-reasoning-chain cell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:31:47 -07:00
Atai Barkai 4bc956346f fix(showcase): null out hallucinated docs-links entries (wrong framework, wrong shell path)
Prior 4085 port blindly copied 4084 docs-links.json patterns, producing many
entries that pointed at wrong-framework docs (e.g. crewai-crews → crewai-flows),
nonexistent shell paths (e.g. /claude-sdk-typescript/... prefix doubled into
/claude-sdk-typescript/claude-sdk-typescript/...), or the wrong field name
(mastra used `shell_docs_url` with absolute http://localhost:3000 URLs).

Audit against live docs.copilotkit.ai and the 4085 shell at :4010 surfaced:
- 9 og_docs_url 404s (8 on claude-sdk-typescript under a nonexistent
  /claude-sdk-typescript/* slug, 1 on /ag2/multi-agent/subagents)
- 24 shell_docs_path 404s (8 claude-sdk-typescript, 6 pydantic-ai, 4 strands,
  4 mastra, 2 langgraph-python)
- crewai-crews pointing entirely at /crewai-flows/... (different product)

Fix: point each entry at a real docs.copilotkit.ai page scoped to the correct
framework OR null it out; normalize shell_docs_path to leading-slash form
that composes with the DocsRow `${shellUrl}/${slug}/unselected${path}` scheme;
null google-adk shell paths (no google-adk-scoped shell docs exist, better to
show ✗ than mislead). Regenerated registry + docs-status bundles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:11:52 -07:00