Commit Graph

12110 Commits

Author SHA1 Message Date
Jordan Ritter b355acd56c fix(runtime): stop converting TanStack stream after RUN_FINISHED
TanStack's chat() engine runs a multi-turn agent loop: after the model
returns tool calls, it tries to execute them via processToolCalls().
Frontend-only tools (like render_pie_chart) are unknown to TanStack, so
executeToolCalls() treats them as errors and buildToolResultChunks()
re-emits TOOL_CALL_END without a preceding TOOL_CALL_START. The ag-ui
verify middleware rejects this duplicate.

Fix: track a runFinished flag in convertTanStackStream and discard all
events after the first RUN_FINISHED, which marks the boundary between
the streaming pass and TanStack's internal tool execution loop.

Also adds built-in-agent to docker-compose.local.yml and local-ports.json.
2026-04-29 22:41:58 -07:00
Tyler Slaton bd68aeda32 fix(deps): bump ag-ui packages to 0.0.53
Picks up ag-ui-protocol/ag-ui#1578 — `import * as jsonpatch from
"fast-json-patch"` produced an empty namespace under Node native ESM
because fast-json-patch@3.x populates exports via Object.assign, which
the CJS→ESM named-export detector cannot see. Result: every STATE_DELTA
and ACTIVITY_DELTA event threw "applyPatch is not a function", and
LangGraph generative UI streams floods the console with the failure on
each patch.

0.0.53 switches to a default import so the emitted bundle works under
both ESM and CJS consumers. Bumped @ag-ui/core and @ag-ui/encoder in
lockstep since they share the release.
2026-04-29 22:41:28 -07:00
Jordan Ritter 32723bb256 fix(showcase): streaming tool execution for spring-ai D5 (#4475)
## Summary

- Replace `SpringAIAgent` (AG-UI SDK) with custom `StreamingToolAgent`
that uses `.stream()` for real-time text delivery and `.call()` with
tool callbacks when tools are needed
- Spring AI's `ChatClient.stream()` fundamentally does NOT auto-execute
tool callbacks; only `.call()` has the built-in tool execution loop. The
stock `SpringAIAgent` used `.stream()` exclusively, so tools were never
invoked and the CopilotKit runtime re-invoked the agent infinitely
- Fix Jackson `InvalidTypeIdException` crashes from CopilotKit runtime
messages with unrecognized roles (`activity`, `reasoning`) by disabling
`FAIL_ON_INVALID_SUBTYPE`, with null-filtering at controller boundaries

## Root cause

The AG-UI Java SDK's `SpringAIAgent` calls
`chatClient.prompt().stream().chatResponse().subscribe()`. In Spring AI
1.0.1, the `.stream()` path does not execute tool callbacks — it only
streams text chunks. When the model returns `tool_calls` in the stream,
they're captured as AG-UI events but the actual Java tool functions are
never invoked. The CopilotKit runtime sees incomplete tool cycles and
re-invokes the agent infinitely.

## Approach

`StreamingToolAgent` implements a two-phase approach:
1. **Phase 1 (stream)**: Uses `.stream()` WITHOUT tool callbacks for
real-time text delivery. Detects whether the model wants to call tools.
2. **Phase 2 (call with tools)**: If tools are needed, re-invokes via
`.call()` WITH tool callbacks. Spring AI's built-in tool execution loop
handles all iterations automatically. AG-UI tool events are emitted via
`AgUiToolCallbackWrapper`.

When no tools are needed, the agent behaves as a pure streaming agent
(no regression from the previous `SpringAIAgent` behavior for tool-free
conversations).

## Why not the previous fix (PR #4469)?

PR #4469 replaced `SpringAIAgent` with `SyncAgent` which used `.call()`
only — this fixed tools but broke streaming entirely. This PR preserves
streaming for the happy path (no tools) and only falls back to `.call()`
when tool execution is actually needed.

## Files changed

- **New**: `StreamingToolAgent.java` — custom `LocalAgent` with
stream+call hybrid
- **New**: `JacksonConfig.java` — tolerant deserialization for unknown
message roles
- **New**: `MessageListFilter.java` — strips null messages from
deserialized lists
- **Modified**: `AgentConfig.java`, `AgentController.java`,
`AgentConfigController.java`, `A2uiFixedSchemaController.java` — use
`StreamingToolAgent` instead of `SpringAIAgent`
- **Modified**: `SubagentsController.java`,
`SharedStateReadWriteController.java` — add
`MessageListFilter.filterNulls()` at controller boundary

## Test plan

- [x] Docker build passes (`depot build --platform linux/amd64`)
- [ ] D5 agentic_chat features with tool calls (weather, flights, etc.)
complete without infinite loop
- [ ] D5 agentic_chat features without tools stream text correctly
- [ ] D5 subagents feature works (already used `.call()`, just added
null filtering)
- [ ] D5 shared-state-read-write feature works
- [ ] D5 a2ui-fixed-schema feature works
- [ ] D5 agent-config feature works
2026-04-29 22:38:28 -07:00
Jordan Ritter db44c4d4ae fix(ci): restore Railway deploy trigger in showcase build workflow (#4474)
## Summary

- Restores the Railway `serviceInstanceRedeploy` call after each GHCR
image push in the build workflow
- PR #4471 removed this trigger assuming Railway
`environmentPatchCommit` auto-update would handle deploys, but not all
services have auto-update configured
- Without the explicit trigger, GHCR images get pushed and nothing tells
Railway to pull them

## Changes

- `showcase_build.yml`: Replace the "no deploy step" comment with the
actual Railway redeploy step (matching the old `showcase_deploy.yml`
behavior)
- `showcase_deploy.yml`: Update comments to reflect that the build
workflow triggers deploys (verify workflow only checks health)

## Test plan

- [ ] Merge and observe that the next showcase push to main triggers
Railway redeploys
- [ ] Verify the "Showcase: Verify Deploy" workflow confirms services
are healthy after build completes
2026-04-29 22:37:52 -07:00
Jordan Ritter 67df2dedce fix(showcase): streaming tool execution for spring-ai D5
Spring AI's ChatClient.stream() does NOT auto-execute tool callbacks
(only .call() has a built-in tool execution loop). The stock AG-UI
SpringAIAgent uses .stream() exclusively, so tools are never invoked
and the CopilotKit runtime re-invokes the agent infinitely.

Replace SpringAIAgent with a custom StreamingToolAgent that:
- Phase 1: streams text via .stream() for real-time UX delivery
- Phase 2: if tool calls detected, re-invokes via .call() with
  tool callbacks so Spring AI's internal loop handles execution
- Emits proper AG-UI tool call events via wrapper callbacks

Also fix Jackson deserialization crashes from CopilotKit runtime
messages with unrecognised roles (activity, reasoning) by disabling
FAIL_ON_INVALID_SUBTYPE, with null-filtering at controller boundaries
to prevent NPEs in LocalAgent.combineMessages().

Affects all spring-ai endpoints: agentic_chat, agent-config,
a2ui-fixed-schema, subagents, shared-state-read-write.
2026-04-29 22:37:42 -07:00
Jordan Ritter ff2eb9c84b fix(ci): restore Railway deploy trigger in showcase build workflow
The decoupled build workflow (PR #4471) removed the Railway deploy
trigger, assuming environmentPatchCommit auto-update would handle
deploys. Not all services have auto-update configured, so GHCR images
were pushed but Railway never pulled them. Restore the explicit
serviceInstanceRedeploy call after each GHCR push.
2026-04-29 22:37:22 -07:00
Tyler Slaton b2c8209081 fix(a2ui-renderer): remove redundant prepack causing canary publish ENOENT (#4467)
## What does this PR do?

Removes the `prepack` script from
`@copilotkit/a2ui-renderer/package.json`, which was causing the canary
publish job to fail with `ENOENT` on the staging tarball.

### The failure

[Failed
run](https://github.com/CopilotKit/CopilotKit/actions/runs/25148053109/job/73712052657):

\`\`\`
✔ Build complete in 3002ms
ENOENT ENOENT: no such file or directory, open
'/tmp/.../copilotkit-a2ui-renderer-1.56.4-canary.tyler/romantic-spence-2261fe.tgz'
Error: Command failed: pnpm publish --no-git-checks --tag canary
--access public
\`\`\`

### Root cause

The release scripts already build the workspace before publishing:
- `scripts/release/prerelease.ts:77` runs `pnpm run build` across the
monorepo
- `scripts/release/publish-release.ts:145` does the same

`a2ui-renderer` was the **only** package in the monorepo with
`"prepack": "pnpm run build"`. That script fired during `pnpm publish`
and rebuilt the package a second time. Combined with tsdown's `exports:
true` (which regenerates `package.json`'s exports field on every build),
the second rewrite happened mid-publish and pnpm's pack step failed
silently — leaving an empty staging directory and the ENOENT we see.

### Why removal is safe

- All 12 other monorepo-scope packages publish without `prepack` and
work fine.
- Both release scripts (`prerelease.ts`, `publish-release.ts`) build the
whole workspace before the publish loop, so dist is always ready.
- No CI workflow or script invokes `pnpm pack` directly.
- Consumer-side installs, workspace deps, and `pnpm link` don't run
`prepack` anyway.

The only behavioral change: a developer manually running `pnpm publish`
or `pnpm pack` from the package directory now needs to `pnpm build`
first — matching the rest of the monorepo.

## Related PRs and Issues

- Failed CI run:
https://github.com/CopilotKit/CopilotKit/actions/runs/25148053109/job/73712052657

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked
2026-04-29 22:36:24 -07:00
Jordan Ritter 1888728914 fix(ci): decouple showcase build and deploy workflows (#4471)
## Summary

The old "Showcase: Build & Deploy" workflow used a single concurrency
group
with cancel-in-progress. Every push to main cancelled in-flight builds,
which meant rapid-fire PR merges routinely killed deploys mid-run. The
harness logging PR (#4465) never deployed because a subsequent merge
cancelled it.

This splits the workflow into two independent files:

- **showcase_build.yml** ("Showcase: Build & Push") -- triggered on push
to
main, builds Docker images and pushes to GHCR. Has NO concurrency group
so every run completes. Railway auto-update picks up the new :latest
tag.

- **showcase_deploy.yml** ("Showcase: Verify Deploy") -- triggered by
workflow_run from the build workflow. Polls Railway to verify each
service
  picked up the new image and is healthy. Uses cancel-in-progress since
  verification is idempotent.

Also updates showcase_capture-previews.yml to reference the renamed
build
workflow.

## Test plan

- Workflow YAML validates (checked locally)
- Build workflow: no concurrency group, every push-to-main build
completes
- Verify workflow: triggered by workflow_run, cancel-in-progress is safe
- showcase-aimock included in both workflows (from #4458)
2026-04-29 22:23:08 -07:00
Jordan Ritter 0b44960db8 Decouple showcase build and deploy into separate workflows
The old "Showcase: Build & Deploy" workflow used a single concurrency group
that cancelled in-flight builds on every push to main. When multiple PRs
merged in quick succession, most service builds got cancelled and never
deployed.

Split into two workflows:

1. showcase_build.yml ("Showcase: Build & Push") - triggered on push to main,
   builds Docker images and pushes to GHCR. Has NO concurrency group so every
   run completes. Railway auto-update picks up the new :latest tag.

2. showcase_deploy.yml ("Showcase: Verify Deploy") - triggered by workflow_run
   from the build workflow. Polls Railway to verify each service picked up the
   new image and is healthy. Uses cancel-in-progress since verification is
   idempotent. Posts results to showcase-harness via webhook.

Also updates showcase_capture-previews.yml to trigger from the renamed build
workflow.
2026-04-29 22:22:33 -07:00
Jordan Ritter 9381c93bac revert: undo SyncAgent replacement for spring-ai (#4469) (#4470)
## Summary
Reverts PR #4469 which replaced SpringAIAgent (streaming) with SyncAgent
(synchronous).
The fix changed the nature of the demo — spring-ai should demonstrate
streaming, not synchronous calls.
The proper fix needs to make SpringAIAgent.stream() correctly
auto-execute tool callbacks.

## Test plan
- [x] Revert verified — restores original SpringAIAgent code
2026-04-29 22:21:31 -07:00
Jordan Ritter 69398c01c9 Revert "fix(showcase): replace SpringAIAgent with SyncAgent for spring-ai D5 (#4469)"
This reverts commit ad774aed70, reversing
changes made to f420ceb696.
2026-04-29 22:21:11 -07:00
Jordan Ritter ad774aed70 fix(showcase): replace SpringAIAgent with SyncAgent for spring-ai D5 (#4469)
## Summary

- Replace `SpringAIAgent` (streaming `.stream()`) with custom
`SyncAgent` (synchronous `.call()`) for all spring-ai showcase
controllers
- Add `JacksonConfig` to handle unknown AG-UI message roles (activity,
reasoning) gracefully
- Fix the root cause of spring-ai D5 timeouts on tool-rendering,
hitl-steps, gen-ui, mcp-apps, and shared-state features

## Why

Spring AI's streaming mode does NOT auto-execute tool callbacks. The
model returns `tool_calls` in the SSE stream but the tools are never
invoked. The AG-UI event stream emits `TOOL_CALL_START/ARGS/END` without
`TOOL_CALL_RESULT`, causing the CopilotKit runtime to re-invoke the
agent in an infinite loop (and eventual timeout).

By using `ChatClient.call()`, Spring AI's internal tool-execution loop
runs tools automatically. `SyncAgent` wraps each backend tool callback
to capture AG-UI events during auto-invocation, and registers frontend
tools as pass-through callbacks.

The `JacksonConfig` addresses a secondary issue: the AG-UI Java SDK's
`MessageMixin` only maps 5 roles (assistant, developer, user, system,
tool). Messages with roles like "reasoning" cause
`InvalidTypeIdException` during deserialization. The config overrides
the mixin with `defaultImpl = UnknownMessage.class` so unknown roles
deserialize harmlessly.

## Test plan

- [ ] Docker build succeeds (verified locally)
- [ ] D5 tool-rendering passes
- [ ] D5 subagents passes
- [ ] D5 hitl-steps passes
- [ ] D5 mcp-apps passes
- [ ] D5 shared-state passes
2026-04-29 22:18:46 -07:00
Jordan Ritter f420ceb696 chore(showcase): trigger harness rebuild (#4468)
## Summary
No-op commit to trigger harness rebuild. Previous deploy was cancelled
by CI concurrency.
Picks up: #4460 (pool starvation), #4461 (pool logging), #4465
(comprehensive logging).
2026-04-29 22:18:20 -07:00
Jordan Ritter 44778e637e fix(showcase): replace SpringAIAgent with SyncAgent for spring-ai D5
SpringAIAgent from the AG-UI Java SDK uses streaming (.stream()) for
Spring AI's ChatClient. In streaming mode, Spring AI does NOT auto-execute
tool callbacks -- the model returns tool_calls but the tools are never
invoked. This causes the CopilotKit runtime to see incomplete tool cycles
(TOOL_CALL_START/ARGS/END without TOOL_CALL_RESULT) and re-invoke the
agent in an infinite loop, producing the D5 timeouts on tool-rendering,
hitl-steps, gen-ui, and mcp-apps features.

SyncAgent uses ChatClient.call() instead, which runs Spring AI's internal
tool-execution loop synchronously. Backend tools are auto-invoked and
their results are captured as AG-UI events (TOOL_CALL_START/ARGS/END/
RESULT). Frontend tools from the CopilotKit runtime are registered as
pass-through callbacks that emit start/args/end events without a result,
letting the runtime handle frontend execution.

Also adds JacksonConfig to handle unknown message roles (activity,
reasoning) that the AG-UI Java SDK doesn't model. Without this, Jackson
throws InvalidTypeIdException on deserialization, causing request failures
before any controller logic runs.

Changes:
- Add SyncAgent.java: LocalAgent subclass using .call() with event-
  capturing tool callback wrappers
- Add JacksonConfig.java: lenient MessageMixin with defaultImpl for
  unknown roles
- Update AgentConfig to return SyncAgent bean instead of SpringAIAgent
- Update AgentController to inject SyncAgent instead of SpringAIAgent
- Update A2uiFixedSchemaController to build SyncAgent per request
- Update AgentConfigController to build SyncAgent per request
2026-04-29 22:18:19 -07:00
Jordan Ritter bae2799f2b chore(showcase): trigger harness rebuild
Previous harness deploy was cancelled by CI concurrency. This no-op
triggers a rebuild to pick up PRs #4460 (pool starvation fix), #4461
(pool logging), and #4465 (comprehensive harness logging).
2026-04-29 22:18:03 -07:00
Jordan Ritter 93f62d474e fix(showcase): preserve langgraph-typescript agent node_modules in compose (#4466)
## Summary

- Fixes `ERR_MODULE_NOT_FOUND: Cannot find package 'tsx'` crash in the
  local langgraph-typescript container
- Root cause: the host bind mount `src:/app/src` clobbers
  `src/agent/node_modules` that was installed during the Docker build
- Fix: anonymous volume `/app/src/agent/node_modules` preserves the
  image's copy through the bind mount overlay
- google-adk D5 intermittent failures are pool starvation (already
  addressed by #4460); no code change needed

## Test plan

- [x] Verified the compose container crash-loops before the fix
- [x] Verified the container starts healthy after the fix
- [x] Confirmed the production GHCR image works correctly (the issue
  is compose-local only)
2026-04-29 22:09:37 -07:00
Tyler Slaton 644a4a2d5f fix(a2ui-renderer): remove redundant prepack script causing canary publish failures
The prepack script ran `pnpm run build` during `pnpm publish`, duplicating
the build that prerelease.ts/publish-release.ts already do across the
workspace. With tsdown's `exports: true` regenerating package.json's
exports field on every build, the second rewrite happened mid-publish
and pnpm's pack step failed with ENOENT on the staging tarball.

No other monorepo package has prepack — bringing a2ui-renderer in line.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 22:09:34 -07:00
Jordan Ritter 49efc3f0e6 fix(showcase): preserve langgraph-typescript agent node_modules in compose
The host bind mount `src:/app/src` in docker-compose.local.yml overlays
the container's /app/src directory, clobbering src/agent/node_modules
that was installed during the Docker build. This causes `node --import tsx`
to fail with ERR_MODULE_NOT_FOUND at container startup.

Add an anonymous volume for /app/src/agent/node_modules so Docker
preserves the image's copy even when the parent directory is bind-mounted.
2026-04-29 22:09:14 -07:00
Jordan Ritter 144fffcd0e fix(showcase): add comprehensive logging to harness probe execution pipeline (#4465)
## Summary

- Add structured INFO-level logging to the probe execution pipeline so
Railway logs give full visibility into probe runs without needing
PocketBase
- Cover all four layers: invoker tick lifecycle, D5 driver
per-service/per-feature results, and browser pool
acquire/release/recycle stats
- Single `probe.run-summary` log line at tick completion carries every
service's outcome in one structured payload

## Changes

**probe-invoker.ts** — tick lifecycle:
- `probe.tick-start`: probe ID, kind, discovered targets, trigger source
- `probe.target-start` / `probe.target-complete`: per-service key,
state, duration
- `probe.tick-complete`: passed/failed counts, total duration
- `probe.run-summary`: all service results in one line

**e2e-deep.ts** — D5 driver:
- `probe.e2e-deep.service-start`: slug, feature count, backend URL
- `probe.e2e-deep.feature-complete`: slug, feature type, pass/fail,
error, duration
- `probe.e2e-deep.service-complete`: slug, aggregate counts, duration

**browser-pool.ts** — pool stats:
- `browser-pool.acquire` / `browser-pool.release`: available/inUse
counts
- `browser-pool.recycle`: slot index, context count, threshold

**orchestrator.ts** — thread logger into BrowserPool constructor

## Test plan

- [x] `cd showcase/harness && npx vitest run` — 70 test files, 1372
tests pass
- [x] New log lines visible in test output confirming correct event
names and payloads
- [x] No verbose per-turn or per-page-operation logging added —
lifecycle events only
2026-04-29 22:08:20 -07:00
Jordan Ritter 41480e84e7 fix(showcase): add comprehensive logging to harness probe execution pipeline
Probe runs were a black box in Railway logs — results only visible via
PocketBase API. Add structured INFO-level logging at every lifecycle
boundary so operators can diagnose probe outcomes from logs alone.

probe-invoker.ts:
- tick-start: probe ID, kind, discovered target count, trigger source
- target-start/complete: key, state (green/red/error), duration
- tick-complete: passed/failed counts, total duration
- run-summary: single structured line with every service's outcome

e2e-deep.ts (D5 driver):
- service-start: slug, feature count, backend URL
- feature-complete: slug, feature type, pass/fail, error if failed, duration
- service-complete: slug, passed/failed/skipped counts, duration

browser-pool.ts:
- acquire/release: available/inUse counts after operation
- recycle: slot index, context count, recycle threshold

orchestrator.ts:
- Thread logger into BrowserPool constructor
2026-04-29 22:07:53 -07:00
Jordan Ritter b2d48ea4dd fix(showcase): fix llamaindex hitl-text-input D5 test failure (#4464)
## Summary

- Introduces `FixedAGUIChatWorkflow` in the llamaindex hitl-in-chat
agent to work around 3 upstream bugs in `llama-index-protocols-ag-ui`
v0.2.2:
1. **Missing `parentMessageId`** on `ToolCallChunkWorkflowEvent` —
patched to propagate parent message ID
2. **Duplicate tool calls** from both `MESSAGES_SNAPSHOT` and
`TOOL_CALL_CHUNK` events — deduplicates by tracking emitted tool call
IDs
3. **Wrong role on tool results** — tool result messages sent as
`role="user"` instead of `role="tool"` — patched to correct the role

## Test plan

- [ ] D5 probe for `llamaindex / hitl-text-input` passes
- [ ] No regressions in other llamaindex features
2026-04-29 22:03:14 -07:00
Jordan Ritter 0d4cdc6625 fix(showcase): fix llamaindex hitl-text-input D5 test failure
The LlamaIndex AGUIChatWorkflow has three bugs that caused the
hitl-text-input test to fail:

1. ToolCallChunkWorkflowEvent emitted without parent_message_id,
   causing the AG-UI client to create a duplicate assistant message
   (one from MESSAGES_SNAPSHOT, one from dechunked TOOL_CALL_START).
   The duplicate produced 8 time-picker buttons instead of 4.

2. _snapshot_messages embeds toolCalls in the MESSAGES_SNAPSHOT AND
   emits separate TOOL_CALL_CHUNK events, so the client registers
   the same tool call twice on the same message.

3. ag_ui_message_to_llama_index_message converts AG-UI ToolMessages
   to ChatMessage(role='user') instead of role='tool', so the second-
   leg OpenAI API call has no tool-result message and aimock's
   hasToolResult matcher fails.

Fix: subclass AGUIChatWorkflow (FixedAGUIChatWorkflow) that:
- Assigns a stable ID to assistant messages and passes it as
  parent_message_id on ToolCallChunk events
- Strips toolCalls from MESSAGES_SNAPSHOT (letting TOOL_CALL_CHUNK
  be the sole mechanism)
- Corrects tool-result message role from 'user' to MessageRole.TOOL
  so the OpenAI API receives proper tool-result messages
2026-04-29 22:02:39 -07:00
Jordan Ritter 2781b7d80e fix(showcase): add logging to pool abort-release path (#4461)
## Summary
- Log pool stats when abort signal force-releases a browser back to the
pool
- Provides visibility into starvation events for diagnosis in Railway
logs

Follow-up to PR #4460 which added the abort-release mechanism but no
telemetry.

## Test plan
- [x] 1372 harness tests pass
2026-04-29 21:45:07 -07:00
Jordan Ritter 3c56220770 fix(showcase): add logging to pool abort-release path
Log pool stats and context counts when the abort signal fires and
force-releases a browser, so pool starvation events are visible in
Railway logs for diagnosis.
2026-04-29 21:44:51 -07:00
Jordan Ritter 8f2bce476b fix(showcase): release pooled browsers on abort to prevent D5 pool starvation (#4460)
## Summary

- Wire abort signal through to `createPooledE2eDeepLauncher` so pooled
browsers are force-released when the invoker's timeout fires
- Track open browser contexts and close them before releasing on abort
to prevent orphaned contexts
- Guard against double-release with a `forceReleased` flag so the
driver's finally block is a no-op after abort

## Root cause

When `executeOne`'s `Promise.race` timeout fires, it returns the timeout
result but the driver promise continues running with a pooled browser
acquired. The browser is not released back to the pool until the driver
eventually finishes (up to 5 minutes later). Since pool size (4) equals
`max_concurrency` (4), one orphaned browser means the next probe run (15
minutes later) can only acquire 3 browsers. Orphans accumulate across
ticks, causing cascading starvation that manifests as D5 probe flapping
in production.

## Changes

- `E2eDeepBrowserLauncher` type now accepts an optional `AbortSignal`
- `createPooledE2eDeepLauncher` registers an abort listener that closes
open contexts and releases the browser back to the pool immediately
- Driver's `run()` passes its abort signal to the launcher
- 5 new tests covering: abort release, context cleanup on abort,
pre-aborted signal, full starvation scenario with pool recovery, and
backwards-compat no-signal path

## Test plan

- [x] 5 new unit tests for the pooled launcher abort behavior (all pass)
- [x] Full harness test suite: 1372 tests pass, 0 failures
- [x] TypeScript typecheck clean
2026-04-29 21:37:49 -07:00
Jordan Ritter ed0933e5c4 fix(showcase): release pooled browsers on abort to prevent pool starvation
When the probe invoker's timeout fires, Promise.race returns the timeout
result but the driver promise keeps running with a pooled browser held.
The browser is not released back to the pool until the driver eventually
finishes (up to 5 minutes later). Since pool size (4) equals
max_concurrency (4), orphaned browsers accumulate across ticks and cause
cascading starvation -- each 15-minute probe run has fewer available
browsers until the pool is completely exhausted.

The fix wires the abort signal through to createPooledE2eDeepLauncher.
When abort fires, the launcher force-closes open browser contexts and
releases the browser back to the pool immediately. A forceReleased flag
prevents the driver's finally block from double-releasing.
2026-04-29 21:37:20 -07:00
github-actions[bot] a094fa92fb style: auto-fix formatting 2026-04-30 04:29:46 +00:00
Tyler Slaton 4bd909f0ca refactor(react): polish image attachment thumbnails
Iterate on the image attachment rendering based on review feedback:

- Reduce thumbnail size to 80x80 (down from 300x300) so attachments
  read as compact thumbnails like Claude's chat UI
- Render attachments above the message text instead of below, and lay
  multiple attachments out in a horizontal row (flex-row + flex-wrap +
  justify-end) instead of stacking vertically
- Add a muted background so transparent images stay readable
- Extract Lightbox + useLightbox into a shared module so the rendered
  attachment can reuse the same click-to-zoom modal as the queue preview;
  clicking a thumbnail now opens it in a fullscreen lightbox with a
  view-transition morph

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 21:27:43 -07:00
Tyler Slaton f480e2427d fix(react): render image attachments as compact thumbnails
Constrain image attachments to a 300x300 max size with object-cover and
12px rounded corners so they appear as small thumbnails in chat instead
of filling the message width. Applies to both the v2 renderer (Tailwind)
and the legacy react-ui renderer (CSS).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 20:48:35 -07:00
Jordan Ritter ed56950650 feat(showcase): restore eval system + per-test JSON + slug-keyed baselines (#4457)
## Summary

- **Restores the eval system** accidentally deleted by PR #4449 ("D5
all-green" session removed it as "superseded by D5 depth probes" while
unaware #4448 had just merged)
- **Per-test JSON granularity** — eval runner now injects
`--reporter=list,json` + `PLAYWRIGHT_JSON_OUTPUT_NAME` when spawning
test subprocesses, reads JSON file after exit for per-test results,
falls back to exit-code when absent
- **Slug-keyed baselines** — `transformHarnessResponse` restructured to
key by integration slug (via `summary.services[]`) instead of probe ID,
matching `collectResults` format so `computeRegressions` works against
harness-prod baselines

## Context

PR #4448 added the eval system (5 CR rounds, 8 bugs fixed, 35+ agent
reviews). PR #4449 (a concurrent session) deleted it. This PR restores
the CR-converged code and adds two follow-up improvements from the eval
plan.

## Test plan

- [x] `npx nx run @copilotkit/showcase-harness:test` — 1375 tests pass
(70 files)
- [x] All 8 CR fixes from #4448 verified present in restored code
- [ ] CI green
2026-04-29 20:27:33 -07:00
Jordan Ritter 6e5a641241 fix: add showcase-aimock to CI build & deploy pipeline (#4458)
## Summary

- Add `showcase-aimock` to the Build & Deploy workflow matrix so fixture
file changes in `showcase/aimock/` auto-deploy on merge to main
- Create `showcase/aimock/Dockerfile` -- thin overlay on
`ghcr.io/copilotkit/aimock:latest` that bakes fixture files into the
image
- Add local-vs-production parity comment in `docker-compose.local.yml`
explaining that new fixture files must be added to both the volume
mounts and the Dockerfile
- Add "Aimock Fixture Deployment" section to `showcase/RUNBOOK.md` with
manual build command and new-fixture checklist

## Why

Production showcase-aimock was running a week-old image with no D5
fixtures because fixture file changes did not trigger a CI rebuild. This
caused D5 probe failures that required manual intervention to resolve.

## Test plan

- [ ] CI workflow parses correctly (no YAML syntax errors)
- [ ] `workflow_dispatch` with `service=showcase-aimock` triggers the
aimock build
- [ ] Push to `main` touching `showcase/aimock/**` triggers the aimock
matrix leg
2026-04-29 20:24:05 -07:00
Jordan Ritter e4a332e095 fix: add showcase-aimock to CI build & deploy pipeline
Production showcase-aimock was running a week-old image because fixture
file changes in showcase/aimock/ did not trigger a CI rebuild. This adds
showcase-aimock to the Build & Deploy workflow matrix so it auto-deploys
on merge, creates a thin Dockerfile that bakes fixture files into the
image, documents the local-vs-production parity requirement in
docker-compose.local.yml, and adds an aimock fixture deployment section
to the RUNBOOK.
2026-04-29 20:23:41 -07:00
Jordan Ritter b4d5b9c0d4 fix(showcase): update pin ratchet baseline for restored eval files
Bump validatePinsFailCount 132→134 and hash to account for
eval-tiers.json and showcase_eval.yml re-added by eval restoration.
2026-04-29 20:22:47 -07:00
github-actions[bot] 4d2b426494 style: auto-fix formatting 2026-04-30 03:13:25 +00:00
Jordan Ritter 0dec780d19 feat(showcase): per-test JSON reporter + slug-keyed baselines
Runner: inject --reporter=list,json and PLAYWRIGHT_JSON_OUTPUT_NAME
when spawning test subprocesses; read JSON file after exit for per-
test granularity; fall back to stdout then exit-code.

Baseline: restructure transformHarnessResponse to iterate
summary.services[] and produce slug-keyed results with _status
sub-keys matching collectResults format. Worst-status-wins when
same slug appears in multiple probes.
2026-04-29 20:11:41 -07:00
Jordan Ritter db62915362 fix(showcase): restore eval system accidentally deleted by #4449
PR #4449 removed the eval tier system while unaware #4448 had just
merged. Restoring orchestrator, matrix, scope, config, GHA workflow,
and CLI wiring with all 8 CR fixes intact.
2026-04-29 20:11:33 -07:00
Jordan Ritter 5654964b8c Add showcase favicon and og:image (#4455)
## Summary
- Adds favicon (heater shield + CopilotKit kite, magenta #ff44bb on dark
#0a0a0f)
- Adds 1200x630 og:image PNG with shield+kite icon and "Showcase /
CopilotKit Integration Gallery" text
- Updates layout.tsx metadata: title, description, icons, openGraph

## Test plan
- [ ] Verify favicon renders in browser tab at showcase.copilotkit.dev
- [ ] Verify og:image appears when sharing showcase URL on Slack/Twitter
- [ ] Confirm no build regressions in showcase shell
2026-04-29 20:03:49 -07:00
Jordan Ritter 1cd151e5b8 feat(showcase): add favicon and og:image
Heater shield with CopilotKit kite logo in magenta on dark
background, matching the sub-property icon style across
copilotkit.dev properties. Wires up Next.js metadata for
title, description, icons, and openGraph.
2026-04-29 20:03:33 -07:00
Jordan Ritter c9b0c59a02 fix(showcase/ag2): remove __future__ annotations causing deploy crash (#4456)
## Summary

- Remove `from __future__ import annotations` from
`agent_config_agent.py` which causes the AG2 agent server to crash on
startup with `PydanticInvalidForJsonSchema: Cannot generate a JsonSchema
for core_schema.CallableSchema`
- The import turns `ContextVariables` type hints into string ForwardRefs
that AG2's pydantic TypeAdapter can't resolve at tool registration time
- This fix was previously applied (cb2ef941) but reverted as part of the
broader D2 revert (5a28b778)

## Test plan

- [x] Built Docker image locally and confirmed crash reproduction on
current main
- [x] Applied fix, rebuilt, confirmed agent starts successfully (`Agent
started (PID: 8)` vs previous `ERROR: Agent failed to start`)
- [x] Verified the current deployed GHCR image also crashes
(pre-existing since the D2 revert)
2026-04-29 20:00:55 -07:00
Jordan Ritter 6ddd3bb603 fix(showcase/ag2): remove __future__ annotations from agent_config_agent
The `from __future__ import annotations` import turns the
`ContextVariables` type hint on the @tool-decorated
`get_current_config` function into a string ForwardRef that AG2's
pydantic TypeAdapter cannot resolve at registration time, raising
`PydanticInvalidForJsonSchema: Cannot generate a JsonSchema for
core_schema.CallableSchema` and crashing the entire FastAPI agent
server on startup.

This fix was previously applied (cb2ef9411) but reverted as part of
the broader D2 revert (5a28b778c). The file uses no PEP 585/604
syntax, so the import was purely harmful.
2026-04-29 20:00:30 -07:00
Jordan Ritter c3baa974a4 fix(showcase): add missing @copilotkit/voice dep to mastra and google-adk (#4454)
## Summary
- Add `@copilotkit/voice` to mastra and google-adk package.json
- Mastra build was failing in CI; google-adk resolved via hoisting but
adding explicitly

## Test plan
- [x] Audit confirmed these are the only two packages missing this dep
2026-04-29 19:58:21 -07:00
Jordan Ritter af289671d0 fix(showcase): add missing @copilotkit/voice dep to mastra and google-adk
The voice demo route handler imports @copilotkit/voice which was not
declared in these two package.json files. Other integrations either
had it or resolved it via hoisting, but mastra's Docker build failed.
2026-04-29 19:58:05 -07:00
Jordan Ritter 723cfd88d5 fix: add missing byoc-json-render deps to mastra showcase (#4453)
## Summary

- Adds 4 missing dependencies to
`showcase/integrations/mastra/package.json`:
  - `@json-render/core` (0.18.0)
  - `@json-render/react` (0.18.0)
  - `@radix-ui/react-checkbox` (^1.3.3)
  - `@radix-ui/react-separator` (^1.1.8)

## Why

The `byoc-json-render` and `beautiful-chat` demos import these modules,
but they were never added to mastra's package.json. Every other
integration that ships these demos (langgraph-python, agno, etc.)
already has them. The omission causes webpack `Module not found` errors
during `next build`, blocking the mastra Docker deploy.

## Test plan

- [ ] CI builds mastra showcase successfully
- [ ] Docker build no longer fails on missing module errors
2026-04-29 19:51:52 -07:00
Jordan Ritter 6dfdc55ad3 fix: add missing byoc-json-render deps to mastra showcase
The byoc-json-render demo imports @json-render/core, @json-render/react,
@radix-ui/react-checkbox, and @radix-ui/react-separator, but these were
never added to mastra's package.json (they exist in langgraph-python and
all other integrations). This causes webpack Module not found errors
during next build in Docker.
2026-04-29 19:51:30 -07:00
Jordan Ritter f374f264b1 style(showcase/langroid): auto-format tool-rendering page (#4452)
## Summary
- Auto-format langroid tool-rendering page.tsx that was missed in PR
#4449

## Test plan
- [x] `npx oxfmt --check` passes on the file
2026-04-29 19:46:06 -07:00
Jordan Ritter 3a8c5131cb style(showcase/langroid): auto-format tool-rendering page 2026-04-29 19:45:48 -07:00
Jordan Ritter 39faeb70ac fix(showcase): D5 all-green — fixture migration + 11 integration fixes (#4449)
## Summary

Migrate all D5 (e2e-deep) fixtures from `turnIndex` to `hasToolResult`
matching and fix integration-level bugs across 12 showcase frameworks to
achieve 15/17 D5 green.

- **Fixture migration**: `hasToolResult` checks whether tool-role
messages exist in the request, correctly disambiguating multi-turn
fixtures regardless of backend AG-UI implementation
- **Integration fixes**: agno, claude-sdk-python/typescript,
crewai-crews, google-adk, langgraph-typescript, langroid, llamaindex,
ms-agent-python, pydantic-ai, spring-ai, strands
- **Test infra**: cmd-test.sh rewrite, fail-baseline update, eval tier
removal, dev volume mounts

**15/17 green locally.** llamaindex (hitl-text-input disabled buttons)
and spring-ai (Java AG-UI message format) remain red — tracked for
follow-up.

## Test plan
- [x] All 15 passing frameworks verified locally with `showcase test
<slug> --d5`
- [x] langgraph-python (gold standard) confirmed green after changes
- [x] No regressions in existing D0-D4 probes (CI green on prior push)
2026-04-29 19:40:51 -07:00
Jordan Ritter d0212f2bf1 fix(showcase): add dev volume mounts to docker-compose.local.yml
Bind-mount source directories into all 17 integration containers so
code changes only need a container restart, not a full image rebuild.
2026-04-29 19:40:11 -07:00
Jordan Ritter 5abf0409a8 fix(showcase): D5 test infrastructure and CLI improvements
- Rewrite cmd-test.sh to use harness CLI for D5 probes
- Add run-e2e-with-aimock.sh helper (removed stale version)
- Update fail-baseline hash for D5 coverage
- Fix provider base URLs in .env.example
- Update local-ports.json for spring-ai
- Remove eval tier system (superseded by D5 depth probes)
- Add D5 debugging runbook
2026-04-29 19:40:10 -07:00
Jordan Ritter 534cd1efa7 fix(showcase): D5 integration fixes across 12 frameworks
Per-framework fixes to pass D5 e2e-deep probes:
- agno: deduplicate agent_server routes
- claude-sdk-python: handle ParsedContentBlockStopEvent (SDK v0.97+)
- claude-sdk-typescript: remove orphan tool-rendering page
- crewai-crews: add backend tool_rendering agent + shared_state fix
- google-adk: add AGUIToolset to all ADK agents for frontend tools
- langgraph-typescript: remove stale import
- langroid: emit ToolCallResultEvent for backend tools + fix adapter
- llamaindex: v2 provider import, book_call stub, PYTHONPATH fix
- ms-agent-python: disable Responses API store for aimock compat
- pydantic-ai: simplify gen-ui page component
- spring-ai: raise tool iteration cap (1→5) + fix connection pooling
- strands: shared tools symlink + requirements update
2026-04-29 19:40:10 -07:00