GHSA-72qq-p3r5-f7wq (CVSS 9.3). web_core <= 0.10.1 passed an agent-supplied
`openUrl` argument straight to `window.open()` with no scheme allowlist, so a
Button whose `functionCall` named a `javascript:` URI executed arbitrary script
in the host origin when a user clicked it. The Basic Catalog is the default, so
no non-default configuration was required to be exposed.
We pinned 0.9.0 exactly, as a runtime dependency of two published packages
(@copilotkit/a2ui-renderer, @copilotkit/vue) and transitively of
@copilotkit/react-core and @copilotkit/angular, so downstream users could not
upgrade out of it on their own. 0.10.4 keeps the ./v0_9 and
./v0_9/basic_catalog entrypoints we import; the only symbol dropped from v0_9
is FrameworkSignal, which we never referenced.
Add regression tests over both renderers that reach the sink independently
(React and Lit). They assert that javascript: and data: URIs never reach
window.open, that https URLs still open with noopener,noreferrer, and that a
blocked scheme leaves the surface mounted rather than escaping into the click
handler. Verified they fail against 0.9.0 and pass against 0.10.4.
## 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)
## 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)
Bump @ag-ui/core, @ag-ui/client, @ag-ui/encoder from 0.0.53 to 0.0.56
across all packages.
@ag-ui/client 0.0.56 changed runHttpRequest from (url, requestInit) to a
fetch-thunk signature (() => Promise<Response>). Update the single-route
and connect transport paths in ProxiedCopilotRuntimeAgent to wrap the
request in () => this.fetch(url, init), restoring the broken envelope
transports.
Add @ag-ui/core, client, encoder, proto to minimum-release-age-exclude
in .npmrc so the freshly published 0.0.56 (under the 24h release-age
gate) installs in CI.