Commit Graph

533 Commits

Author SHA1 Message Date
Max Korp 5fdc07dfb9 Reapply "fix: catch async errors in LangGraphAgent.run() Observable"
This reverts commit ca233aea20.
2026-04-09 13:01:40 -07:00
github-actions[bot] 1bc4786759 chore: version packages (next) 2026-04-09 18:09:30 +00:00
Tyler Slaton 62c604213c chore(a2ui-renderer): bump version for a2ui-middleware to 0.0.4
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-04-09 11:05:10 -07:00
github-actions[bot] 0093bdbaa4 chore: version packages 2026-04-09 02:26:42 +00:00
github-actions[bot] c2837a3001 chore: version packages (next) 2026-04-09 01:53:20 +00:00
Tyler Slaton 422f5f55e1 fix(open-gen-ui): correctly import clairinet
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
2026-04-08 18:09:03 -07:00
github-actions[bot] 00cace6abf chore: version packages 2026-04-08 23:54:54 +00:00
Ben Taylor ca233aea20 Revert "fix: catch async errors in LangGraphAgent.run() Observable" 2026-04-08 18:40:52 -05:00
Max Korp c5d712f4cf test(runtime): add unit tests for LangGraphAgent.run() async error handling
Verify that async rejections from runAgentStream are routed through the
Observable error channel instead of becoming unhandled promise rejections.
2026-04-08 16:17:55 -07:00
Max Korp cffc4cafe0 fix: catch async errors in LangGraphAgent.run() Observable
The base class @ag-ui/langgraph creates an Observable that calls
runAgentStream() but ignores the returned Promise. Any async rejection
becomes an unhandled promise rejection that crashes the process.

Override run() to properly route async errors through the Observable
error channel so downstream catchError handlers can process them.
2026-04-08 16:17:55 -07:00
Tyler Slaton 02b2638433 fix: generate runtime-client-gql types before tests 2026-04-08 15:32:49 -07:00
Ben Taylor 9181bf9ae0 fix: use plain UUID for thread-naming runs (#3664)
The thread naming creates an additional run, using a thread name that
was something like
"thread-naming:${originalthreadiduuid}:${newrandomuuid}" which
shockingly is not a valid uuid. This just drops the first 2 parts and
keeps the random uuid so langgraph can stop being mad.
2026-04-08 16:22:23 -05:00
Maxim 9ee8e92766 chore: resolve merge conflicts with origin/main
Keep both defaultThrottleMs (our branch) and inspectorDefaultAnchor +
design-skill context registration (main) in CopilotKitProvider.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:58:45 +02:00
Max Korp 2142ecea99 fix: use plain UUID for thread-naming runs 2026-04-08 13:52:06 -07:00
Maxim 7a99322e77 fix: harden throttleMs cascade — setter validation, timing fix, JSDoc accuracy, tests
- Core setter rejects invalid values (NaN, Infinity, negative) instead of
  storing them, preventing garbage from reaching downstream consumers.
- Provider initializes defaultThrottleMs synchronously during instance
  creation so child hooks see the correct value on their first render.
- Remove phantom notificationThrottle JSDoc reference (API does not exist).
- Remove dead "default" branch in error source detection.
- Fix misleading @default comments re: 0-vs-undefined semantics and
  cascade direction.
- Replace React-specific "re-renders" language in framework-agnostic core.
- Add test for dynamic provider defaultThrottleMs changes.
- Add tests for core setter validation behavior.
- Add defaultThrottleMs to renderWithCopilotKit test helper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:05:22 +02:00
Markus Ecker a5c1137465 chore: update ag-ui deps and web-inspector styles 2026-04-08 12:51:07 -07:00
Markus Ecker 71c77d4072 feat(cli): add a2ui and opengenui scaffold options 2026-04-08 12:50:37 -07:00
Markus Ecker e6988014f8 feat(runtime): OpenGenerativeUI middleware + A2UI config cleanup
Add OpenGenerativeUIMiddleware (clarinet streaming JSON parser, held event
buffering, activity snapshot/delta conversion), remove a2ui boolean shorthand,
includeBasicCatalog defaults to false, before_agent SystemMessage ID reuse fix.
2026-04-08 12:50:18 -07:00
Markus Ecker 05ddcfc12a feat(react-core): Open Generative UI — sandboxed HTML/CSS/JS rendering via iframe
AI-generated UI streamed into chat via websandbox iframe (allow-scripts only,
no allow-same-origin), progressive HTML preview with throttled updates,
one-shot auto-resize, sandboxFunctions for host-to-iframe calls, designSkill
styling guidelines, A2UI catalog context injection.
2026-04-08 12:49:38 -07:00
Markus Ecker 93d0a76ddc feat(a2ui-renderer): A2UI v0.9 — BYOC catalogs, dark mode, component-neutral guidelines
Replace @a2ui/lit with @a2ui/web_core 0.9, introduce createCatalog() API for
custom component catalogs, add basic (18 components) and minimal (5 components)
built-in catalogs, CSS variable theming for dark mode, eliminate XSS vector
(dangerouslySetInnerHTML removed from Text component).
2026-04-08 12:48:47 -07:00
Maxim cb7bfae188 refactor: move defaultThrottleMs from React context onto CopilotKitCore instance
Addresses Tyler's review comment on #3657 — defaultThrottleMs is configuration
that belongs on the copilotkit instance, not as a sibling field in the React
context value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:44:47 +02:00
Maxim a8e05401e7 fix: improve throttleMs validation, JSDoc accuracy, and test coverage
- Add source attribution to error messages (hook-level vs provider-level)
- Add eager validation of defaultThrottleMs in CopilotKitProvider
- Add tests for invalid provider defaultThrottleMs (NaN, Infinity, -1)
- Remove redundant resolved !== 0 guard in validation
- Fix JSDoc: "streaming" → "message change notifications", "passed
  directly" → "forwarded", align @default terminology
- Extract shared mock helpers to reduce test duplication

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:31:28 +02:00
Maxim c8db6476a8 test: verify CopilotSidebar/CopilotPopup inherit throttleMs type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:14:51 +02:00
Maxim a7c57cef9c feat: add throttleMs prop to CopilotChat, forwarded to internal useAgent
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:11:46 +02:00
Maxim e30d27b46b test: guard throttleMs:0 override of provider defaultThrottleMs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:07:57 +02:00
Maxim af98091bda feat: add defaultThrottleMs to CopilotKitProvider, read as fallback in useAgent
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:49:57 +02:00
Maxim 8a1f111602 Merge branch 'main' into perf/chat-input-cache-container-measurements 2026-04-08 19:38:34 +02:00
Maxim ad4271c266 test(react-core): extract shared helpers and document casts in cache tests
Extract renderAndWarmCache() and installBoundingRectSpy() helpers to
eliminate duplicated setup across 4 tests. Add justifying comments on
all necessary type casts (MockResizeObserver, CSSStyleDeclaration,
TextMetrics, canvas getContext overloads).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:22:33 +02:00
Maxim 35e35bb5bc fix(react-core): preserve cache across layout toggles and reject zero-width entries
Self-inflicted resizes (compact↔expanded) were clearing the cache before
the ignoreResizeRef guard, forcing a full re-measurement on the very next
keystroke. Move the guard ahead of invalidation so layout toggles keep the
cache warm — container dimensions don't change during these transitions.

Also reject compactWidth <= 0 from being cached, preventing a zero-width
entry from silently disabling text-width expansion until the next resize.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:54:25 +02:00
Maxim 84d531c1e3 fix: address review feedback — single observer, fresh font read, test coverage
- Merge two ResizeObserver instances into one; inspect entry.target to
  decide cache invalidation.  Eliminates the ignoreResizeRef race where
  two observers could consume the guard independently.
- Move textarea font read out of the cache into evaluateLayout so CSS/
  theme changes are picked up without a container resize.
- Return { compactWidth } | null from updateContainerCache; callers use
  the value directly instead of the two-step "call then check ref" pattern.
- Hoist cache invalidation above the ignoreResizeRef guard to remove
  duplicated containerCacheRef.current = null.
- Add zero-width guard in updateContainerCache to avoid caching
  compactWidth: 0 when the container is hidden.
- Replace vi.fn() as any ResizeObserver mock with a class-based
  MockResizeObserver that tracks observed targets, enabling per-target
  resize triggers in tests.
- Fix empty-font and null-canvas tests to assert data-layout="compact"
  (verifying the fallback path) instead of just non-null.
- Add tests: warm-cache keystroke perf (getBoundingClientRect not called),
  textarea-only vs container resize invalidation, ignoreResizeRef + cache
  invalidation during layout toggle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:39:22 +02:00
Maxim f35df00b16 test(react-core): improve test hygiene in container dimension cache tests
Extract shared helpers (DEFAULT_LAYOUT_OPTIONS, getLayoutGrid,
renderTypeAndExpectLayout) to eliminate duplication, add explanatory
comments for unavoidable `as any` casts, and add missing test for
canvas.getContext returning null.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:49:24 +02:00
Maxim 8da887c14c fix(react-core): harden container dimension cache in CopilotChatInput
- Split ResizeObserver into container vs textarea observers so textarea
  height changes (typing, manual resize) don't invalidate the dimension
  cache unnecessarily — only grid/button resizes invalidate it
- Invalidate cache on self-triggered resizes (compact↔expanded toggle)
  even when skipping re-evaluation, preventing stale dimensions
- Validate fontSize and fontFamily before constructing font fallback
  string, avoiding malformed CSS font values in Safari
- Add dev-mode console.warn when font resolution or canvas context
  fails, making silent measurement path failures diagnosable
- Update test ResizeObserver mock to support multiple observers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:36:47 +02:00
Maxim 27bde85b6f test(react-core): add tests for container dimension cache in CopilotChatInput
- Test canvas text measurement triggers expansion for long single-line text
- Test compact layout preserved when text fits within cached width
- Test cache invalidation on resize produces correct re-measurement
- Test empty font string is not cached (guard prevents invalid canvas state)
- Mock ResizeObserver to exercise cache invalidation path in jsdom
- Extend mockLayoutMetrics with getComputedStyle mocks for grid and textarea

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 23:10:10 +02:00
Maxim 8986a47220 fix(react-core): harden container dimension cache in CopilotChatInput
- Store compactWidth (pre-padding) in cache, compute compactInnerWidth
  at read time from live measurementsRef to avoid stale padding values
- Move cache invalidation after ignoreResize check so programmatic
  layout transitions don't needlessly destroy the cache
- Guard against empty/invalid font strings before writing to cache
- Fix comment inaccuracies: 2x (not 3x) getComputedStyle, lazily
  populated (not on mount), invalidated (not updated) on resize

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:56:12 +02:00
Maxim d9125fc076 perf(react-core): cache container dimensions in CopilotChatInput to reduce per-keystroke reflows
evaluateLayout runs on every keystroke via useLayoutEffect. Previously it
performed 3x getComputedStyle + 2x getBoundingClientRect on every call to
measure grid/button dimensions and the textarea font — none of which change
between keystrokes.

Cache these measurements in a ref (containerCacheRef), populated lazily on
first evaluateLayout call and invalidated when the ResizeObserver fires.
Per-keystroke reflows drop from 3-4 to 1 (the unavoidable adjustTextareaHeight).

Also removes a stale console.log("FOOBAR") debug line.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:24:17 +02:00
Martha Schumann 3e2b1a8915 fix(cli): bump tar to v7 to resolve npm deprecation warning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:48:32 -07:00
Martha Schumann 1a61be49dc refactor(FOR-75): replace useJsonStable with useShallowStableRef; add unit tests and changeset
- Kill useJsonStable in CopilotChatConfigurationProvider — useShallowStableRef
  is now the single stabilization primitive for both messageView props and labels
- Harden useShallowStableRef with isPlainObject guard so arrays, Dates, and
  class instances are never shallow-compared (reference-only for non-plain objects)
- Remove redundant stableParentLabels — parentConfig?.labels is already stabilized
  by the parent provider's own useShallowStableRef
- Uninstall ts-deepmerge from react-core dependencies (no longer imported anywhere)
- Add unit tests for useShallowStableRef in slots.test.ts (5 tests)
- Add changeset (patch) describing the FOR-75 performance fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 11:00:19 -07:00
Martha Schumann 56cc194782 refactor(react-core): extract useShallowStableRef; fix Test 2 to guard labels regression
- Extract useShallowStableRef<T> into slots.tsx alongside shallowEqual —
  replaces the 20-line inline stabilization block in CopilotChat with two
  readable call sites, and covers suggestionView for free
- Apply useShallowStableRef to both messageView and suggestionView in CopilotChat
- Use post-hoc assignment for messageView to avoid an empty object allocation
  on every render when messageView is undefined
- Rewrite Test 2 (labels) to use LabelConsumerMessage which calls
  useCopilotChatConfiguration() directly — context consumers re-render when
  their context changes regardless of parent memo boundaries, making this a
  genuine regression guard for the labels fix (previous version tracked
  assistantRenderCount which was already protected by messageView stabilization)
- Replace `as any` on messageView with scoped cast to preserve structural
  type checking on the surrounding object
- Fix text/testid collision: CountingAssistantMessage now renders data-testid
  instead of a hardcoded string that matched the agent reply text

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:41:46 -07:00
Martha Schumann 35481074bd refactor(react-core): replace eslint-disable comments with useJsonStable hook
Replaces the manual JSON.stringify dep-key pattern (which required three
eslint-disable-next-line comments) with a useJsonStable utility that
stabilizes object references using JSON comparison. The dep array for
mergedLabels is now honest — it references the exact variables used inside
the useMemo callback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:30:26 -07:00
Martha Schumann cdbc3457cf fix(react-core): stabilize messageView/labels props to prevent message re-renders on keystroke
Inline `messageView` and `labels` objects passed to CopilotChat caused all
completed assistant messages to re-render on every keystroke because:

1. `ts-deepmerge.merge()` deep-clones its inputs, producing a new `messageView`
   reference on every render even when content is identical — defeating
   `MemoizedSlotWrapper`'s shallow equality check.

2. An inline `labels` object (new reference each render) invalidated the
   `mergedLabels` useMemo in `CopilotChatConfigurationProvider`, causing
   every `useCopilotChatConfiguration()` consumer to re-render on every keystroke.

3. The inline `onAddFile` arrow function was a new reference each render.

Fixes:
- Replace `merge()` with shallow spread; stabilize `messageView` via a ref +
  `shallowEqual` guard so the same object reference is returned when props
  are shallowly equal across renders.
- Use `useCallback` for `handleAddFile`.
- Add JSON.stringify-based dep keys in `CopilotChatConfigurationProvider` so
  `mergedLabels` is only recomputed when label values actually change.

Adds deterministic render-count regression tests (FOR-75) that fail on the
unfixed code and pass after the fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 09:55:34 -07:00
Martha Kelly Schumann 30b81395f6 Merge branch 'main' into feat/CPK-7190-chat-performance 2026-04-07 08:51:33 -07:00
Martha Schumann 82d58fae9b chore: merge main into feat/CPK-7190-chat-performance
Resolves conflict in CopilotChat.tsx — takes main's attachment-aware
messagesMemoKey fingerprint (scans all messages, avoids JSON.stringify
on base64 data) which subsumes our all-tool-calls fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 08:35:29 -07:00
Alem Tuzlak 39b5f671c0 Merge branch 'main' into claude/remove-endpoint-flag-xd3G1 2026-04-07 14:47:09 +02:00
Alem Tuzlak 4b11b995a2 feat: multimodal attachments — images, audio, video, documents in CopilotChat (#3604)
## Summary

Adds full multimodal attachment support to CopilotKit's v2 chat
components. Users can send images, audio, video, and documents alongside
text messages to any AG-UI-compatible agent.

### Frontend
- **`attachments` prop** on `CopilotChat` — `{ enabled, accept, maxSize,
onUpload, onUploadFailed }`
- **`useAttachments` hook** — reusable, referentially stable hook for
custom chat UIs (all callbacks memoized, zero unnecessary re-renders)
- **Attachment queue** with lightbox previews (View Transition API),
video player, PDF/text document viewer
- **Drag-and-drop**, **clipboard paste**, and **file picker** upload
flows
- **Drop zone overlay** with visual indicator
- **Filename preservation** via `InputContent` metadata
- **`onUploadFailed` callback** with typed error reasons
(`file-too-large`, `invalid-type`, `upload-failed`)
- **`AttachmentUploadResult` discriminated union** for type-safe
`onUpload` returns
- Memoized attachment renderer components (v1 and v2)

### Backend
- `BuiltInAgent` properly converts multimodal `InputContent[]` to Vercel
AI SDK format
- Handles image, audio, video, document, and legacy binary content parts

### Deprecation lifecycle
- `@deprecated` JSDoc on all legacy image upload APIs (9 symbols) with
`@since 1.56.0`
- **Codemod** at `codemods/migrate-attachments.ts` — transforms props +
imports (18 tests)
- Migration guide moved to new **Migration Guides** docs section

### Docs
- New guide: `docs/(root)/multimodal-attachments.mdx`
- Updated migration guide with codemod instructions, new `onUpload`
type, metadata
- Cross-links from prebuilt-components page
- New **Migration Guides** section (moved from troubleshooting)

### AG-UI
- Bumped `@ag-ui/client`, `@ag-ui/core`, `@ag-ui/encoder`,
`@ag-ui/proto` to 0.0.51

### Example
- React Router example with dual agent setup (BuiltInAgent + TanStack
AI)
- Multimodal content properly forwarded to TanStack AI `chat()`

## Test plan

- [x] `useAttachments` hook stability tests — 9 tests (referential
stability, re-render counting, state defaults)
- [x] `CopilotChat.attachments` tests — 5 tests (onUploadFailed for
invalid-type, file-too-large, upload-failed, multiple rejections, valid
files)
- [x] Codemod tests — 18 tests (prop transforms, import renames,
idempotency, edge cases)
- [x] Full react-core suite — 966 tests passing
- [x] Tool call streaming tests pass (messagesMemoKey fingerprint fix)
2026-04-07 14:26:03 +02:00
Ran Shemtov 0a262b3ad6 Merge branch 'main' into claude/remove-endpoint-flag-xd3G1 2026-04-07 12:55:33 +02:00
Ran Shem Tov 51bcc94988 chore: fix failing tests 2026-04-07 12:47:08 +02:00
Alem Tuzlak 773a4a6415 fix: format package.json files 2026-04-07 12:29:29 +02:00
Alem Tuzlak 0aca4e88bc fix: address review feedback — concurrent-safe consumeAttachments & scope-aware codemod
1. consumeAttachments: read from a ref mirror instead of side-effecting
   out of a setState updater, avoiding reliance on synchronous updater
   execution under React concurrent mode.

2. Codemod: skip declaration positions (variable, function, class, type,
   interface) and non-reference positions (object keys, member accesses).
   When a local declaration shadows the import name, only rename
   unambiguous type-position references to avoid corrupting unrelated code.
2026-04-07 12:14:14 +02:00
Alem Tuzlak 1317e51302 fix: revert copy-dts script, use typesVersions for legacy TS consumers
Reverts the copy-dts.mjs approach from #3612 in favor of typesVersions
which is the standard Node/TS mechanism for resolving subpath types
under legacy moduleResolution: "node".
2026-04-07 11:39:15 +02:00
Alem Tuzlak 136bfc1182 Merge branch 'main' into feat/multimodal-builtin-agent 2026-04-07 09:51:46 +02:00