Commit Graph

8565 Commits

Author SHA1 Message Date
Alem Tuzlak 99868c7c46 feat(showcase): regenerate d5-all.json bundle and fix DOM typing in chat-css probe (F) 2026-04-30 13:25:27 +02:00
Alem Tuzlak 203da612db feat(showcase): D5 scripts for interrupt + BYOC families (B6) 2026-04-30 13:23:26 +02:00
Alem Tuzlak 56974444f8 feat(showcase): D5 scripts for gen-UI family (B5) 2026-04-30 13:22:00 +02:00
Alem Tuzlak b9246586b5 feat(showcase): D5 scripts for state family (B4) 2026-04-30 13:20:20 +02:00
Alem Tuzlak 465c9b697a feat(showcase): D5 scripts for frontend-tools + reasoning families (B3) 2026-04-30 13:19:15 +02:00
Alem Tuzlak 7f3d458cdf feat(showcase): D5 scripts for platform family (B2: auth, multimodal, agent-config) 2026-04-30 13:17:01 +02:00
Alem Tuzlak a5f347e88f feat(showcase): D5 scripts for chat-surface family (B1) 2026-04-30 13:12:16 +02:00
Alem Tuzlak e2065dc98f feat(showcase): scaffold 20 new D5FeatureType literals for LGP coverage 2026-04-30 13:06:33 +02:00
Alem Tuzlak b5e43b35c6 chore(showcase): ratchet validate-pins hash baseline
Pre-existing drift on main: count held at 134, hash shifted (one
FAIL healed, another regressed). Captured the new sorted-FAIL hash
locally with the same algorithm CI uses (sort -u | shasum -a 256)
and updated showcase/scripts/fail-baseline.json to match.
2026-04-30 12:59:28 +02:00
Alem Tuzlak ef4d88b717 fix(showcase/langroid): drop DEBUG warning leak + update stale tool-result tests
Two bugs introduced in 534cd1efa (D5 integration fixes) when the
langroid adapter switched backend tool results from TEXT_MESSAGE_*
triples to TOOL_CALL_RESULT events:

1. Two leftover `logger.warning("DEBUG ...")` calls in agui_adapter.py
   that fired on every chat turn — exactly the noise pattern the
   `test_plain_text_turn_does_not_warn` test was written to prevent.

2. Three stale tests in test_agui_adapter.py (only run on 3.11+, so
   the failure was 3.12-only):
   - test_backend_tool_execution_happy_path: still expected the old
     TEXT_MESSAGE_START/CONTENT/END inner triple instead of a single
     TOOL_CALL_RESULT event after TOOL_CALL_END.
   - test_backend_tool_exception_returns_sanitized_error: still read
     the sanitized error JSON from TEXT_MESSAGE_CONTENT.delta — it
     now rides on TOOL_CALL_RESULT.content.
   - test_plain_text_turn_does_not_warn: caught the DEBUG leak above.

Tests are skipped on Python 3.10 (langroid imports `typing.Self`),
which is why this only shows up on the 3.12 matrix entry.
2026-04-30 12:59:28 +02:00
Alem Tuzlak b243a21e95 docs: LGP D5 coverage design plan 2026-04-30 12:42:35 +02:00
Alem Tuzlak f1b02a4616 fix(showcase): stop infinite tool-call loop in beautiful-chat + restore brand styling
Beautiful Chat suggestion clicks looped forever because feature-parity.json
tool-calling fixtures lacked an `id` and a paired `toolCallId` followup.
After the agent ran the tool and re-prompted aimock, the same userMessage
substring matched again and the same toolCall was returned indefinitely.
Added explicit ids to 10 broken fixtures (pieChart, barChart, render_*_chart,
scheduleTime, search_flights, toggleTheme) and 11 paired toolCallId
followups returning content summaries — same convention the file already
uses for show_card, weather, etc.

Beautiful Chat layout also showed a black/white split and a broken logo on
the 8 integrations using the full ExampleLayout pattern (crewai-crews,
langgraph-fastapi, langgraph-python, langgraph-typescript, mastra,
ms-agent-dotnet, ms-agent-python, pydantic-ai). Two issues:

1. globals.css hardcoded `body { background: #fafaf9 }` and never defined
   the brand tokens (--background, --foreground, --card, --primary, …) that
   the layout, mode-toggle, todo card/column, and chart components reference
   via Tailwind 4 arbitrary values. ThemeProvider was also adding `dark` to
   <html> from system preference, so CopilotKit's chat went dark while body
   stayed cream.
2. example-layout/index.tsx renders <img src="/copilotkit-logo.svg" /> but
   the file did not exist in any integration's public/.

Added the full token set (light + dark) under :root and :root.dark/.dark,
registered the Tailwind 4 dark variant, switched body to var(--background)
/var(--foreground), and copied copilotkit-logo.svg + copilotkit-logo-mark.svg
into each integration's public/ from examples/integrations/langgraph-python.
2026-04-30 12:42:26 +02:00
Alem Tuzlak d34bb1ac16 fix(showcase/langroid): drop DEBUG warning leak + update stale tool-result tests
Two bugs introduced in 534cd1efa (D5 integration fixes) when the
langroid adapter switched backend tool results from TEXT_MESSAGE_*
triples to TOOL_CALL_RESULT events:

1. Two leftover `logger.warning("DEBUG ...")` calls in agui_adapter.py
   that fired on every chat turn — exactly the noise pattern the
   `test_plain_text_turn_does_not_warn` test was written to prevent.

2. Three stale tests in test_agui_adapter.py (only run on 3.11+, so
   the failure was 3.12-only):
   - test_backend_tool_execution_happy_path: still expected the old
     TEXT_MESSAGE_START/CONTENT/END inner triple instead of a single
     TOOL_CALL_RESULT event after TOOL_CALL_END.
   - test_backend_tool_exception_returns_sanitized_error: still read
     the sanitized error JSON from TEXT_MESSAGE_CONTENT.delta — it
     now rides on TOOL_CALL_RESULT.content.
   - test_plain_text_turn_does_not_warn: caught the DEBUG leak above.

Tests are skipped on Python 3.10 (langroid imports `typing.Self`),
which is why this only shows up on the 3.12 matrix entry.
2026-04-30 11:56:08 +02:00
Alem Tuzlak bea43fb999 chore(showcase): ratchet validate-pins hash baseline
Pre-existing drift on main: count held at 134, hash shifted (one
FAIL healed, another regressed). Captured the new sorted-FAIL hash
locally with the same algorithm CI uses (sort -u | shasum -a 256)
and updated showcase/scripts/fail-baseline.json to match.
2026-04-30 11:41:03 +02:00
Alem Tuzlak 4f0301922a chore(showcase): unify unsupported demo convention
Drop gen-ui-interrupt and interrupt-headless demo entries from
manifest.yaml for langroid and claude-sdk-typescript, and delete
the orphan stub page.tsx + README.md files for those routes. Both
features stay listed in not_supported_features so the dashboard
keeps showing the no-entry icon with hover tooltip.

Aligns these two integrations with the convention used by the other
12 integrations that mark these features unsupported (ag2, agno,
built-in-agent, claude-sdk-python, crewai-crews, google-adk,
llamaindex, mastra, pydantic-ai, spring-ai, strands): icon-only
with tooltip rather than a Demo / Code row that links to a stub
page explaining why there is nothing to demo.
2026-04-30 11:35:56 +02:00
Alem Tuzlak 917c54a8d1 Merge branch 'main' into tyler/kind-mendeleev-636369 2026-04-30 11:03:19 +02:00
github-actions[bot] 2ed24a1cb5 style: auto-fix formatting 2026-04-30 08:49:30 +00:00
Jordan Ritter f0952e7656 fix(showcase): fix llamaindex D5 failures for tool-rendering, gen-ui-headless, hitl-steps
Three D5 probe failures fixed:

tool-rendering: get_weather was a backend tool so the workflow emitted
TOOL_CALL_CHUNK but no TOOL_CALL_RESULT, leaving useRenderTool stuck in
loading state forever. Move get_weather to frontend_tools so CopilotKit
manages the tool call lifecycle. Add ToolCallResultWorkflowEvent (which
subclasses ToolCallEndWorkflowEvent to pass the AG_UI_EVENTS isinstance
filter) and override aggregate_tool_calls to emit TOOL_CALL_RESULT for
render-only tools. The WeatherCard component now always renders with
data-testid="weather-card" even during loading, instead of switching to
a separate div that lacks the testid.

gen-ui-headless: the shared agent was missing a show_card frontend tool
stub, so the workflow never emitted TOOL_CALL_CHUNK for it. Add the
stub and register it in frontend_tools.

hitl-steps: human_in_the_loop was routed to the hitl-in-chat specialized
agent (which only has book_call), not the shared agent (which has
generate_task_steps). Move human_in_the_loop to sharedAgentNames so it
routes through the default agent. Introduce render_only_tool_names set
to distinguish render-only tools (get_weather) from interactive tools
(generate_task_steps, book_call, show_card) — only render-only tools
get premature TOOL_CALL_RESULT; interactive tools let CopilotKit manage
the result lifecycle to keep buttons enabled.
2026-04-30 01:47:37 -07:00
Jordan Ritter d44de79512 fix(showcase): fix gen-ui probe __name ReferenceError and querySelectorAll cascade
The gen-ui-headless D5 probe had two bugs:

1. esbuild's keepNames transform injects __name() wrappers around const
   arrow functions. When those functions run inside page.evaluate() in
   the browser context, __name is not defined. Replace with string-based
   new Function() construction using function declarations.

2. findFirstNonTrivial and readChildCountForSelector used querySelector
   which only returns the first match. On headless chat pages, the first
   assistant message div is an empty wrapper (children=0), causing the
   cascade to skip the selector even when a later match contains the
   rendered gen-UI component. Switch to querySelectorAll and iterate all
   matches.
2026-04-30 01:47:19 -07:00
Alem Tuzlak 8647a1fcb6 Merge branch 'main' into fix/spring-ai-streaming-regression 2026-04-30 10:39:02 +02:00
Alem Tuzlak 73c0e5e8eb fix(react-core): re-attach input overlay observer after welcome screen (#4472)
## Summary

Fixes a regression introduced by
[f9eee68](https://github.com/CopilotKit/CopilotKit/commit/f9eee688b)
(overlay chat input on scroll area) where late messages and "always"
suggestions slid underneath the absolute-positioned input pill once the
user submitted their first message.

**Root cause:** the `ResizeObserver` `useEffect` in `CopilotChatView`
had `[]` deps. On a fresh chat it mounted with the welcome-screen branch
active — `inputContainerRef.current` was null, the effect bailed, and it
never re-ran when the chat-view branch attached the overlay element.
`inputContainerHeight` stayed at 0, so the scroll content's reserved
bottom padding sat at 32px instead of ~input height.

**Fix:** hold the overlay element in state via a callback ref and key
the effect on the element. Same pattern already used by
`nonAutoScrollRefCallback` elsewhere in this file. The observer now
attaches and detaches reactively as the overlay mounts/unmounts.

## Test plan

- [x] New regression test in `CopilotChatView.inputOverlay.test.tsx`
mounts on the welcome screen, re-renders with messages, and asserts the
observer attaches to the new overlay element and feeds the correct
`paddingBottom` (88 + 32 = 120px). Reverting the fix makes it fail at
the post-transition padding assertion.
- [x] Existing 4 inputOverlay tests still pass.
- [ ] Verify in the demo: load a fresh chat, submit a message, confirm a
long assistant response leaves a gap above the input pill (no content
sliding under the pill).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-30 10:36:36 +02:00
Jordan Ritter e7980c71fd fix(showcase/spring-ai): prevent streaming crash on frontend tool calls
Spring AI's OpenAiChatModel.internalStream() auto-executes tool calls
through the global ToolCallingManager, which only knows about backend
tools. When the model calls frontend tools (generate_task_steps,
show_card, etc.) injected by the CopilotKit runtime, the
StaticToolCallbackResolver returns null and DefaultToolCallingManager
throws IllegalStateException, crashing the stream.

Two fixes:

1. StreamingToolAgent Phase 1 (streaming): set
   internalToolExecutionEnabled=false via OpenAiChatOptions so Spring AI
   detects tool_calls in the stream without attempting execution. Phase 2
   (.call()) keeps internal execution enabled so Spring AI's built-in
   loop handles backend tools.

2. BoundedToolCallingManagerConfig: wrap StaticToolCallbackResolver with
   LenientToolCallbackResolver that returns a FrontendToolPlaceholder
   for unknown tools instead of letting the resolver return null. This
   prevents the IllegalStateException during Phase 2 when the model
   calls frontend tools.

Also reorder AG-UI event emission so tool call events are emitted
BEFORE textMessageEnd, which is required for the frontend's
useRenderTool to see them while the message is still open.

Recovers agentic-chat, gen-ui-custom, and gen-ui-headless D5 tests
from 0/11 to 3/11. Remaining failures are pre-existing issues in
other controllers (subagents, shared-state, hitl).
2026-04-30 01:30:56 -07:00
Alem Tuzlak 672070946f fix(react): render image attachments as compact thumbnails (#4459)
## What does this PR do?

Polish image attachments in chat. Previously they rendered at the full
message
width, taking up most of the screen. After this PR they render as
compact 80x80
thumbnails — closer to how Claude shows attached images.

**Layout & sizing**
- Image attachments render as 80x80 thumbnails with `object-cover`, 12px
rounded corners, and a muted background so transparent PNGs stay
readable.
- Multiple attachments lay out in a horizontal row (`flex-row` +
`flex-wrap` +
  `justify-end`) instead of stacking vertically.
- Attachments render *above* the message text instead of below.

**Click-to-zoom**
- Clicking a thumbnail opens it in a fullscreen lightbox with a smooth
  view-transition morph (same UX as the attachment queue preview).
- The `Lightbox` + `useLightbox` previously private to
  `CopilotChatAttachmentQueue` got extracted into a shared module
(`packages/react-core/src/v2/components/chat/Lightbox.tsx`) so the queue
and
  the rendered message attachment share one modal implementation.

**Both renderers updated**
- v2 (Tailwind): `CopilotChatAttachmentRenderer.tsx` +
`CopilotChatUserMessage.tsx`
- legacy (CSS): `packages/react-ui/src/css/messages.css`

## Related PRs and Issues

- N/A

## 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-30 09:53:20 +02:00
github-actions[bot] 73c90bf022 style: auto-fix formatting 2026-04-30 07:40:00 +00:00
Jordan Ritter 4c8a2efe37 fix(showcase): align built-in-agent D5 wiring with LGP fixture tool names
Seven D5 features fail because built-in-agent demo pages use different
tool names and hook types than the LangGraph-Python reference agent
that the D5 fixtures were recorded against.

- tool-rendering: switch from useComponent("weather") to
  useRenderTool("get_weather") with correct {parameters, result, status}
  shape
- gen-ui-tool-based: rename useComponent("haiku") to
  useComponent("generate_haiku"), fix HaikuCard to accept args as
  direct props instead of {status, result, parameters}
- hitl-in-chat: create /demos/hitl-in-chat route (was missing); reuses
  TimePickerCard from hitl-in-chat-booking with book_call tool
- shared-state: add set_notes server tool to baseServerTools so the
  fixture's set_notes call has a backend handler
- subagents: rename delegate_to_planner/researcher to
  research_agent/writing_agent/critique_agent matching LGP tool names
- subagents page: update useComponent registrations and DelegationCard
  for the renamed tools
2026-04-30 00:38:38 -07:00
Jordan Ritter b3fb89842d fix(showcase): remove trailing slash from ms-agent-python hitl-in-app agent URL
The hitl-in-app agent was registered with a trailing slash in the URL
(`/hitl-in-app/`), while the FastAPI backend mounts the endpoint at
`/hitl-in-app` (no trailing slash). FastAPI's default redirect_slashes
behavior returns a 307 redirect for POST requests to the trailing-slash
variant, and the AG-UI HttpAgent does not follow POST redirects. This
caused the agent to appear completely unresponsive — the D5
hitl-approve-deny probe timed out at 60s with zero assistant messages.

Use the shared `createAgent()` helper (which does not append a trailing
slash) for consistency with every other agent registration in the file.
2026-04-30 00:34:46 -07:00
Jordan Ritter f5c97aea1e fix(showcase): test built-in-agent with @copilotkit/runtime@4482 pkg-pr-new build
Install the pkg.pr.new build of @copilotkit/runtime from PR #4482 to
validate the runFinished fix that stops duplicate TOOL_CALL_END events
in convertTanStackStream after TanStack's RUN_FINISHED.
2026-04-30 00:26:21 -07:00
Jordan Ritter f0c73fdebc fix(showcase): replace ? with strikethrough for unavailable probe depths (#4484)
## Summary

- When a dashboard cell has no probe data for a depth (D5, D6), the
badge now renders the dimension name with CSS `line-through` instead of
appending a `?` label
- Strikethrough communicates "this depth hasn't run" more clearly than
`D5 ?`
- Same treatment applied to the CellDrilldown panel (renders
strikethrough `n/a` instead of `?`)

## Test plan

- [x] cell-drilldown tests updated and passing
- [x] live-status tests passing (formatLabel still returns `?`
internally; rendering layer handles display)
- [x] depth-chip, depth-utils, chips-explainer tests all green
2026-04-30 00:08:58 -07:00
Jordan Ritter 7e5da38471 fix(showcase): replace ? with strikethrough for unavailable probe depths
When a dashboard cell has no probe data for a depth (D5, D6), render
the dimension name with CSS line-through instead of appending a "?"
label. Strikethrough communicates "this depth doesn't exist / hasn't
run" more clearly than "D5 ?".

Changes:
- Badge component: when label is "?", render name with line-through
  styling and suppress the question mark
- CellDrilldown BadgeRow: render "n/a" with line-through instead of "?"
- Update cell-drilldown test to verify strikethrough rendering
2026-04-30 00:08:34 -07:00
Tyler Slaton 98fbcd5ea4 feat(langgraph-py): add file-uploads and bug-fixes (#4483) 2026-04-30 00:01:21 -07:00
Tyler Slaton cff40fdf20 fix(react-core): re-attach overlay observer when leaving welcome screen
`CopilotChatView` mounts on the welcome-screen branch, where the absolute-
positioned input overlay (and its `inputContainerRef`) does not exist. The
ResizeObserver useEffect ran once with an empty `[]` dep array, found
`ref.current === null`, and bailed. Submitting the first message swapped
to the chat-view branch and attached the overlay element — but the effect
never re-ran, so `inputContainerHeight` stayed at 0 and the scroll
content's reserved bottom padding sat at 32px instead of ~input height.
Late messages and any "always" suggestion strip slid underneath the input
pill, invisible to the user.

Hold the overlay element in state via a callback ref and key the effect
on the element. Same pattern already used by `nonAutoScrollRefCallback`
in this file. Effect now attaches and detaches reactively as the overlay
mounts/unmounts (e.g. clearing messages and falling back to the welcome
screen also resets the measured height instead of holding stale data).

Add a test that mounts on the welcome screen, re-renders with messages,
and asserts the observer attaches to the new overlay element and feeds
the correct paddingBottom. Reverting the fix makes it fail on the
post-transition padding assertion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 23:55:26 -07:00
Tyler Slaton 01fd0ac6d7 feat(langgraph-py): add file-uploads and bug-fixes
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-04-29 23:54:10 -07:00
Jordan Ritter 0a7e1be184 fix(showcase): apply FixedAGUIChatWorkflow to all llamaindex agents (#4481)
## Summary
- Apply `FixedAGUIChatWorkflow` to `hitl_in_app_agent.py` and the main
`agent.py` router
- PR #4464 only fixed `hitl_in_chat_agent.py`; the same three upstream
llamaindex bugs affect all agents using `AGUIChatWorkflow`
- Switches both files to `workflow_factory` pattern importing
`FixedAGUIChatWorkflow`

## Why
The D5 probe failures for llamaindex hitl-approve-deny and hitl-steps
features are caused by the same upstream bugs (duplicate tool-call
events, missing parent_message_id, wrong tool-result message role) that
were already fixed for hitl-in-chat.

## Test plan
- [ ] GHCR image builds successfully
- [ ] D5 probes pass for llamaindex hitl-approve-deny and hitl-steps
features
2026-04-29 23:20:09 -07:00
Jordan Ritter c1775f37ca fix(showcase): apply FixedAGUIChatWorkflow to all llamaindex HITL agents
PR #4464 only applied the upstream bug fixes to hitl_in_chat_agent.py.
The same three bugs (duplicate tool-call rendering, missing
parent_message_id, incorrect tool-result message roles) affect the
hitl_in_app_agent and the main agent router.

Switch both to use FixedAGUIChatWorkflow via workflow_factory so all
llamaindex demo features get the corrected AG-UI event stream.
2026-04-29 23:19:46 -07:00
Jordan Ritter bafd9f9fab docs(showcase): add production debugging section to RUNBOOK (#4480)
## Summary
- Production debugging section: harness log events, debug-level traces,
manual probe triggering
- Package.json rebuild requirement documentation

## Test plan
- [x] Documentation only
2026-04-29 22:59:37 -07:00
Jordan Ritter 495a1c8b03 docs(showcase): add production debugging section to RUNBOOK
Covers harness probe logging, debug-level conversation traces,
manual probe triggering, and package.json rebuild requirements.
2026-04-29 22:59:26 -07:00
Jordan Ritter c7967dd375 fix: add debug logging to D5 probe execution pipeline (#4479)
## Summary

Adds comprehensive `console.debug` logging to the D5 probe execution
pipeline so production probe failures are diagnosable from logs alone,
without requiring reproduction.

**conversation-runner.ts** (the core multi-turn conversation engine):
- Logs conversation start/end with turn count and settle configuration
- Logs chat input selector cascade resolution -- each selector miss and
the final hit
- Logs per-turn lifecycle: message send, assistant settle wait, message
count changes, assertion execution, and failures with elapsed times
- Logs the fillAndVerifySend retry loop with attempt counts and user
message baseline vs current counts
- Logs waitForAssistantSettled polling with count changes, periodic
status during long waits (~every 5s), and timeout details

**e2e-deep.ts** (the driver that orchestrates per-feature runs):
- Logs page navigation URL and result
- Logs React hydration wait start, success, and timeout
- Logs conversation turn count after buildTurns
- Logs conversation success/failure with diagnostic capture summary

**All 9 D5 scripts** (agentic-chat, gen-ui-custom, gen-ui-headless,
hitl-approve-deny, hitl-steps, hitl-text-input, mcp-subagents,
shared-state, tool-rendering):
- Logs expected tokens/fragments and actual assistant text at each
assertion checkpoint
- Logs DOM element discovery (matched selectors, child counts, card
structure)
- Logs polling progress for long-running assertions (tool card probe,
chained reply fragments)

All logging uses `console.debug(...)` which is suppressed at INFO level
in production but available when operators set `LOG_LEVEL=debug` for
investigation.

## Test plan

- [x] All 1372 existing tests pass (`cd showcase/harness && npx vitest
run` -- 70 test files, 0 failures)
- [x] Debug output is visible in test stdout (vitest captures it
per-test)
- [x] No functional changes -- all logging is additive `console.debug`
calls
2026-04-29 22:56:30 -07:00
Jordan Ritter 1320abb40f fix: add comprehensive debug logging to D5 probe execution pipeline
Instrument the conversation runner, e2e-deep driver, and all D5 scripts
with console.debug logging at every decision point so production probe
failures are diagnosable without reproduction.

conversation-runner.ts:
- Log conversation start/end with turn count and settle config
- Log chat input selector cascade resolution (each miss and final hit)
- Log per-turn lifecycle: message send, settle wait, count changes,
  assertions, and failures with elapsed times
- Log fillAndVerifySend retry loop with attempt counts and user message
  baseline/current counts
- Log waitForAssistantSettled polling with count changes, periodic
  status during long waits, and timeout details

e2e-deep.ts (runFeature):
- Log page navigation URL and result
- Log React hydration wait (start, success, timeout)
- Log conversation turn count after buildTurns
- Log conversation success/failure with diagnostics

All D5 scripts (d5-agentic-chat, d5-gen-ui-custom, d5-gen-ui-headless,
d5-hitl-approve-deny, d5-hitl-steps, d5-hitl-text-input,
d5-mcp-subagents, d5-shared-state, d5-tool-rendering):
- Log expected tokens and actual assistant text at each assertion
- Log DOM element discovery (selectors matched, child counts)
- Log polling progress for long-running assertions (tool card probe,
  chained reply fragments)
2026-04-29 22:56:00 -07:00
Jordan Ritter 46ab39b593 fix(showcase): scope D5 text extraction to prose div, not full message (#4478)
## Summary

- **Root cause**: `readLastAssistantText` was reading `textContent` from
the entire `[data-testid="copilot-assistant-message"]` wrapper, which
includes rendered tool-component output (PieChart SVG labels like
`Electronics42,000Clothing28,000...`) alongside the actual assistant
chat text
- Scopes the text extraction to the prose/markdown child div
(`[class*="prose"]`) inside the canonical CopilotKit message DOM
structure, which contains only the assistant's text response
- Falls back gracefully for headless/custom-composer demos that don't
use the canonical DOM layout
- Adds debug logging (`[readLastAssistantText]`) so production traces
show which DOM element was selected and what text was extracted

## Test plan

- [x] All 1372 harness unit tests pass (`npx vitest run`)
- [x] TypeScript compiles cleanly (`npx tsc --noEmit`)
- [ ] Local D5 test against langgraph-python gen-ui-custom passes
- [ ] Production D5 probe for langgraph-python stops flapping
2026-04-29 22:53:17 -07:00
Jordan Ritter 1834ffe2d0 fix(showcase): scope D5 text extraction to prose div, not full message
readLastAssistantText was reading textContent from the entire assistant
message wrapper, which includes rendered tool-component output (SVG
chart labels like "Electronics42,000Clothing28,000...") in addition to
the actual chat text. This caused gen-ui-custom probes to fail token
assertions because the extracted text was the rendered PieChart content
instead of the follow-up narration.

Scope the extraction to the prose/markdown child div inside the
canonical CopilotKit message structure, which contains only the
assistant's text response. Falls back gracefully when the expected
DOM structure isn't present (headless/custom composers).

Also adds debug logging so production traces show which element was
selected and what text was extracted.
2026-04-29 22:52:52 -07:00
Tyler Slaton 5fe7ec55ca Merge branch 'main' into tyler/kind-mendeleev-636369 2026-04-29 22:46:51 -07:00
Jordan Ritter 6c5304dff7 fix(runtime): stop converting TanStack stream after RUN_FINISHED (#4476)
## Summary

- Fixes built-in-agent D5 failures caused by duplicate `TOOL_CALL_END`
events from TanStack's internal tool execution loop
- TanStack's `chat()` engine runs a multi-turn agent loop that re-emits
`TOOL_CALL_END` for frontend-only tools (like `render_pie_chart`)
without a preceding `TOOL_CALL_START`, violating the ag-ui verify
middleware
- Adds `runFinished` flag to `convertTanStackStream` to discard all
events after the first `RUN_FINISHED`
- Adds built-in-agent to `docker-compose.local.yml` and
`local-ports.json`

## Test plan

- [x] Runtime test suite passes (1412/1412 tests, 101 files)
- [x] Container hotpatch verified: no more duplicate TOOL_CALL_END
errors in logs
- [x] D5 hitl-approve-deny now passes (was failing due to this bug)
- [x] aimock v1.16.3 released with complementary item_id fix
2026-04-29 22:42:22 -07:00
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