Commit Graph

6858 Commits

Author SHA1 Message Date
Ben Taylor ae20d6b239 feat(runtime): remove intelligence organization id config (#3537)
<!--
Thank you for sending the PR! We appreciate you spending the time to
work on these changes.

Help us understand your motivation by explaining why you decided to make
this change.


**Please PLEASE reach out to us first before starting any significant
work on new or existing features.**

By the time you've gotten here, you're looking at creating a pull
request so hopefully we're not too late.

We love community contributions! That said, we want to make sure we're
all on the same page before you start.
Investing a lot of time and effort just to find out it doesn't align
with the upstream project feels awful, and we don't want that to happen.
It also helps to make sure the work you're planning isn't already in
progress.

As described in our contributing guide, please file an issue first:
https://github.com/ag-ui-protocol/ag-ui/issues
Or, reach out to us on Discord: https://discord.com/invite/6dffbvGU3D


You can learn more about contributing to copilotkit here:
https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md

Happy contributing!

-->

## What does this PR do?

(Describe the changes introduced in this PR)

## Related PRs and Issues

- (Direct link to related PR or issue, if relevant)

## Checklist

- [ ] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
2026-04-22 09:41:34 -05:00
Mike Ryan 4369252a1e chore(format): run prettier on runtime test 2026-04-22 07:24:51 -07:00
Mike Ryan 85ea1969e5 feat(runtime): remove intelligence organization id config 2026-04-22 07:24:51 -07:00
Alem Tuzlak 00a908aa47 fix: ensure TextMessageOutput.id is never null by falling back to randomId() (#2118) (#3814)
Fixes #2118

Red-green tested locally.
2026-04-22 14:55:31 +02:00
Alem Tuzlak 605d039469 fix: support both old and new crewai import paths (#3791)
## Summary
- Fixes #3268
- `crewai>=0.177.0` moved `flow_events` from
`crewai.utilities.events.flow_events` to
`crewai.events.types.flow_events`
- Added try/except fallback to support both import paths
- Backward compatible with older crewai versions

## Test plan
- [x] Added `test_crewai_import_compat.py` testing old path, new path
fallback, and both-fail error propagation
- [x] All tests pass
2026-04-22 14:53:33 +02:00
Alem Tuzlak b17726d168 Merge branch 'main' into fix/crewai-import-compat-3268 2026-04-22 14:46:06 +02:00
Alem Tuzlak 90941ed08e Merge branch 'main' into fix/issue-2118 2026-04-22 14:43:39 +02:00
Alem Tuzlak b305f365cb fix: honor Retry-After header on 429 responses (#3842)
Closes #3637

Parse Retry-After as seconds or HTTP-date and use it as the retry delay
instead of exponential backoff. Throw if the server requests a wait
longer than `maxRetryAfterSeconds` (60s default) to avoid hanging.

Split from #3838.
2026-04-22 14:43:29 +02:00
Alem Tuzlak 35a68aea90 Merge remote-tracking branch 'origin/main' into fix/crewai-import-compat-3268
# Conflicts:
#	sdk-python/pyproject.toml
2026-04-22 14:36:35 +02:00
Alem Tuzlak c92dde419b fix(showcase): complete open-gen-ui scrub + fix smoke-test filter regression (#4029)
## Summary
Prior PR removed the open-gen-ui feature but left several loose ends.
This PR completes the scrub:

1. **Source YAML** — removed `open` profile + `open-gen-ui` entries from
`showcase/shared/constraints.yaml` (was missed before; would have
re-introduced `open-gen-ui` on next generator run)
2. **Schema enum** — dropped `"open"` from `generative_ui` enum in
`showcase/shared/manifest.schema.json`
3. **Test fixture** — `invalid-genui-manifest.yaml` now uses
`[unknown-profile]` instead of `[open]`; tests still pass (validator
rejects unknown profiles)
4. **Manifest descriptions** — all 17
`showcase/packages/*/manifest.yaml` files: "5 GenUI rendering
strategies" → "4" (open-gen-ui was the 5th; now removed)
5. **Derived JSON regen** — `registry.json` regenerated cleanly via
`generate-registry.ts`
6. **Smoke-test filter fix** — `integration-smoke.spec.ts:383` now reads
top-level `i.deployed` instead of stale `i.starter?.deployed`. The old
filter returned 0 starters post-regen (silent CI skip every 6h); new
filter correctly gates on canonical top-level field.

## Context
- `showcase/shell/src/data/demo-content.json` is also regenerated but
NOT committed because it exceeds the lefthook 1MB binary-size cap
(pre-existing repo condition, separate from this PR).
- 3 starters (mastra, crewai-crews, claude-sdk-typescript) that had
manually-patched `starter.deployed: false` are now covered by smoke —
verified all 3 starter URLs respond HTTP 200 live.

## Test plan
- [ ] CI green
- [ ] Post-merge `starter-smoke` workflow picks up all 17 starters (not
zero, as was the silent broken state)
- [ ] No `open-gen-ui` references remain anywhere in showcase/
showcase-previews
2026-04-22 14:34:14 +02:00
Alem Tuzlak d8d6b71490 feat: switch preview captures from GIF to MP4 with GitHub Release storage (#3752)
## Summary

- **CI workflow consolidation**: Merged commitlint into static_quality,
added concurrency groups to test workflows, added path filters to
publish-commit, fixed static_danger matrix bug and python-sdk
concurrency
- **Showcase deploy refactor**: Replaced 23 duplicate jobs with a
dynamic matrix strategy (~1000 lines removed)
- **Preview capture → MP4**: Switched from GIF to optimized MP4 (H.264,
CRF 28, faststart), uploads to GitHub Release `showcase-previews`
instead of committing blobs to the repo. DemoCard uses `<video autoplay
muted loop>` for hover previews.

## Test plan

- [x] Local MP4 capture test passed (langgraph-python, 14KB, 8s, H.264
400x300 @ 10fps)
- [ ] CI workflows pass on this branch
- [ ] Capture workflow produces and uploads MP4 to release
- [ ] DemoCard hover shows video preview when URL is populated

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-22 14:34:01 +02:00
Alem Tuzlak a337b5f63d Merge branch 'main' into fix/issue-2118 2026-04-22 14:29:11 +02:00
Alem Tuzlak ec905205f9 Merge branch 'main' into fix/issue-3637 2026-04-22 14:27:14 +02:00
Alem Tuzlak b095cf0443 fix: add remarkPlugins/rehypePlugins passthrough support (#2296) (#3822)
## Summary

Fixes #2296

The Markdown component had hardcoded remark/rehype plugins with no way
for consumers to add their own. This adds optional `remarkPlugins` and
`rehypePlugins` props that are spread into the existing plugin arrays,
enabling custom markdown processing.

## Test plan

- [ ] Verify custom remarkPlugins are applied alongside built-in plugins
- [ ] Verify custom rehypePlugins are applied alongside built-in plugins
- [ ] Verify Markdown renders correctly without custom plugins (no
regression)
2026-04-22 14:26:17 +02:00
Alem Tuzlak 2b88450b44 fix: prevent 'Unknown provider' regression for LangChainAdapter (#3840)
Closes #3217

When `getLanguageModel()` returns null and provider/model are undefined
(as with LangChainAdapter), the code constructed `"undefined/undefined"`
as a model string, causing a cryptic "Unknown provider" error.

Now checks each source of model info explicitly and throws a clear error
message directing users to provide an explicit agents config when using
adapters that don't expose model metadata.

Split from #3838.
2026-04-22 14:23:49 +02:00
Alem Tuzlak d02c5eed6a fix: allow Python 3.13+ in SDK version constraint (#3834)
## Summary
- Widens the python dependency from `>=3.10,<3.13` to `>=3.10,<4`
- Adds Python 3.13 classifier to enable installation on newer Python
versions

Closes #3156
2026-04-22 14:23:04 +02:00
Alem Tuzlak 087255de76 fix: handle null-union types in convertJsonSchemaToParameter (#3825)
## Summary

- `convertJsonSchemaToParameter` now handles null-union types
(`["string", "null"]`) by filtering out `"null"` and recursing with the
resolved scalar type

Closes #2651

## Test plan

- [x] `@copilotkit/shared` tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-22 14:22:28 +02:00
Alem Tuzlak d9c49fbeba fix: add py.typed marker to Python SDK for PEP 561 compliance (#3828)
## Summary

- Added `py.typed` marker file to the Python SDK for PEP 561 compliance,
enabling type checker support

Closes #2921

## Test plan

- [x] Python SDK builds successfully with marker included

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-22 13:36:20 +02:00
github-actions[bot] e713ad2a07 style: auto-fix formatting 2026-04-22 11:20:02 +00:00
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 9ca2fc6d9d fix(ci): 4 hygiene fixes from QA/E2E blitz findings (#4024)
## CI hygiene fixes

Four direct-fix items surfaced during the 2026-04-16 QA/E2E blitz,
bundled as one PR with one commit per fix:

1. **`test_unit.yml` paths-ignore** — add `showcase/**` +
`sdk-python/**` (prevents spurious TS unit matrix runs on showcase-only
or sdk-python-only PRs). `sdk-python-old/**` was mentioned in the plan
but doesn't exist on main; only the two existing dirs are added.
2. **`test_doc-examples.yml`** — scope PR trigger to `branches: [main]`
(matches convention of other workflows).
3. **`e2e_dojo.yml`** — symmetric `.changeset` path filter on push+PR
(was asymmetric — already in the `dorny/paths-filter` step's `ts:` list,
so this aligns the trigger).
4. **`starter-smoke.yml`** — rename internal job id (`starter-smoke` →
`smoke-starter`) and artifact name pattern (cosmetic; matches the new
`test_<layer>-<target>` / `smoke-<layer>` naming convention; no external
consumers).

### Fix skipped

**`showcase_smoke-monitor.yml` slug normalization** (item #10 in the
Notion page) — inspecting the file, it already uses the
`showcase/packages/` slug convention (`ms-agent-python`,
`ms-agent-dotnet`, `strands`). The apparent inconsistency is actually in
`starter-smoke.yml`, whose matrix keys must stay as
`ms-agent-framework-*` / `strands-python` because they are literal
directory names in `examples/integrations/` (used as `working-directory:
examples/integrations/${{ matrix.starter }}`). So there is no actionable
change here — the two files use different slug conventions *by
necessity*, because they target different directories (deployed
`showcase/packages/*` vs. local `examples/integrations/*`). Flagging for
the author of the blitz notes in case the actual concern was something
else.

Refs: [Bugs Found During
Blitz](https://www.notion.so/3443aa381852812fb595c5118dd68818) items #3,
#8, #9, #12.
2026-04-22 13:08:40 +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
Alem Tuzlak 3f6c80d8a0 Merge branch 'main' into fix/issue-2921 2026-04-22 12:59:18 +02:00
Alem Tuzlak 24b5e80bcc Merge remote-tracking branch 'origin/main' into fix/issue-2118 2026-04-22 12:59:14 +02:00
Alem Tuzlak f9d01dfd3b refactor(runtime): extract resolveMessageId helper and cover it with tests
Addresses PR feedback on #3814: replaces the "red-green tested locally"
rationale with actual unit coverage.

The inline `event.messageId || randomId()` fallback becomes an exported
`resolveMessageId` helper. The resolver now calls it; the helper is
covered by a small test suite asserting: provided ids pass through
verbatim; null / undefined / empty-string event ids fall back to the
"ck-<uuid>" shape; successive fallbacks produce distinct ids.
2026-04-22 12:59:06 +02:00
Alem Tuzlak ae1219455a Merge branch 'main' into fix/crewai-import-compat-3268 2026-04-22 12:58:24 +02:00
Alem Tuzlak 2a800f99a7 Merge branch 'main' into fix/issue-3156 2026-04-22 12:57:58 +02:00
Alem Tuzlak 77dd2abc29 Merge remote-tracking branch 'origin/main' into fix/remark-rehype-plugins-2296
# Conflicts:
#	packages/react-ui/src/components/chat/Markdown.tsx
2026-04-22 12:53:35 +02:00
Alem Tuzlak 38e95491e7 refactor(react-ui/Markdown): pass through all react-markdown Options via spread
Addresses PR feedback on #3822.

Instead of plumbing remarkPlugins and rehypePlugins through the component
one prop at a time, MarkdownProps now extends Omit<Options, "children">
and spreads the remainder into the underlying ReactMarkdown. Consumers
can now pass any react-markdown Options field (urlTransform, allowedElements,
skipHtml, etc.) without further plumbing.

The three fields that need library-specific defaults (components,
remarkPlugins, rehypePlugins) are still destructured, merged, and memoized
internally so the built-in behavior is preserved and merged values keep
stable references across renders.

Dropping the custom memo comparator — now that arbitrary props flow
through, a fixed 4-key check would silently skip re-renders when
consumer-provided props change. React's default shallow-compare, combined
with the internal useMemo on merged values, gives correct behavior without
regressing the memoization for the common case.
2026-04-22 12:50:09 +02:00
Alem Tuzlak acb30f23cc Merge branch 'main' into fix/issue-2651 2026-04-22 12:42:19 +02:00
Alem Tuzlak db38f50918 test(runtime): cover handleServiceAdapter model-source resolution (#3217)
Covers the three branches added by the fix:
- getLanguageModel() returns a LanguageModel -> wired into BuiltInAgent
- provider + model strings present -> composed as "provider/model"
- neither present (e.g. LangChainAdapter) -> throws CopilotKitMisuseError
  with adapter name in the message, instead of silently producing
  "undefined/undefined" and failing downstream.

Also guards the partial-info case where only one of provider/model is set
to ensure the regression doesn't re-emerge via a half-populated adapter.
2026-04-22 12:39:31 +02:00
Alem Tuzlak b8349d2067 fix(sdk-python): copilotkit_interrupt handles non-list resume values (#3096) (#3784)
## Summary
- `copilotkit_interrupt` now handles string and dict resume values from
LangGraph 1.x's `interrupt()`
- Previously crashed with `AttributeError: 'str' object has no attribute
'content'` or `KeyError: -1`
- Type-checks response: str returned directly, dict JSON-serialized,
list uses existing `[-1].content` path

## Test plan
- [x] Red-green test: string resume value returns without crash
- [x] Red-green test: dict resume value returns JSON string
- [x] Test: list resume value still works (existing behavior)
- [x] Full test suite passes (15/15)

Closes #3096
2026-04-22 12:33:27 +02:00
Alem Tuzlak d6ff5772ed fix(sdk-python): LangGraphAGUIAgent serializes Context objects to dicts (#3690) (#3787)
## Summary
- `LangGraphAGUIAgent.langgraph_default_merge_state` now calls
`model_dump()` on Pydantic Context objects before storing in copilotkit
state
- Previously stored raw Pydantic objects, causing JSON serialization
failures downstream
- Handles mixed types: Pydantic objects get `model_dump()`, plain dicts
pass through unchanged
- Matches the existing pattern already used in `CopilotKitMiddleware`

## Test plan
- [x] Red-green test: AG-UI Context objects stored as plain dicts, not
Pydantic
- [x] Red-green test: mixed Pydantic + dict context items all
serializable
- [x] Full test suite passes (15/15)

Closes #3690
2026-04-22 12:33:14 +02:00
Alem Tuzlak 95fe4889b6 Merge remote-tracking branch 'origin/main' into fix/crewai-import-compat-3268
# Conflicts:
#	sdk-python/pyproject.toml
2026-04-22 12:26:49 +02:00
Alem Tuzlak 51b0513eab Merge remote-tracking branch 'origin/main' into fix/issue-3156
# Conflicts:
#	sdk-python/pyproject.toml
2026-04-22 12:13:45 +02:00
Alem Tuzlak e71b27ed97 Merge remote-tracking branch 'origin/main' into fix/issue-2651
# Conflicts:
#	packages/shared/src/utils/json-schema.ts
2026-04-22 12:02:32 +02:00
github-actions[bot] 15c6e027b9 style: auto-fix formatting 2026-04-22 09:58:37 +00:00
Alem Tuzlak d7c7b52620 test(runtime): cover Retry-After parsing and 429 fetch path (#3637)
Adds parseRetryAfter unit tests (header absent, integer seconds, zero,
negative, future/past HTTP-date, unparseable) and fetchWithRetry 429
branch tests (honored within limit, rejected when exceeding
maxRetryAfterSeconds, fallback to exponential backoff when missing,
ignored on non-429 retryable statuses).
2026-04-22 11:56:53 +02:00
Alem Tuzlak b28d15339c feat(react-core): add pin-to-send scroll mode to CopilotChat v2 (#4142)
## What does this PR do?

Adds a new `"pin-to-send"` value to `CopilotChatView`'s `autoScroll`
prop, matching ChatGPT's scroll behavior: when the user sends a message,
that message scrolls to the top of the viewport and stays there while
the assistant's response streams in below. The user reads at their own
pace — the viewport does not chase the bottom.

### New public API

```ts
autoScroll?: "pin-to-bottom" | "pin-to-send" | "none" | boolean
```

- `"pin-to-bottom"` — current behavior. Chases the bottom as content
streams (still the default).
- `"pin-to-send"` — **new.** Scrolls the latest user message to ~16px
from the top, maintains a dynamic bottom spacer so the message can
actually reach the top, and does not chase the bottom.
- `"none"` — no auto-scroll.
- Boolean back-compat: `true` → `"pin-to-bottom"`, `false` → `"none"`.
Existing consumers unchanged.

`AutoScrollMode` type is exported from the package barrel.

### Example usage

```tsx
<CopilotChat autoScroll="pin-to-send" />
```

### How it works

1. `CopilotChat` computes the latest user message ID from `messages` and
publishes `{ id, sendNonce }` via a new `LastUserMessageContext`. The
nonce increments on each new send so message edits also retrigger.
2. A new `usePinToSend` hook reads the context and, on each new send:
measures viewport + user message heights, sets a spacer `<div>`'s
height, then does a single smooth `scrollTo` to the user message's
offset.
3. A shrink-only `ResizeObserver` on content collapses the spacer as the
assistant's response fills space below, so there's no wasted empty
space.
4. `ScrollView` branches on the normalized mode: existing
`<StickToBottom>` path for `"pin-to-bottom"`, existing plain-div path
for `"none"`, new `PinToSendScrollContainer` for `"pin-to-send"`.

### Edge cases handled

- Thread restore with existing messages doesn't trigger a spurious
scroll.
- `scrollTo` target computed via `getBoundingClientRect` arithmetic —
robust across any CSS positioning.
- Virtualization: the spacer lives outside the virtualized list.
- Back-compat: `autoScroll={true|false}` produces byte-for-byte
identical DOM to pre-change.

### Tests

- 7 unit tests for `normalizeAutoScroll`.
- 4 TDD behavior tests for `usePinToSend` (spacer sizing, scroll target,
shrink-only ResizeObserver, cleanup).
- 5 integration tests for `CopilotChatView` covering all three modes +
boolean back-compat.
- Full `@copilotkit/react-core` suite: **85 files / 1120 tests pass,
zero regressions.**
- publint + attw green across all 13 packages.
- Existing `CopilotChatView.slots.e2e.test.tsx` (43 tests) unchanged.

## Related PRs and Issues

- (none — new feature)

## Checklist

- [x] I have read the Contribution Guide
- [ ] Docs site update to follow in a separate PR

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-22 10:54:35 +02:00
Jordan Ritter 307172e9d1 fix(showcase/langgraph-typescript): bind liveness probe before heavy module imports (#4148)
## Summary
- Split `server.mjs` into `liveness.mjs` (new entry) + `server.mjs`
(actual langgraph-api server).
- `liveness.mjs` binds `/ok` on :8124 first, then dynamic-imports
`server.mjs` to kick off the real langgraph bootstrap.
- `src/agent/package.json` `start` script now runs `node --import tsx
liveness.mjs` as the entry.

## Why
#4140 put the probe inside `server.mjs` under the belief it would bind
"synchronously before any await." ES module semantics say otherwise —
all top-level `import` declarations resolve before any module-body code
runs. Cold start of `@langchain/langgraph-api` + graph transpile takes
~4m30s on Railway, but watchdog grace (180s) + strikes (90s) = 270s
kills before the probe ever opens. Dynamic import defers the heavy load
until after liveness is already serving 200.

## Test plan
- [x] Local Docker build green (amd64)
- [x] Container serves /ok within <2s of launch (measured: 1.4s)
- [ ] Railway deploy green + smoke passes sustained
2026-04-21 22:24:16 -07: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 e35071106c chore(ci): add interim keep-alive cron for showcase starters (#4126)
## Why

Adds an interim GitHub Actions cron that curls `/api/health` on each of
the 17 showcase starter services every 5 minutes. The goal is to keep
the agent's in-memory state warm — JIT tiers, module cache, connection
pools. Railway Pro tier doesn't sleep containers, but warm-state still
decays over idle periods.

This is a stopgap until the `showcase-ops` service (see Notion proposal
§2a) lands. Its smoke probe runs at the same cadence and will subsume
this workflow entirely.

## What

New file: `.github/workflows/showcase_keep-alive.yml`

- Schedule: `*/5 * * * *` (every 5 minutes) + manual `workflow_dispatch`
- Matrix over all 17 starter slugs (`ag2`, `agno`, `claude-sdk-python`,
`claude-sdk-typescript`, `crewai-crews`, `google-adk`,
`langgraph-fastapi`, `langgraph-python`, `langgraph-typescript`,
`langroid`, `llamaindex`, `mastra`, `ms-agent-dotnet`,
`ms-agent-python`, `pydantic-ai`, `spring-ai`, `strands`)
- Each cell: `curl -fsS --max-time 15
https://showcase-<slug>-production.up.railway.app/api/health`
- `fail-fast: false` + `continue-on-error: true` so one starter being
down doesn't cancel the others
- No Slack notification — `showcase_smoke-monitor.yml` already covers
alerting. This workflow is strictly keep-alive, not observability.
- Header comment marks it INTERIM and references the showcase-ops
cutover

## Interim marker

Remove this workflow once `showcase-ops` ships its smoke probe at the
same 5-min cadence (tracked in the Notion showcase-ops proposal).

## Test plan

- [ ] Verify workflow parses in the Actions UI after merge
- [ ] Trigger `workflow_dispatch` once manually; confirm all 17 cells
run and pass for healthy starters
- [ ] Confirm no Slack noise generated by this workflow
- [ ] Remove when showcase-ops cutover lands
2026-04-21 20:10:43 -07:00
Jordan Ritter 8a39783e58 fix(showcase/langgraph-typescript): restore watchdog health probe after prod-mode migration (#4140)
## Summary

- Root cause: #4132 migrated langgraph-typescript from `langgraph-cli
dev` (which exposed `/ok` immediately) to `@langchain/langgraph-api`
`startServer`. That server DOES register `/ok` via its meta routes, but
only binds the port AFTER `registerFromEnv` imports the graph and spawns
the N workers. On Railway that window has been exceeding the watchdog's
180s startup grace + 90s strike budget, producing an infinite restart
loop (deploy `ddc88ef9` is flapping ~every 4m30s).
- Fix: move liveness off the heavy Hono path entirely. `server.mjs` now
starts a bare `node:http` server on port 8124 (configurable via
`HEALTH_PORT`) synchronously before any `await`, answering `GET /ok`
with `{"status":"ok"}`. `entrypoint.sh` watchdog now probes
`127.0.0.1:8124/ok` instead of `:8123/ok`.

## Why

The service has been 502-ing all day. Railway deploy `ddc88ef9` never
serves a single `/api/health` 200 despite `[server] LangGraph API
listening on 0.0.0.0:8123` landing in the logs — the `listening`
callback fires after the watchdog has already killed PID 4 and triggered
a restart.

Liveness on a sibling port is independent of whether :8123 has finished
its 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 hang-detection is preserved.

## Test plan

- [x] Local Docker build green (`--platform linux/amd64`, depot)
- [x] Local container `curl http://127.0.0.1:8124/ok` returns `HTTP=200
{"status":"ok"}` within 20s of boot
- [x] `[server] Liveness probe listening on 0.0.0.0:8124` emitted before
`[server] LangGraph API listening on 0.0.0.0:8123`
- [ ] Railway redeploy recovers `/api/health` + `/api/smoke`
2026-04-21 20:10:40 -07:00
Jordan Ritter c71641db2e chore(showcase/scripts): revert validate-pins baseline hash to CI-observed value 2026-04-21 20:10:21 -07:00
github-actions[bot] 24d0ed442c style: auto-fix formatting 2026-04-22 02:49:13 +00: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 00a3fc50bf docs(vscode-extension): correct RELEASING.md and move one-time setup to appendix (#4146)
## Summary

- Correct RELEASING.md prose to reflect the metadata-sync automation:
contributors only edit CHANGELOG.md; the `VS Code Extension — Metadata
Sync` workflow auto-bumps `package.json.version`/`.description` and
commits `chore: release vX.Y.Z` on the PR branch. The old copy still
said maintainers hand-edit `package.json`.
- Fix the Rollback section step 2 to match the new flow (CHANGELOG-only,
sync workflow handles `package.json`).
- Move all one-time bootstrap setup (GH environment variables/secrets +
Open VSX namespace claim) to a `One-time setup (historical reference)`
appendix at the bottom of the doc, under a horizontal rule. The front of
the doc now flows maintainer-first: Cutting a release → CI publish flow
→ Transient failures → Verifying → Rollback → Rollback guidance → Notes.
One-time setup was completed during 2026-04-21 pipeline bootstrap and
shouldn't need redoing; keeping it for disaster recovery only.

## Why

Two readers in a row tripped on the "hand-edit package.json" wording
because the automation now does that for them. And the one-time setup
was the first thing a maintainer saw on every release, even though
they'd never need to run any of it again.

## Test plan

- [x] `oxfmt --write` clean on RELEASING.md (no re-format on second
run).
- [x] H2 order matches the maintainer-first flow: Cutting a release, CI
publish flow, Transient registry failures, Verifying, Rollback, Rollback
guidance, Notes and future work, One-time setup (historical reference).
- [x] No content edits inside the moved sections — only relocation + a
one-line historical-reference intro above them.
2026-04-21 19:32:04 -07:00
Jordan Ritter fbdaa75d65 docs(vscode-extension): correct RELEASING.md to reflect metadata-sync automation
The aimock-parity paragraph and the Rollback section still described
contributors hand-editing both package.json and CHANGELOG.md. That was
stale — the vscode-extension-changelog-sync.yml workflow auto-syncs
package.json.version (and optionally package.json.description) from
the CHANGELOG entry (and README first paragraph) when the PR is opened
and commits 'chore: release vX.Y.Z' on the PR branch.

Rewrite both passages so the documented flow matches reality: contributor
edits CHANGELOG.md only (optionally README.md), sync workflow handles the
package.json bump automatically, publish workflow self-gates via vsce show
and ships on merge to main.
2026-04-21 19:27:11 -07:00
Jordan Ritter d297f7d3e3 fix(vscode-extension): use ovsx verify-pat for Open VSX auth check (#4145) 2026-04-21 19:14:51 -07:00