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).
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
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).