Commit Graph

650 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
Alem Tuzlak 7c00903c6a Merge remote-tracking branch 'origin/main' into jpr5/ci-workflow-optimization
# Conflicts:
#	.github/workflows/showcase_capture-previews.yml
2026-04-22 13:03:04 +02:00
Jordan Ritter c71641db2e chore(showcase/scripts): revert validate-pins baseline hash to CI-observed value 2026-04-21 20:10:21 -07:00
Jordan Ritter 40adafd199 fix(showcase/scripts/generate-starters): strip langgraph-ts starter start script referencing deleted server.mjs 2026-04-21 19:38:29 -07:00
Jordan Ritter c6437b3443 fix(showcase/scripts/generate-starters): emit || true on watchdog cleanup kill, preserve --rootDir ., strip dead langgraph-ts server.mjs, guard AGENT_DIR expansion
- entrypoint.template.sh: cleanup trap + final kill now tolerate no-match
  (|| true) so trap doesn't abort shell when one of the three PIDs has
  already exited. Package-level entrypoint.sh already had this shape.
- generate-starters.ts: claude-sdk-typescript tsc now uses --rootDir .
  so emitted artifact is /app/dist/agent/index.js (matches entrypoint
  expectation + runner-stage COPY). tsc was inferring agent/ as rootDir
  and stripping the prefix.
- generate-starters.ts: drop server.mjs from langgraph-typescript starter
  output. The starter uses 'npx @langchain/langgraph-cli dev' and never
  invokes server.mjs; the file imports @langchain/langgraph-api/server
  which is not in the starter's extraDependencies (resolution relied on
  transitive hoist via @langchain/langgraph-cli). The file stays in the
  PACKAGE where prod mode does use it.
- generate-starters.ts: assert agentDir non-empty / non-absolute before
  Dockerfile substitution so the 'rm -f {{AGENT_DIR}}/package*.json' line
  can never degrade into 'rm -f /package*.json' across /app.
2026-04-21 19:01:10 -07:00
Jordan Ritter bff45416e7 docs(showcase): document multi-stage Dockerfile conventions
Add showcase/starters/template/README.md covering the multi-stage build
convention (builder + runtime split), the >=40% image-size reduction
target, the linux/amd64 platform requirement for local builds, and the
dockerfile_hygiene probe rules (>=2 FROM stages, no dev-dep installs in
the runtime stage).

Expand the header comment on showcase/scripts/generate-starters.ts to
document the >=40% size target, the getAgentBuildSteps/getAgentBuildCopy
handoff in more detail, and the linux/amd64 platform pin in the deploy
workflow.
2026-04-21 18:41:48 -07:00
Jordan Ritter 6cfcde61c1 chore(showcase/scripts): re-ratchet validate-pins fail-baseline post-regen 2026-04-21 18:14:04 -07:00
Jordan Ritter 59a09e3e65 feat(showcase/scripts/generate-starters): encode prod-mode conditionals per framework
Add per-slug Dockerfile emitters that pair with the new
AGENT_BUILD_STEPS / AGENT_BUILD_COPY tokens in Dockerfile.typescript:

- getAgentBuildSteps(fw): runs in the builder stage, after `npm run build`.
  Emits `npx tsc` for claude-sdk-typescript (compiles agent/index.ts →
  /app/dist/agent/index.js with flags that match the sibling package
  Dockerfile), and `npx mastra build --dir src/mastra` for mastra
  (bundles the server into .mastra/output/index.mjs). Returns "" for
  every other slug so their Dockerfile cache stays unchanged.

- getAgentBuildCopy(fw): runs in the runner stage, after the agent-code
  COPY. Moves /app/dist (claude-sdk-ts) or /app/.mastra (mastra) from
  the frontend stage into the runner.

- getEntrypointBlock() prod-mode updates: mastra now boots via
  `node /app/.mastra/output/index.mjs` (not `npx mastra dev`) and
  claude-sdk-typescript via `node /app/dist/agent/index.js` (not
  `npx tsx agent/index.ts`). Cold start is a straight `node`
  invocation on Railway — mirrors PR #4132's fix for langgraph-ts.

- Wire AGENT_BUILD_STEPS / AGENT_BUILD_COPY into the `vars` map in
  generateStarterImpl so the template substitution picks up the new
  tokens, and export the two helpers so the test suite can guard them.

Also refresh the generator header comment to describe the multi-stage
shape (builder toolchain vs. minimal runtime) and the prod-mode emitter
wiring.

Tests:
- Replace the legacy `mastra dev` / `npx tsx` entrypoint expectations
  with prod-mode assertions (`node /app/.mastra/output/index.mjs`,
  `node /app/dist/agent/index.js`), plus not-to-contain guards so a
  future refactor can't accidentally re-enable the tsx/dev path.
- Add a dedicated describe block for getAgentBuildSteps /
  getAgentBuildCopy covering the two opted-in slots, the ""
  fallthrough for langgraph-typescript (which has its own server.mjs
  migration path), and the "" fallthrough for every Python slug
  (Python prod-mode is shared-template, not per-slug).

Full `vitest run` in showcase/scripts is green (1085 tests).
Starter Dockerfile regeneration is deferred to a follow-up commit
block once Task 1's template wiring lands.
2026-04-21 17:20:50 -07:00
Jordan Ritter 53273b96cd fix(showcase/scripts): allow generate-search-index to emit stub when scan dirs missing
The all-dirs-missing guard added in c91c7c567 fataled the shell Docker
build, which intentionally does not COPY shell-docs/src/content/. The
shell only needs the static-pages stub so its header search modal has
something to render (links resolve across to docs.showcase.copilotkit.ai).

Downgrade the fatal to a loud warn + emit the 5-entry static-pages stub.
A misconfigured full build is still visible in logs.

Regression from PR #4127 (8e6991cea) on top of c91c7c567 / debfa6600.
2026-04-21 15:39:41 -07:00
Jordan Ritter e2a6cc2bfe Regenerate shell-dojo registry at build and expand CI trigger paths
The dojo app was missing items under the langgraph column because
shell-dojo shipped a stale committed registry.json. The generator
only wrote to shell/, the dojo Dockerfile didn't run the generator
at build, and the CI path filter didn't rebuild the dojo when
manifest files changed.

Fix: emit from generate-registry.ts to shell, shell-dojo, and
shell-docs; add the generator step to shell-dojo's Dockerfile;
expand the deploy workflow's path filter to include packages/**
and shared/**; and refresh the committed registry/demo-content
JSON so files on disk match what the generator produces today.
2026-04-21 13:10:52 -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 8708095d34 fix(showcase): langgraph watchdog regression — startup grace period (#4123)
## Incident

`langgraph-typescript` is in a restart loop on Railway as of **04-20
17:05 UTC** (deployment `58bbebe8-7a94-4f99-b6e4-ffcbb4eb78b9`),
returning 502 to production traffic.

## Root cause

PR #4116 generalized the silent-hang watchdog from `crewai-crews` to
every showcase starter: poll agent health every 30s, kill after 3
consecutive failures (~90s). That's fine for uvicorn/express agents
(responsive in <5s), but `langgraph-cli dev` does a heavy cold-start:

- Studio browser IPC handshake (`createIpcServer`)
- `@langchain/langgraph-api` JIT spawn (`spawnServer`)
- Graph compile

On cold Railway containers this routinely exceeds 90s — the watchdog was
killing the process before `/ok` ever became reachable, producing a
kill-loop every ~90s.

## Phase 1 findings

Verified the health path is correct — not a path bug:

- `@langchain/langgraph-cli@1.1.17/dist/cli/up.mjs:27` uses `/ok` as its
own probe.
- `@langchain/langgraph-api@1.1.17/dist/api/meta.mjs:64` registers
`api.get("/ok", ...)`.
- `langgraph_cli` (Python) serves `/ok` from the same api surface.

The regression is purely a **timing** issue: 90s strike budget is
insufficient for langgraph cold-start on Railway.

## Fix — Option B (startup grace)

Added per-framework `getWatchdogGraceSeconds()` in the generator:

- `langgraph-*` starters: **180s grace**
- All other starters: **0s grace** (matches pre-#4116 behavior)

The grace loop waits up to 180s for the first healthy `/ok` probe before
arming the strike counter:

- First success → fall through immediately and arm the counter.
- 180s elapsed without success → arm the counter anyway (steady-state
watchdog then handles true hangs on the normal 90s schedule).
- Agent dies during grace → exit grace loop, `wait -n` in main shell
handles it.

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

## Per-starter changes

| Starter | Grace | Behavioral change |
|---|---|---|
| langgraph-typescript | 180s | **Fixes restart loop** |
| langgraph-python | 180s | Preemptive |
| langgraph-fastapi | 180s | Preemptive |
| crewai-crews, ag2, agno, claude-sdk-*, google-adk, llamaindex,
langroid, mastra, ms-agent-*, pydantic-ai, spring-ai, strands | 0s |
None (comment-only diff) |

## Files touched

- `showcase/scripts/generate-starters.ts` — new
`getWatchdogGraceSeconds()` + grace block in `getWatchdogBlock()`
- `showcase/starters/*/entrypoint.sh` — regenerated (17 files; grace
block for langgraph-*, comment-only for the other 14)
- `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

## Validation

- `bash -n` passes on all 19 edited `entrypoint.sh` files
- 1079/1079 `showcase-scripts` vitest pass (`pnpm --filter
@copilotkit/showcase-scripts test`)

## References

- Regression source: #4116
- Railway deployment: `58bbebe8-7a94-4f99-b6e4-ffcbb4eb78b9`
- Incident window: 04-20 17:05 UTC — ongoing at PR creation

## Test plan

- [ ] CI green
- [ ] Deploy to Railway — watch first boot log for `[watchdog] Startup
grace: waiting up to 180s...` followed by `[watchdog] Agent healthy
after Ns — arming strike counter`
- [ ] Confirm no restart loop on `langgraph-typescript`
2026-04-21 11:01:19 -07:00
Jordan Ritter 008d4e58e8 chore(showcase): ratchet validate-pins baseline after Dojo pin bump 2026-04-21 10:51:55 -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 7dd930cf32 fix(showcase): add Railway image-ref drift assertion
Regression from the 2026-04-21 incident: 18 production Railway services
were found with malformed image refs of the form
`ghcr.io/copilotkit/showcase-<slug>atest` (missing the `:` before
`latest`, so Docker treats `...atest` as the tag). Root cause was an
out-of-band MCP/manual mutation — no committed code touched those refs,
so the data has been fixed but no source-controlled guardrail exists.

Add a standalone script that queries Railway's GraphQL API for every
service in the CopilotKit Showcase project and asserts each image ref
matches the canonical shape `ghcr.io/copilotkit/<service-name>:latest`.
Wire it into showcase_deploy.yml as a pre-build job so any drift aborts
the workflow before the build matrix fans out.

On violation the script prints the service name, the current image, the
expected shape, and the reason, so the fix is obvious in the run log.
Slack classification in the notify job distinguishes a drift failure
from other pre-build failures.

Verified locally: 41 services pass against current Railway state; the
exported `validateImage` function rejects the exact `...atest`
corruption, mismatched service/image names, missing tags, wrong
registries, wrong tag values, and null sources (9/9 simulated cases).
2026-04-21 10:02:35 -07:00
Jordan Ritter de3db5e72e fix(showcase/scripts): add silent-hang watchdog + unbuffered-stdout scaffolding to generator + template
Generalize the watchdog shape proven in showcase/packages/crewai-crews/entrypoint.sh
(PRs #4114 + #4115) so that every Bucket-B starter gets:
  - PYTHONUNBUFFERED=1 export (harmless for non-Python frameworks)
  - A backgrounded watchdog subshell that polls the agent health endpoint
    every 30s and kills the agent after 3 consecutive failures, letting
    wait -n + container runtime handle the restart through the normal path.
  - python -u on uvicorn / langgraph_cli invocations (Python frameworks).
  - awk ... fflush() log prefixing (replaces the previous sed pipe; keeps $!
    pointing at the real agent process).

Per-framework agent health paths:
  * FastAPI / uvicorn agents                                     -> /health
  * langgraph-python, langgraph-fastapi, langgraph-typescript    -> /ok
  * claude-sdk-typescript, ms-agent-dotnet, spring-ai            -> /health
  * mastra                                                        -> /api

Spring Boot starter also gets a 60s /health startup probe (replacing the
blind sleep 5) because JVM warmup + context refresh can exceed 30s.
2026-04-21 09:20:36 -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 ec8430a939 fix(scripts/bundle-demo-content): multi-file tracking, endLine extension, Linux watch warning, Windows paths
Correctness and portability fixes in the demo-content bundler:

- Track contributor snippets per-file so edits to multiple files in
  one commit all get attributed, not just the last one walked.
- Extend endLine when the same file is seen again rather than
  dropping the earlier slice — previously a later, smaller region
  overwrote a larger one.
- Warn when the watch flag is set on Linux without the recursive
  fs.watch support matrix, so the user sees why nothing is firing
  instead of assuming silent success.
- Normalize path separators for Windows so bundle manifests use
  POSIX paths regardless of the host OS.
2026-04-20 22:20:56 -07:00
Jordan Ritter 1d5131c2d5 fix(showcase/scripts): bundle-demo-content path-traversal guard; generate-registry manifest.slug assertion
- bundle-demo-content: reject highlight: paths that resolve outside
  the package root. The bundle output is committed to the repo and
  consumed by both shells at build time, so a malicious or
  mistake-riddled manifest could otherwise smuggle arbitrary
  filesystem contents (../../secrets, absolute paths) into
  demo-content.json. Resolve relative to pkgRoot and throw on
  escape (finding #19).
- generate-registry: add a runtime guard that manifest.slug is a
  non-empty string before path.join(PACKAGES_DIR, manifest.slug).
  Schema validation upstream already enforces this, but a
  silently-undefined slug fed to path.join yields
  "<packages-dir>/undefined" and would produce an empty docs_links
  without surfacing any error. Fail loudly instead (finding #20).
2026-04-20 19:42:28 -07:00
github-actions[bot] dc587abbcd style: auto-fix formatting 2026-04-21 00:51:40 +00:00
Jordan Ritter c91c7c567d fix(showcase/scripts): generate-search-index warns on missing scan dirs
If shell-docs/src/content/{reference,ag-ui,docs} didn't exist the
script silently produced a tiny index with no warning — operators
only found out by noticing Cmd-K search returning nothing.
Warn per missing directory and exit non-zero if ALL scan roots are
missing (that means we're running outside a prepared tree, e.g.
shell-docs didn't emit into the expected layout).
2026-04-20 17:49:59 -07:00
Jordan Ritter c647a1dc9b fix(showcase/scripts): bundle-demo-content watch-mode tracks failure state
The watch loop logged '[watch] bundle failed' once and then fell
silent — repeat failures looked like success, and recoveries were
invisible (no news = assumed fine). Track the last error in module
scope so we distinguish first-failure from repeat-failure, and log an
explicit 'bundle recovered' note when the next green run clears the
state. Makes dev-mode transitions visible instead of silent.
2026-04-20 17:49:58 -07:00
Jordan Ritter 018be75633 fix(showcase/scripts): loadDocsLinks parse failures surface via allErrors
A malformed docs-links.json was only console.warn'd and treated as
empty — build ran green while the override file silently rotted.
Accept an errors accumulator and push parse failures into it so
main()'s non-zero exit path fires. Missing file and stale-shape
tolerance are unchanged (both are legitimate states).
2026-04-20 17:49:58 -07:00
Jordan Ritter 3326b3c1e1 fix(showcase/scripts): probeOg adds timeout + error logging
Without a timeout on the inner fetch, a hung upstream would stall the
entire docs-probe run indefinitely (Node's fetch has no default
timeout). Add a 10s AbortController. The bare catch also left 'error'
states opaque — log URL + error kind so operators can tell an
abort from DNS from TLS.
2026-04-20 17:49:58 -07:00
Jordan Ritter 5068852a29 fix(showcase/scripts): probeHealth logs error details
The bare catch returned 'unknown' with no trace, making it impossible
to distinguish DNS failures from aborts from TLS errors in the unknown
bucket. Log the URL plus a kind:code pair so spikes of 'unknown' are
actually diagnosable.
2026-04-20 17:49:58 -07:00
Jordan Ritter ef9b856ed6 fix(showcase/scripts): generate-status env-gates mock e2e/smoke/qa + warning banner
mockE2E/mockSmoke/mockQA were called unconditionally from main(), so
production dashboards shipped seeded test data in columns that aren't
wired to real CI yet. Gate each behind GENERATE_STATUS_MOCK_*
env vars (default off → emit null / 'unknown'). Print a prominent
banner to stderr naming which columns are mock whenever any gate is
on, so operators can't discover stub data by surprise.
2026-04-20 17:49:57 -07:00
github-actions[bot] 61c3833110 style: auto-fix formatting 2026-04-21 00:42:20 +00:00
Jordan Ritter 3a3b3e37b4 refactor(showcase/scripts): starter-consistency uses agent-URL factory instead of .source munging
The cross-starter consistency test had two AGENT_URL matchers:

    function re8000() {
      return new RegExp(AGENT_URL_LOCALHOST_8000_RE.source, flags);
    }

    function re8123() {
      return new RegExp(
        AGENT_URL_LOCALHOST_8000_RE.source.replace(/8000\\b/, "8123\\b"),
        flags,
      );
    }

The 8123 variant does regex-source string munging — brittle: the
replacement target `8000\\b` has to stay in lockstep with the
shared regex's literal source, and a future port format change would
snap in a subtle way (the test could keep passing while matching
subtly wrong content, or start failing for reasons unrelated to the
starter it's guarding).

Replace the `.source.replace` hack with an exported factory
`makeAgentUrlLocalhostPortRE(port)` in `generate-starters.ts` and
have both `re8000`/`re8123` use it. The existing
`AGENT_URL_LOCALHOST_8000_RE` constant is preserved (now delegating to
the factory) so any external import stays happy.

Bonus: the factory validates port bounds so a typo can't silently
produce a pattern that never matches.
2026-04-20 17:40:22 -07:00
Jordan Ritter 571519b6ab fix(showcase/scripts): bundle-demo-content collates README regions; skips binary files
Two orthogonal bugs in the demo-content bundler:

1) README regions were silently dropped. `collectDemoFiles` pulls the
   demo-dir root README out into the `readme` field rather than
   appending to `files`, but it STILL wrote any `@region[...]`
   slices it found inside the README into `perFileRegions` under the
   README's bundled path. The region-collation loop at write time
   iterated `fileOrder = files.map(f => f.filename)`, which by
   construction never contains the README path — so those regions
   went nowhere. Walk any `perFileRegions` keys not in fileOrder
   after the main pass, so README regions still land in the public
   map. Preserves the stable file-order rule (README contributions
   come after source files, alphabetical tiebreak).

2) Binary-like files were being passed through
   `fs.readFileSync(abs, "utf-8")` and then stringified into the
   bundle. A PNG or font file typically produces a mess of
   replacement characters in `demo-content.json` — waste at best,
   visible corruption at worst when the Code tab ever tried to
   render them. Extend `SKIP_EXTENSIONS` with the usual binary
   suspects (images, fonts, archives, media, PDFs) so the walker
   drops them at scan time.
2026-04-20 17:40:11 -07:00
Jordan Ritter 91d847be06 fix(showcase/scripts): probe-docs reads shell_docs_path with legacy fallback
The canonical schema in `scripts/generate-registry.ts` + per-package
`docs-links.json` calls the shell path `shell_docs_path`. probe-docs
was still reading ONLY the legacy `shell_docs_url` key, so any
future cleanup of `shared/feature-registry.json` to the canonical
key would silently turn every shell-docs status into 'missing'.

Prefer `shell_docs_path`, fall back to `shell_docs_url` so older
registry snapshots still contribute. Emit a one-shot dev-mode warning
when we see only the legacy key, so a lingering registry stays visible
without spamming CI logs.
2026-04-20 17:39:58 -07:00
Jordan Ritter 6379cbbda0 Merge jpr5/ftg-shell-ops-v2: Fix Cycle 1+2 (smart rigor) 2026-04-20 17:26:32 -07:00
Jordan Ritter c756b6404e fix(showcase/shell-docs): scripts stage uses npm ci when lockfile present
showcase/scripts/ now ships a package-lock.json (committed here for the
first time), so the Docker builder can use 'npm ci' for deterministic
installs instead of 'npm install'. Update the comment to match actual
state and copy the lockfile into the scripts stage.
2026-04-20 17:23:36 -07:00
Jordan Ritter 8d56c2230b chore: apply formatter to integration branch
- Extend .oxfmtrc.json ignore to cover shell-docs/src/content
  (mirrors existing shell/src/content ignore, which was not updated
  when shell-ops-v2 moved MDX content into the new shell-docs package)
- Format showcase/scripts/generate-registry.ts and probe-docs.ts
2026-04-20 14:13:30 -07:00
Jordan Ritter debfa6600d refactor(showcase): update generator scripts to emit into both shell and shell-docs where applicable
MDX docs moved from shell to shell-docs, but several generated artifacts
are still consumed by both shells:
- registry.json: shell uses it (home grid, integrations, matrix,
  middleware, layout); shell-docs uses it (docs renderer framework lookup)
- demo-content.json: shell uses it (integrations/[slug]/[demo]); shell-docs
  uses it (<Snippet> in docs renderer)
- search-index.json: shell-docs consumes it for the docs search modal;
  shell also keeps a copy so its header search still works — links 301
  across to docs.showcase.copilotkit.ai.

Updated scripts:
- generate-registry.ts: dual-emits registry.json to both shells
  (constraints.json stays shell-only — integration-explorer is shell)
- bundle-demo-content.ts: dual-emits demo-content.json
- generate-search-index.ts: scans from shell-docs/src/content (where MDX
  now lives), writes to both shells' data dirs
- probe-docs.ts: scans shell-docs/src/content/docs (content source moved),
  still writes docs-status.json under shell/ for the dashboard
- sync-docs-from-main.ts: target path updated to shell-docs/src/content

Tests in __tests__/ reference shell/src/data paths; dual-emit keeps
those stable so existing afterEach-restore hooks continue to work.
2026-04-20 14:00:28 -07:00
Jordan Ritter daf09358c4 chore(showcase): rename shell-internal → shell-dashboard and provision Railway service @ dashboard.showcase.copilotkit.ai
- Renamed showcase/shell-internal/ → showcase/shell-dashboard/ (git mv for history).
- Updated package name to @copilotkit/showcase-shell-dashboard (+ lockfile).
- Updated external references: showcase/scripts/{probe-docs,generate-status}.ts comments and showcase/README.md.
- Added dispatch + filter + ALL_SERVICES entry for shell-dashboard in .github/workflows/showcase_deploy.yml.
- Provisioned Railway service showcase-shell-dashboard (id 4d5dfd74-be61-40b2-8564-b53b7dd4c15b):
  - image source ghcr.io/copilotkit/showcase-shell-dashboard:latest
  - env vars OPENAI_API_KEY, ANTHROPIC_API_KEY, PORT copied from showcase-shell
  - custom domain dashboard.showcase.copilotkit.ai attached (CNAME → 6u9icvje.up.railway.app)
  - image will be built and pushed via showcase_deploy.yml on merge
2026-04-20 13:54:17 -07:00
Jordan Ritter 82e9769f5c fix(showcase/starters/langroid): restore /api/health 200 by dropping sed log-prefix wrappers and gating Next.js on agent readiness
Production showcase-starter-langroid returns ``{"status":"degraded","agent":"down"}`` HTTP 503
at ``/api/health`` — the exact path the showcase-deploy workflow's ``ALL_SERVICES.health_path``
verify step asserts, so the next CI redeploy of this starter would fail verification.

Root cause: two compounding bugs in ``showcase/starters/langroid/entrypoint.sh``.

1. Process-substitution log prefixers suppressed stdout. Both backgrounded
   services were wrapped with ``> >(sed 's/^/[agent] /') 2>&1 &`` /
   ``> >(sed 's/^/[nextjs] /') 2>&1 &``. In Railway's V2 runtime this shape
   reliably produced ZERO ``[agent]``/``[nextjs]`` log lines (confirmed against
   several weeks of Railway logs for this service) and correlated with
   agent-unreachable 503s at ``/api/health``. The package entrypoint
   (``showcase/packages/langroid/entrypoint.sh``) uses the plain-``&`` pattern
   with no wrapper, stays green on the same Railway runtime, and shows full
   uvicorn ``INFO:`` startup logs including ``127.0.0.1:...  "GET /health
   HTTP/1.1" 200 OK`` from Next.js's fetch. Match the working package pattern.

2. Cold-start race amplified by ``sleepApplication=true``. Railway sleeps
   idle services. On wake, Next.js is ready in <1s but Python + langroid
   imports take 10-20s. Without a readiness gate, Next.js answered the first
   post-wake ``/api/health`` probe with ``agent:"down"`` before uvicorn had
   bound port 8123. The deploy workflow's verify step then saw the 503 and
   reported the deploy as unhealthy. Add a 30s readiness probe that curls
   ``http://127.0.0.1:8123/health`` in a loop before starting Next.js. The
   IPv4 literal (``127.0.0.1`` not ``localhost``) is load-bearing — Node
   22+'s fetch resolves ``localhost`` to IPv6 ``::1`` first, and uvicorn
   binds IPv4 only; the readiness probe must not false-negative on resolver
   semantics alone.

Also export ``PYTHONUNBUFFERED=1`` so Python import-time tracebacks (e.g.
langroid module-load failures) reach the container log immediately instead
of sitting in userspace buffers until process exit closes them off.

Verification:
- Built langroid starter image locally from this branch. ``/api/health``
  returns 200 with ``{"status":"ok","agent":"ok"}``. Full uvicorn INFO
  logs visible including ``127.0.0.1:xxxxx - "GET /health HTTP/1.1"
  200 OK`` confirming Next.js -> agent path works.
- Readiness probe fires before Next.js launch: ``[entrypoint] Agent
  /health ready after 3s``.
- New regression guards in ``starter-consistency.test.ts`` cover all four
  invariants (no sed wrapping around uvicorn, no sed wrapping around next
  start, ``PYTHONUNBUFFERED=1`` export, readiness probe on 127.0.0.1:8123
  before ``next start``). Verified red→green (reverted entrypoint -> 4
  fails; restored -> 4 pass). Full showcase/scripts suite: 1079/1079 pass.
2026-04-20 13:53:08 -07:00
Atai Barkai a12579167e fix(showcase/scripts): validate-parity skips command-only demos in missing-dir check
The auditableDemos filter (introduced for cli-start's informational
`command:` entry) was applied to the spec/qa SHOULD checks but not
the missing-demo-dir MUST check. Result: cli-start in langgraph-python's
manifest triggered a false missing-dir failure even though it has no
on-disk folder by design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:37:00 -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 2c10faef6b refactor(showcase): rename hitl-in-app to "(Frontend Tools + async HITL)"
Clarify the positioning of the In-App Human in the Loop cell. The old
label was ambiguous with the in-chat variant; the new label surfaces
the core technical story at-a-glance: this is the async useFrontendTool
pattern where the approval UI pops up OUTSIDE the chat surface.

Also register the cell in the langgraph-python manifest (features list
+ demos entry pointing to the new files).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:58:33 -07:00
Atai Barkai 7cb19385b6 chore(showcase): delete declarative-gen-ui-hardcoded testing cell
The "Declarative Generative UI (A2UI — Custom Catalog)" single-file
prototype variant was a testing-only companion to the canonical
declarative-gen-ui 3-file BYOC pattern. Drop it everywhere:

- Delete src/app/demos/declarative-gen-ui-hardcoded/ cell directory
- Delete src/agents/a2ui_dynamic_hardcoded.py
- Remove from langgraph-python manifest.yaml (features + demos) and
  langgraph.json graphs
- Remove from docs-links.json, route.ts (agent entry + a2ui agents
  list), shared/feature-registry.json, shared/constraints.yaml
- Drop the Callout promoting it from a2ui/dynamic-schema.mdx
- Bump expected langgraph-python count 32 → 31 in generate-registry
  test; regenerate all bundles

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:33:43 -07:00
Atai Barkai 1b9b7ab0e6 chore(showcase): remove redundant prebuilt-chat row
Pre-Built: Chat was redundant with Pre-Built: CopilotChat — both rows
referred to the same CopilotChat surface with slightly different
framing. Drop prebuilt-chat everywhere:

- Delete src/app/demos/prebuilt-chat/ cell directory
- Remove from langgraph-python manifest.yaml features + demos
- Remove from shared/feature-registry.json features + chat-ui allowlist
- Remove from shared/constraints.yaml constrained-explicit
- Remove from src/app/api/copilotkit/route.ts neutral-fallthrough list
- Update expected counts in bundle-demo-content + generate-registry
  tests (langgraph now exposes 32 features, down from 33)
- Fix stale src/agents/main.py test expectation to
  src/agents/agentic_chat.py (main.py was split into a neutral assistant
  by an earlier Phase-1 fix commit)
- Regenerate shell/src/data/{registry,constraints,demo-content,
  docs-status}.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:22:51 -07:00
Atai Barkai dc9811b1b7 feat(showcase/scripts): port probe-docs.ts for live docs-status checks
Ports probe-docs.ts from 4084. For each entry in shared/feature-registry.json
it HEADs the feature's og_docs_url and checks whether a matching .mdx exists
under shell/src/content/docs/<shell_docs_url>, then writes the results to
shell/src/data/docs-status.json. shell-internal's DocsRow falls back to this
probed state for (integration, feature) cells that don't have a per-column
docs-links.json override. Exposes the script as `probe-docs` in
showcase/scripts/package.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:10:06 -07:00
Atai Barkai 0df1d23cf4 feat(showcase/scripts): port loadDocsLinks() into generate-registry
Ports the loadDocsLinks() helper from 4084 into generate-registry.ts. After
schema validation, each integration gets a docs_links field merged in from its
sibling packages/<slug>/docs-links.json (best-effort: missing file or stale
shell_docs_url shape is tolerated). The shell-internal DocsRow reads this via
integration.docs_links.features[<id>] to prefer curated per-column overrides
over the per-feature defaults.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:09:55 -07:00
Atai Barkai ff6dd2eed5 chore(showcase): sync feature-registry + constraints with 4084
- Add prebuilt-chat feature to the registry and constrained-explicit
  allowlist so the 4085 manifest's intentional prebuilt-chat wiring
  validates cleanly (4084 omits it; 4085 committed the cell earlier)
- Bump expected langgraph-python feature count in generate-registry
  test from 32 to 33 to reflect the new row

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:01:48 -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
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
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
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
github-actions[bot] e45d86b8f3 style: auto-fix formatting 2026-04-19 08:57:44 -07:00