Commit Graph

102 Commits

Author SHA1 Message Date
MikeRyanDev 69861f13df chore: release monorepo v1.63.2 2026-07-23 16:15:51 +00:00
Ben Taylor 7fb0abf60e fix(vue): keep attachment sources structuredClone-safe (#5933)
## What does this PR do?

Fixes a Vue `DataCloneError` that occurred when uploaded attachment
sources crossed the `structuredClone` boundary in core. Vue’s deep
`ref()` conversion wrapped nested attachment sources in reactive
proxies; `useAttachments` now keeps the attachment container shallow
with `shallowRef()`, preserving externally supplied sources as raw
cloneable values before they reach AG-UI/core payloads.

The change is intentionally Vue-only: core and React are untouched
because the defect is caused by Vue’s reactivity behavior at the
framework boundary. Focused regressions cover both the attachment hook
and `CopilotChat` submission path, including non-reactivity and
successful `structuredClone` behavior.

## Related PRs and Issues

- [CopilotKit issue
#3](https://github.com/enekesabel/CopilotKit/issues/3)

## Verification

- `pnpm nx run @copilotkit/vue:check-types` — passed.
- `pnpm nx run @copilotkit/vue:test --
src/v2/hooks/__tests__/use-attachments.test.ts
src/v2/components/chat/__tests__/CopilotChat.attachments.test.ts` —
passed, 18 tests in 2 files.
- `pnpm nx run @copilotkit/vue:build` — passed.
- Pre-commit package gate (`test-and-check-packages`) — passed: 1073
tests, publint, and attw.
- `pnpm nx run @copilotkit/vue:lint` — remains blocked by 171
pre-existing errors across unrelated Vue files; no lint errors were
introduced in the changed files.
- `git diff --check upstream/main...HEAD` — passed.

## Scope and exclusions

- Changed files are limited to
`packages/vue/src/v2/hooks/use-attachments.ts`, its focused hook and
`CopilotChat` tests, and the related `packages/vue/PARITY.md` and
`packages/vue/AGENTS.md` guidance.
- No core, React, workflow, or package-wide lint cleanup is included.
- The `PARITY.md` change removes accidental table-format churn and
retains only the meaningful attachment parity note.

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/CopilotKit/CopilotKit/blob/main/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
2026-07-20 11:43:28 -05:00
tylerslaton a7459f4fb2 chore: release monorepo v1.63.1 2026-07-16 18:23:58 +00:00
tylerslaton 6c354037fc chore: release monorepo v1.63.0 2026-07-15 22:18:07 +00:00
enekesabel 4f29932ca8 fix(vue): keep attachment sources structuredClone-safe 2026-07-13 11:35:07 +02:00
tylerslaton 4394f9c81d chore: release monorepo v1.62.3 2026-07-08 16:17:36 +00:00
Benjamin Taylor 8bfd245305 feat(web-components,react,vue,angular): restore collapse + drawer-centered modal [ENT-1051]
Design iteration (Ben's designer):

- RESTORE desktop collapse. Re-add collapsed/collapsible + collapse-change
  (element + all three wrappers, lockstep), the desktop header collapse toggle,
  and the CollapseChangeDetail type/re-export. Default is EXPANDED.
- UNIFY the closed affordance into one floating cluster (Figma 'closed' mockup):
  a sidebar-glyph toggle + a New Conversation (+) icon button, shown in BOTH the
  mobile-closed state (adds New Conversation to the old single launcher) and the
  desktop-collapsed state. Parts: launcher-cluster, launcher, launcher-new-thread.
- COLUMN RECLAIM (no empty reserved gap): on desktop-collapse the element sets
  --cpk-drawer-reserved-width: 0px on the document root (reaches the grid past
  the wrapper host via :root inheritance); hosts read it in grid-template-columns.
  Default expanded never sets it, so no hydration flicker.
- DELETE MODAL centered over the DRAWER PANEL, not the viewport: keeps the
  top-layer showModal() robustness (never clipped) but drives --confirm-cx/cy
  from the visible .root rect and caps width to the drawer band.
- Vue fix: default collapsible to true in the wrapper. Vue coerces an omitted
  Boolean prop to false, which was silently forcing collapsible=false (collapse
  toggle vanished) — React/Angular pass undefined and keep the element default.

Validated live in the Nuxt (Vue) and Angular demos against managed Intelligence:
collapse/expand, cluster + New Conversation, column reclaim, drawer-centered
top-layer modal, mobile cluster. Tests: web-components 89, react-core 1419,
vue 32, angular 34 — all green.
2026-07-07 08:35:22 -05:00
Benjamin Taylor 0d551822dd fix(vue): externalize @copilotkit/web-components in the library build [ENT-1051]
The drawer wrapper loads the Lit `<copilotkit-threads-drawer>` element via a
client-only dynamic import of @copilotkit/web-components/threads-drawer. That
package was not in rollupOptions.external, so vite bundled the whole element +
a second copy of lit-html inline into the vue dist (a ~1MB chunk).

Under Vite/Nuxt consumers this inlined duplicate lit-html produced a fatal
"Identifier 'h' has already been declared" parse error (h = lit-html's minified
`html`), and it also risks double custom-element registration. Externalizing it
(as @copilotkit/core, @copilotkit/shared, @copilotkit/web-inspector already are)
makes the dynamic import resolve to the single real package at runtime.

Verified live: the redesigned drawer now renders + operates in the Nuxt vue demo
(licensed, real threads, kebab Archive/Delete, top-layer delete dialog, mobile
off-canvas + sidebar-glyph close). Vue wrapper unit tests: 29/29 pass.
2026-07-06 17:14:40 -05:00
Benjamin Taylor 01d5850ae8 refactor(web-components,react,vue,angular): drop desktop collapse from threads drawer [ENT-1051]
The thread panel is a persistent always-visible sidebar on desktop; the Figma
"closed" mockup is the MOBILE state, already covered by off-canvas behavior.

- web-components: remove the `collapsed`/`collapsible` properties,
  `_toggleCollapsed`, the header collapse-toggle button, and the
  collapsed-cluster render branch; render() always paints the full panel body.
  Gate the now control-less header on a `_hasHeader` slotchange flag so no empty
  bar renders. Drop the unused `iconSidebar`, the `CollapseChangeDetail` type +
  `collapse-change` event-map entry, the index re-export, and the
  `.root.collapsed`/`.collapsed-cluster` styles.
- react: drop the `collapsible` prop + property assignment, the
  `onCollapseChange` prop + `collapse-change` listener/handler, and the local
  `CollapseChangeDetail` type.
- vue: drop the `collapsible` prop + element binding, the `collapse-change`
  emit + `@collapse-change` handler, and the local `CollapseChangeDetail` type.
- angular: drop the `collapsible` input + property push, the `collapseChange`
  @Output + event wiring (and now-unused EventEmitter/Output imports), and the
  local `CollapseChangeDetail` type.
- tests: remove all collapse tests across the four packages; add an element
  header-gating test. Mobile off-canvas (open-driven) behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:15:58 -05:00
Benjamin Taylor 9c4dcabd52 feat(react,vue,angular): remove CopilotThreadsDrawer search passthrough [ENT-1051]
Also fixes the collapsible-default test assertion (element defaults collapsible=true,
mirroring licensed; the prior undefined assertion only passed vs a stale dist).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 13:44:20 -05:00
Benjamin Taylor c6af37f9fa feat(react,vue,angular): CopilotThreadsDrawer collapsible + onCollapseChange passthrough [ENT-1051]
Mirror the existing recentLabel (passthrough) and onSearch (element-event)
props with two additive props targeting the element's forthcoming
`collapsible` property and `collapse-change` event.

- react: add `collapsible?: boolean` (pushed as an element PROPERTY, like the
  default-true boolean `licensed`) and `onCollapseChange?: (collapsed) => void`
  (wired via the handler-ref addEventListener block, like onSearch).
- vue: add `collapsible?: boolean` (imperative property push in the
  watchEffect, like `licensed`) and re-emit the element's `collapse-change`
  event as `collapse-change(collapsed)` (matching the `search` emit convention).
- angular: add a `collapsible` signal input (property push in the effect, like
  `licensed`) and `@Output() collapseChange = new EventEmitter<boolean>()`
  wired from the element's `collapse-change` event (like `search`).

CollapseChangeDetail is declared locally in each wrapper with a TODO to switch
to the package export once the parallel element PR that adds the collapse
feature lands and is published (the built element types in this worktree
predate it).

Testing: added mirrored tests per framework (property-set + event-passthrough);
full nx test suites green (react-core 1420, vue 1070, angular 178); check-types
and build green for all three packages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 13:13:55 -05:00
Benjamin Taylor ddcb4764ca fix(web-components,react,vue,angular): CR round 2 — open=false default + dead-CSS/comment/test cleanup [ENT-1051]
Bucket A — mobile open-flash:
- Default the drawer element's `open` property to `false`. On a mobile viewport
  the previous `open = true` default made the first render satisfy
  _isMobileModalOpen(), painting the modal + body scroll-lock + focus steal for
  one frame before any wrapper effect could close it. Desktop is unaffected
  (only .root.mobile.open / _isMobileModalOpen() consume `open`).
- Element tests: added an `open` option to setup(); updated the 6 mobile/desktop
  tests that relied on the old open=true default to opt in explicitly; added a
  regression test asserting a fresh mobile element defaults open=false and paints
  no backdrop / applies no scroll-lock until opened.

Bucket B — dead CSS + inert `confirming` machinery:
- styles.ts: removed the `.row-action[data-tooltip]` hover/focus tooltip rules
  and the `.root.confirming .row-action[data-tooltip]…` suppression rules — no
  rendered .row-action carries data-tooltip anymore (row actions moved into the
  kebab menu as labeled .row-menu-items). Kept the .row-action base rules (still
  used by the confirm-dialog Cancel button + fetch-more retry).
- element: removed the now-inert `confirming` root class (it gated only the
  dead CSS above) and its stale comment; deleted the test asserting the no-op
  tooltip suppression. Refreshed two comments that referenced the removed
  row-action tooltip lineage.

Bucket B — comment/test hygiene:
- react-core CopilotThreadsDrawer.tsx: reworded the imprecise event-rebind
  comment to describe the actual [mounted] deps.
- angular spec: added a beforeEach resetting the module-level threadsState
  signals + clearing mock fns to remove order-coupling (parity with react/vue).
- vue use-threads.test.ts: aligned MockThreadStore.unarchiveThread + its
  assertion to the real core contract (PATCH /threads/{id} { archived:false },
  not POST /threads/{id}/unarchive).

Verified: web-components (89), vue (1068), angular (176), react-core suites all
green; web-components + react-core builds green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 11:40:52 -05:00
Benjamin Taylor 9407cfdd7d fix(core,react,vue,angular): CR round 1 — fetchMoreError + Angular open-state/focus + parity nits [ENT-1051]
D2 — wire fetchMoreError end-to-end:
- core: add a dedicated `fetchMoreError` channel to the thread store, tracked
  separately from the initial-list `error`. `nextPageFailed` now writes
  `fetchMoreError` (was `error`), so a paginated-load failure preserves the
  loaded list and drives the element's inline "couldn't load more — retry"
  panel instead of a full-panel error. Cleared on fetch-more request (retry),
  on success, and reset on context change / stop. New symbols:
    * `ThreadState.fetchMoreError`
    * `ThreadSelectors.fetchMoreError` + `ɵselectFetchMoreError`
  Call sites of `ɵselectFetchMoreError`:
    * packages/core/src/threads.ts (export)
    * packages/react-core/src/v2/hooks/use-threads.tsx (selector read)
    * packages/vue/src/v2/hooks/use-threads.ts (bindThreadStoreSelector)
    * packages/angular/src/lib/threads.ts (bridge to signal)
    * packages/vue/src/v2/hooks/__tests__/use-threads.test.ts (core mock)
  Call sites of `ThreadSelectors.fetchMoreError` (mock objects updated):
    * packages/core/src/__tests__/core-thread-store-auto-unregister.test.ts
    * packages/core/src/__tests__/thread-store-registry.test.ts
- react/vue/angular: expose `fetchMoreError` on the hook/composable/store and
  push it onto `el.fetchMoreError`, making the dead `retry{scope:"fetch-more"}`
  handler reachable. Initial-list error behavior unchanged.

D1 — Angular wrapper open-state coordination: drive `el.open` from the config's
`drawerOpen` (default CLOSED) so the element no longer springs open full-screen
and scroll-locks on mobile load; handle `(open-change)` -> `config.setDrawerOpen`
with a provider-less local-state fallback; call `config.registerDrawer()` with
cleanup on destroy. The config's drawer members were fully functional (only
marked "RESERVED/unwired") — wiring them makes them consumed, so their comments
were updated accordingly (no reservation conflict).

D3 — Angular focus-return: add a `findChatInput` scoped to the Angular chat
selectors (`copilot-chat-view` container, `textarea[copilotChatTextarea]`) and
focus it on thread select, mirroring React/Vue.

A6 — react wrapper comment rot: "nine outbound events" -> "eleven" (2 spots).

DEFAULT_AGENT_ID parity (react): import `DEFAULT_AGENT_ID` from
`@copilotkit/shared` instead of hardcoding `"default"` (equal value).

Angular test isolation: three list-path tests now set `licenseStatusSignal`
explicitly instead of relying on inherited module-level state.

Tests: core 552, react-core 1417, vue 1068, angular 176 — all pass;
check-types passes for all four packages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 11:12:29 -05:00
Benjamin Taylor a0487e82aa feat(vue): CopilotThreadsDrawer recentLabel + search parity [ENT-1051]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 09:50:04 -05:00
BenTaylorDev a2cabd9455 chore: release monorepo v1.62.2 2026-07-02 22:23:11 +00:00
Ben Taylor 94379db14a feat(vue): CopilotThreadsDrawer wrapper (Threads v2 Vue vertical) (#5792)
## Summary

Adds a public **Vue `CopilotThreadsDrawer`** — a thin wrapper over the
shared `<copilotkit-threads-drawer>` Lit element — bringing the
Threads-v2 drawer to Vue at parity with the shipped React and Angular
verticals. This is the Vue fast-follow tracked in the [CopilotDrawer —
Vue wrapper](https://app.notion.com/p/3883aa38185281fc8a2cfb69cd1dd88e)
spec.

**SDK-only PR** (`packages/vue`). Like the Angular vertical, the example
de-fork / demo route is a **separate follow-up** — the Vue examples
can't consume the drawer until `@copilotkit/web-components` + a
`react-core` release ship *and* the examples support managed
Intelligence.

Recon during planning corrected the spec's original "cheapest/thinnest
wrapper" premise: Vue is thin on element interop (native
`isCustomElement` + `v-bind`/`v-on`) but — exactly like Angular — needed
an active-thread foundation built. So this ports the full vertical, not
just a binding layer.

## What changed (SDK layers)

- **`useThreads` augmentation** (`hooks/use-threads.ts`): `enabled`
gate, `listError` (genuine list errors, excludes dev/config errors),
`isMutating`, `unarchiveThread` / `refetchThreads` / `startNewThread`;
plus **`registerThreadStore` core-registry integration** and
**`threadEndpoints` list/mutation gating** (React parity).
- **Active-thread + drawer-awareness on
`CopilotChatConfigurationProvider`**: `setActiveThreadId` /
`startNewThread` with the non-explicit-seed override (so
thread-switching + "+ New" work under `<CopilotKit>`), and `drawerOpen`
/ `setDrawerOpen` / `drawerRegistered` / `registerDrawer` with
bidirectional mobile (`<768px`) mutual-exclusion.
- **Clear-on-fresh in `CopilotChat`**: clears the conversation on a
genuine new-thread switch, guarded against initial mount and agent-store
swaps.
- **Mobile launcher in `CopilotModalHeader`**: renders only when a
drawer is registered AND the viewport is mobile.
- **The `CopilotThreadsDrawer.vue` wrapper** + barrel export.
- **SSR safety:** the wrapper imports the `<copilotkit-threads-drawer>`
Lit element **lazily** (`await import(...)` inside `onMounted`,
client-only) rather than at module scope. The element evaluates `class …
extends HTMLElement` at import time, which crashes Nuxt/Vite SSR
(`HTMLElement is not defined`) — a static import would break SSR for
**every** `@copilotkit/vue` consumer, not just drawer users. (Found via
live Nuxt testing; see Testing.)

Load-bearing behaviors carried from the React/Angular rounds: license
gate never flashes the locked view (`licensed || pending`, `loading ||
pending`) and issues no `/threads` fetch while unlicensed; provider-less
`localDrawerOpen` fallback; id-keyed per-row slot reconciliation.

## How it was built & reviewed

Executed via the `micro-task-execution` three-tier model (curator →
decomposition-reviewer → 8 waves, each opus-reviewed) → integration
review → **`cr-loop`: 4 seven-agent review rounds + 7 fix batches,
converged to zero mandatory findings + a Procedure 3 promotion-audit
with zero promotions.** The loop caught and fixed real defects the
per-wave reviews missed: a `useThreads` stuck-loading bug on
`enabled:false`, a wrong chat-input focus `data-testid` (silent a11y
failure), a `CopilotChatToggleButton` fallback regression from the
provider change, net-new lint (`no-dupe-keys`, deprecated `:slot`), and
— surfaced by the promotion audit as load-bearing on the shipped
surfaces — the `registerThreadStore` (inspector visibility) and
`threadEndpoints` gating parity gaps.

## Testing

**Automated (all green in the worktree):**
- `nx run @copilotkit/vue:test` — **99 files / 1061 tests passed**. New
coverage: full `useThreads` augmented surface incl. store registration +
endpoint gating + mutation guards + `enabled` re-arm; the wrapper's
entire 10-event routing table; license pending-vs-resolved gating;
delete-active-thread reset; clear-on-fresh (with a mutation-check
proving it's non-vacuous); bidirectional mobile mutual-exclusion;
`isMobileViewport` guards; the mobile launcher; the
`setModalOpen`-undefined contract that `CopilotChatToggleButton` depends
on.
- `nx run @copilotkit/vue:build` — succeeds (this is the real type gate:
`vue-tsc --declaration`); the compiled `dist` exports
`CopilotThreadsDrawer`.
- `nx run @copilotkit/vue:check-types` — passes.
- `pnpm install --frozen-lockfile` — passes (lockfile in sync).
- Lint: all new/changed source files are clean. (The package-wide `nx
lint` has 173 pre-existing errors, red on `main` and unrelated to this
change.)

Red-green discipline was applied to every behavioral fix (test written
to fail against the bug, then confirmed green after the fix).

**Live-verified via a throwaway hacked example** (uncommitted; the
examples can't ship managed Intelligence yet, so the committed
demo/de-fork is a follow-up): wired the Vue Nuxt demo's runtime to a
managed-Intelligence runtime (`CopilotKitIntelligence` + `licenseToken`,
creds from a CLI scaffold) and loaded `/threads`. Observed: `/info`
reports `mode:"intelligence"`, `licenseStatus:"valid"`,
`threadEndpoints:{list,inspect,mutations,realtimeMetadata: true}`; the
drawer renders the **real thread list from the platform** (licensed —
not the locked or endpoints-unavailable gates), with the Active/All
filter, "New thread", per-row Archive/Delete, and the mobile launcher;
`CopilotChat` renders beside it; no console errors.

This live run is what surfaced the SSR bug above: before the lazy-import
fix, `/threads` (and `/`) 500'd with `HTMLElement is not defined`; after
it, `/threads` → 200 and the error is gone. (Note: the added node-env
regression test `CopilotThreadsDrawer.ssr.test.ts` is a forward-looking
smoke guard — current Lit ships a Node-guarded build so vitest-node
can't reproduce Nuxt's Vite-SSR resolution; the fix's proof is the live
Nuxt run.)

## Release gate

Like the React/Angular de-forks, the wrapper depends on
`@copilotkit/web-components` being published and a
`react-core`/`web-components` release containing the drawer.
`packages/vue` publishes alongside.

## Follow-up work (separate PRs, none blocking)

Tracked in **ENT-1037** (related to ENT-1035): the cross-framework
767/768 mobile-breakpoint reconciliation, `useThreads.startNewThread`
config-error dismissal parity, an identity-guarded
`unregisterThreadStore`, and CopilotChat send/connect error-UX parity.
The Vue examples are intentionally left as-is (no Vue de-fork).

Review feedback (@marthakelly) has been addressed in-branch — see the
review reply.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-01 16:57:00 -05:00
Benjamin Taylor b312fc6ba8 test(vue): real SSR-eager-import guard + row-slot & scoped-focus coverage + typed element reads
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:11:49 -05:00
Benjamin Taylor cdec9c67bb refactor(vue): restore elRef typo-checking, watchEffect for property push, shared mobile-breakpoint constant
- Type elRef as CopilotKitThreadsDrawerElement (drop the `& Record<string, unknown>`
  escape hatch) so every el.<prop> write is checked against the real element type.
- Replace the 12-entry watch([...]) dependency array with watchEffect, which
  auto-tracks its reads and removes the maintenance hazard of keeping the array
  in sync.
- Extract MOBILE_MAX_WIDTH_QUERY in is-mobile-viewport.ts and reuse it in
  CopilotModalHeader's matchMedia listener to remove the within-Vue duplication
  of the breakpoint literal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:52:21 -05:00
Benjamin Taylor f47f4b34dc fix(vue): lazily import the drawer element so @copilotkit/vue stays SSR-safe
CopilotThreadsDrawer.vue previously imported
`@copilotkit/web-components/threads-drawer` statically at module scope.
That module defines a Lit custom element, and eagerly loading it on every
`@copilotkit/vue` import risked an `HTMLElement is not defined`-style crash
under SSR (Node has no DOM) for any consumer that imports the package on
the server, e.g. Nuxt.

Fix: import the element module lazily, inside `onMounted`, so it is only
ever evaluated client-side. `elementTag`/`mounted` are set once the dynamic
import resolves, and the template gates rendering on `mounted`.

Consequence: the wrapper now mounts the custom element asynchronously.
Updated `CopilotThreadsDrawer.test.ts` so `mountDrawer()` awaits
`flushPromises()` (resolving the dynamic import) plus a trailing
`nextTick()` (flushing the render and the `flush: "post"` property-push
watcher) before returning, and centralized this settle in the shared
helper instead of repeating ad hoc `nextTick()` calls per test.

Added `CopilotThreadsDrawer.ssr.test.ts`, a `@vitest-environment node`
regression test asserting the package entry (which re-exports
CopilotThreadsDrawer) imports without throwing when there is no
`HTMLElement` global, guarding against reintroducing an eager DOM-dependent
import into the barrel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:26:24 -05:00
Maximiliano Korp 9a173f1e45 fix(core): harden Intelligence reconnect cursors 2026-07-01 11:07:07 -07:00
Benjamin Taylor e109c350d3 fix(vue): register thread store with core + gate on threadEndpoints (React parity)
Ports two React-parity gaps (audit-flagged as user-facing) into the Vue
`useThreads` composable, mirroring react-core `use-threads.tsx`.

Fix A — register the thread store with core's single-slot registry:
- Adds a `watch([resolvedEnabled, resolvedAgentId])` that calls
  `copilotkit.value.registerThreadStore(agentId, store)` and, via the
  watch's `onCleanup`, `unregisterThreadStore(agentId)` on
  disable/agentId-change/unmount. Gated on `resolvedEnabled` so a disabled
  (unlicensed) store never evicts a co-mounted live store for the same agent.

Fix B — gate list + mutations on `copilotkit.threadEndpoints`:
- Derives `threadListEndpointSupported`/`threadMutationsSupported` via
  `!== false` (legacy runtimes advertise `undefined` => supported).
- Context-dispatch watcher skips dispatching (setContext(null)) when the
  list endpoint is unsupported, so no `/threads` fetch fires.
- Folds `threadEndpointsError` ("Thread endpoints are not available on this
  CopilotKit runtime") into `error` (NOT `listError`, which stays
  storeError-only) and factors `!threadEndpointsUnavailable` into
  `preConnectLoading` so the UI doesn't spin against an endpoint-less runtime.
- `guardMutation` wraps rename/archive/unarchive/delete to reject with
  "Thread mutations are not available on this CopilotKit runtime" when
  `threadEndpoints.mutations === false`, before touching the network.

Call sites:
- The shipped `CopilotThreadsDrawer` consumes `error` for its user-facing
  error banner (now surfaces the endpoints-unavailable message instead of
  spinning) and `listError` for genuine list-load failures (unchanged
  contract: storeError only, no config/runtime-setup leakage). Its `enabled`
  prop (unlicensed gate) already suppressed fetches; it now also correctly
  suppresses core-registry registration so a co-mounted live chat store for
  the same agent is not evicted. Mutation buttons (rename/archive/delete)
  reject locally on runtimes that don't serve mutations.

Item 3 (startNewThread configErrorDismissed) intentionally left out of scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:29:54 -05:00
Benjamin Taylor dc506c4c9b test(vue): cover CopilotThreadsDrawer event-routing table + guard findChatInput for SSR
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:17:33 -05:00
tylerslaton 617e88a069 chore: release monorepo v1.62.1 2026-07-01 17:07:44 +00:00
Benjamin Taylor 7803080713 fix(vue): keep setModalOpen undefined without backing modal state
Wave-4 introduced setModalOpenWithDrawerExclusion as an always-defined
function on configurationValue.setModalOpen. Under a bare
<CopilotChatConfigurationProvider> (no isModalDefaultOpen, no parent
providing modal state), this broke the pre-existing contract that
setModalOpen is undefined when the provider owns no backing modal
state — the function was defined but a no-op, since it delegated to
parentConfigValue?.setModalOpen, which was undefined.

CopilotChatToggleButton.vue depends on that undefined-ness: it checks
`config.value?.setModalOpen` and falls back to a local `fallbackOpen`
ref when absent. With the regression, clicks routed into the no-op
setter instead of the fallback, permanently stuck closed.

Fix: replace the standalone function with a `publicSetModalOpen`
computed that returns undefined when resolvedSetModalOpen (the real,
possibly-parent-inherited setter) is undefined, and otherwise wraps it
with the mobile drawer-exclusion behavior. This preserves the
undefined contract without touching resolvedIsModalOpen,
resolvedSetModalOpen, or the drawer-registration code.

Call-site enumeration for setModalOpen consumers (all read via
optional chaining, so all remain safe):
- CopilotChatToggleButton.vue: `config.value?.setModalOpen` — restored
  fallback-open behavior when the provider is bare, confirmed via new
  presence-contract tests plus the existing 5-test
  CopilotChatToggleButton.test.ts suite (all pass).
- CopilotModalHeader.vue: `config.value?.setModalOpen?.(false)` — only
  used inside modal-backed compositions (CopilotPopup/Sidebar), which
  always pass isModalDefaultOpen, so setModalOpen is always defined
  there; unaffected.
- CopilotSidebarViewInternal.vue / CopilotPopupViewInternal.vue: same
  pattern, same modal-backed guarantee; unaffected.

React's CopilotChatConfigurationProvider.tsx intentionally keeps
setModalOpen always-defined and always backed by internal state — a
different resolution than Vue's. This fix takes the minimal Vue-local
path (preserve the undefined contract CopilotChatToggleButton relies
on) rather than reworking resolvedIsModalOpen's backing, to avoid
changing bare-provider modal-open semantics.

Tests: added a "modal-setter presence contract" describe block
verifying (1) a bare provider exposes setModalOpen as undefined
(red-green verified: fails on pre-fix code, passes after), and (2) a
provider with isModalDefaultOpen exposes a working setModalOpen that
toggles isModalOpen. All 21 tests in the provider suite pass,
including the existing mobile drawer<->modal mutual-exclusion tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:04:56 -05:00
Austin Merrick f54e99697b fix(build): replace Unix-only commands in package.json scripts with Node.js equivalents (#5602)
## What does this PR do?

This PR fixes build failures on Windows by replacing Unix-only shell
commands (`rm -rf`, `cp`, `mkdir -p`) in `package.json` scripts with
cross-platform Node.js `fs` built-in commands.

This follows the project's existing codebase pattern for cross-platform
operations, as seen in `packages/react-ui/package.json` (line 45).

### 🛠️ Changes:
- **`packages/runtime`**: Replaced `rm -rf` in `generate-graphql-schema`
with `fs.rmSync`.
- **`packages/vue`**: Replaced `cp` in `build:types` and `rm -rf` in
`clean` with `fs.cpSync` and `fs.rmSync`.
- **`packages/angular`**: Replaced `mkdir -p` and `cp` in `build:css`
with `fs.mkdirSync` and `fs.cpSync`.
- **`examples/v1/next-openai`, `next-pages-router`, `state-machine`**:
Replaced `rm -rf` clean commands with a single Node.js loop that deletes
`.turbo`, `node_modules`, `dist`, and `.next`.

All modified packages now build successfully on Windows.

## Related PRs and Issues

- Closes #5601

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
2026-07-01 09:56:00 -07:00
Austin Merrick 66de791b40 fix(vue): inherit chat configuration agent id in useAgent (#5695)
## Summary

- Make Vue `useAgent` resolve agent IDs from explicit prop, then
surrounding chat configuration, then `DEFAULT_AGENT_ID`.
- Add regression coverage for bare `useAgent()` inside a non-default
configured chat and for explicit prop precedence.
- Update the Vue parity matrix note for the `useAgent` precedence
behavior.

Fixes #5656

## Verification

- `pnpm nx run @copilotkit/vue:test --
src/v2/hooks/__tests__/use-agent.test.ts` (failed before the fix with
`Received: "default"`, passes after)
- `pnpm --dir packages/vue exec eslint src/v2/hooks/use-agent.ts
src/v2/hooks/__tests__/use-agent.test.ts`
- `pnpm nx run @copilotkit/vue:check-types`
- `pnpm nx run @copilotkit/vue:test` (94 files / 1007 tests)
- `lefthook pre-commit test-and-check-packages` (ran `@copilotkit/vue`
test, build, publint, and attw)
- `git diff --check upstream/main...HEAD`

## Notes

- `pnpm nx run @copilotkit/vue:lint` still fails on pre-existing
unrelated Vue lint errors across untouched files; the changed files pass
eslint directly.
2026-07-01 09:54:41 -07:00
Benjamin Taylor 0399facf05 test(vue): unit-test isMobileViewport guard, reverse mobile mutual-exclusion, de-brittle clear-on-fresh
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:52:29 -05:00
Benjamin Taylor a3d919a995 fix(vue): resolve net-new lint (no-dupe-keys handlers, slot attr) + guard matchMedia in isMobileViewport
CopilotThreadsDrawer.vue declared local handlers `onNewThread`/`onLicensed`
with the same names as the `onNewThread`/`onLicensed` input-callback props,
tripping vue/no-dupe-keys. Renamed the handlers to `handleNewThread`/
`handleLicensed` (props left untouched) and updated their template bindings
(`@new-thread`, `@licensed`) and the one internal call site in `onDelete`
that re-implemented the new-thread flow inline.

Rewrote the per-row `:slot="`row:${t.id}`"` binding as
`v-bind="{ slot: `row:${t.id}` }"` to satisfy vue/no-deprecated-slot-attribute
without changing behavior — it still compiles to a real DOM `slot` attribute
used to project light-DOM children into the custom element's named shadow
slots (not a Vue component slot).

Added a `typeof window.matchMedia !== "function"` guard to
isMobileViewport(), matching CopilotModalHeader.vue and the React reference,
so environments where `window` exists but `matchMedia` doesn't (some test
runners, embedded webviews) don't throw when the provider opens the
drawer/modal.

CopilotModalHeader.vue:40 `_className` unused-var lint error predates this
branch (introduced in 28d07ccaa, already on main) and is left as out of
scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:47:15 -05:00
Benjamin Taylor b1e1d108ef fix(vue): correct chat-input focus testid + scope lookup, add provider-less drawerOpen fallback
focusChatInput() queried `[data-testid="copilot-chat-textarea"]`, a testid
that exists nowhere in the Vue package (copied from React) — the `?.` made
the miss silent, so focus never returned to the composer after selecting a
thread. Corrected to `copilot-chat-input-textarea` (CopilotChatInput.vue)
and added React-parity scoping: `findChatInput` walks up from the drawer's
element via `closest('[data-testid="copilot-chat-view"]')` to scope the
input lookup to the enclosing chat, falling back to a document-global query
when no such ancestor exists. Sole call site: `onThreadSelected`.

Added a `localDrawerOpen` ref fallback for when there is no surrounding
`CopilotChatConfigurationProvider` (`config.value === null`): previously
`el.open` was pinned to `false` forever and `onOpenChange` was a no-op in
that case, since both routed through `config.value?.`. A `drawerOpen`
computed / `setDrawerOpen` function now pick the provider's state when
present and the local ref otherwise; both the property-push watcher and
`onOpenChange` route through them. Mirrors React's bare-drawer-starts-closed
fallback.

Documented (no behavior change) that the `row` scoped slot, unlike React's
`renderRow`, has no per-row escape hatch back to the element default — once
provided it projects for every thread.

Added tests: focus-return after thread-selected (red against the old
testid), delete-of-active-thread resets to a new thread (plus the
negative non-active case), and the provider-less drawerOpen fallback
reflecting open-change events.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:41:17 -05:00
Benjamin Taylor 54b33a7947 fix(vue): re-arm useThreads loading indicator on enable/reconnect + gate preConnectLoading on enabled
Call sites: `preConnectLoading` only feeds `isLoading` (returned to consumers,
e.g. CopilotThreadsDrawer's licensed-gate loading state); `hasDispatchedContext`
is read only by `preConnectLoading` and written only in the context watcher.

Without gating on `resolvedEnabled`, useThreads({ enabled: false }) left
isLoading stuck true forever (hasDispatchedContext never set). Without
resetting the flag on the disabled/no-runtimeUrl branch, toggling
enabled false->true (or runtimeUrl removed+re-added) failed to re-arm the
pre-connect loading synthesis, regressing the empty-list flash it exists
to suppress. Brings the Vue hook back in parity with the React reference's
reset behavior for these two branches.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:37:01 -05:00
Benjamin Taylor d15d95804e feat(vue): export CopilotThreadsDrawer from the package entry
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:19:08 -05:00
Benjamin Taylor bd1289fa44 test(vue): CopilotThreadsDrawer renders element, routes thread-selected, fires onLicensed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:14:19 -05:00
Benjamin Taylor 4cacf5fbd0 feat(vue): CopilotThreadsDrawer wrapper over the shared threads-drawer element
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:12:34 -05:00
Benjamin Taylor 989ed28762 test(vue): mobile drawer launcher visibility + toggle in CopilotModalHeader
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:08:38 -05:00
Benjamin Taylor aaa9916556 feat(vue): mobile drawer launcher in CopilotModalHeader
Adds a mobile-only launcher button at the leading edge of
CopilotModalHeader that toggles the drawer. It renders only when a
CopilotThreadsDrawer has registered with the chat configuration AND
the viewport is mobile (<=767px, matched via matchMedia and mirrored
from the React CopilotModalHeader implementation). Also exports
IconPanelLeftOpen from the shared icons barrel for the button's icon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:06:32 -05:00
Benjamin Taylor d0c77d6d72 test(vue): cover drawer registerDrawer + mobile drawer/modal mutual-exclusion
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:02:50 -05:00
Benjamin Taylor e36a7c953c feat(vue): drawer-awareness state + mobile mutual-exclusion in chat config provider
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:01:34 -05:00
Benjamin Taylor 0254da874e feat(vue): add drawer-awareness fields to CopilotChatConfigurationValue type
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:00:03 -05:00
Benjamin Taylor 1def05e500 test(vue): clear-on-fresh clears messages on new-thread switch, not on mount
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:57:50 -05:00
Benjamin Taylor 0fea7a8ccb feat(vue): clear chat messages on fresh non-explicit thread switch
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:53:58 -05:00
Benjamin Taylor 60c01eef1d test(vue): cover active-thread setActiveThreadId/startNewThread + authoritative guard
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:51:28 -05:00
Benjamin Taylor daf26f22c1 feat(vue): active-thread override + setActiveThreadId/startNewThread in chat config provider
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:50:18 -05:00
Benjamin Taylor 6ba86c4027 feat(vue): add active-thread setters to CopilotChatConfigurationValue type
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:48:37 -05:00
MikeRyanDev ca836ff920 chore: release monorepo v1.62.0 2026-07-01 15:45:54 +00:00
Benjamin Taylor 259276d795 chore(vue): depend on @copilotkit/web-components for the threads drawer element
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:44:32 -05:00
Benjamin Taylor 89837c50fb feat(vue): expose unarchive/refetch/startNewThread/listError/isMutating + enabled gate on useThreads
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:43:09 -05:00
Benjamin Taylor 2f85aa7bc1 feat(vue): add isMobileViewport helper for drawer mobile mutual-exclusion
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:40:07 -05:00
Ran Shemtov e1321406bc fix(vue): enable A2UI via catalog-on-provider path (#5777) 2026-07-01 08:33:53 +02:00
Benjamin Taylor 79276a2d4f fix(chat): keep message padding in sub-viewport panes via container query
CopilotChat message wrappers used viewport-keyed `cpk:sm:px-0`, collapsing
horizontal padding to 0 at any viewport >=640px. The message column is
`max-w-3xl` (768px) centered; the design assumes the chat fills the viewport,
so at >=640px the column has side gutters and inner padding can drop to 0.

But when the chat lives in a sub-viewport-width pane (e.g. the threads drawer
rail beside the chat, ~580px on an 820px iPad-portrait viewport), `sm:px-0`
still fires on viewport width while the 768px column overflows the narrow
pane and sits flush against both edges. The input wrapper looked fine because
it is visually inset by its own pill, so only message text appeared broken.

Make the padding container-relative instead of viewport-relative:
- add `cpk:@container` (container-type: inline-size) to the chat root, and
- switch the message/input/suggestion wrappers from `cpk:sm:px-0` to the
  container variant `cpk:@3xl:px-0`.

Padding now tracks the chat's own width and drops to 0 only once the container
is at least as wide as the column's own max-width, so the column has real
gutters; in any narrower pane the `px-4` inner padding is retained. React,
Angular, and Vue kept in lockstep.

Note on the breakpoint: Tailwind v4 container-query breakpoints differ from
viewport breakpoints (`@sm` = 24rem/384px, not 640px). A mechanical
`sm:` -> `@sm:` swap would still collapse the ~580px repro pane. `@3xl`
(48rem/768px) is used because it exactly matches the column's `max-w-3xl`,
which is the width at which side gutters first appear.

Verified: full-width desktop chat unchanged (container >=768px -> px-0);
580px pane retains 16px padding; render-prop layouts without a container
ancestor degrade safely to `px-4`; sidebar/popup `data-*` padding overrides
are unaffected.

ENT-1020

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:26:15 -05:00
Ran Shem Tov 258cdce49a feat(vue): export vueBasicCatalog for the catalog-on-provider path
A2UI's catalog-on-provider path needs a catalog to pass to
`a2ui.catalog`, but the library build tree-shook the nested barrel
re-export so vueBasicCatalog was unreachable at runtime (present only in
the .d.ts). Re-export it explicitly from the v2 entry, mirroring React's
`basicCatalog` export from @copilotkit/a2ui-renderer. Add an export test
guarding against the regression.
2026-06-30 16:33:12 +02:00