## 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)
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>
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>
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>
Add examples/v2/vue/demo/pages/threads.vue demonstrating the drawer
alongside chat. The demo has no root CopilotKitProvider (app.vue only
renders <NuxtPage/>), so the page mounts its own provider chain,
matching the runtime-url + show-dev-console convention used by every
sibling demo page. CopilotChatConfigurationProvider is left
uncontrolled (no threadId) so the drawer drives the active thread.
No natural nav list exists in index.vue or elsewhere in the demo, so
no nav link was added.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
## Release monorepo v1.62.0
**Scope:** `monorepo` | **Bump:** `minor`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `monorepo` packages to `1.62.0`
and generated AI-enhanced release notes.
2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
must pass before merging. This is the review gate.
3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.
4. **When this PR is merged**, the `release / publish` workflow
automatically:
- Builds all packages
- Publishes the `monorepo` packages to npm at version `1.62.0`
- Creates git tag `monorepo/v1.62.0`
- Creates a GitHub Release with the final release notes
### Before merging
- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)
---
> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
## What
Revert the v2 tool-call resolver so an **unhandled** tool call (no
per-tool or wildcard renderer registered) renders **nothing** again,
instead of auto-painting the built-in `DefaultToolCallRenderer` card.
Showing the card is opt-in, exactly as before:
| Call | Result |
|---|---|
| `useDefaultRenderTool()` | built-in default tool-call card |
| `useDefaultRenderTool({ render })` | the caller's custom UI |
| neither | nothing renders |
## Why
The auto-card default was introduced in commit `ba60df5d33`
(2026-05-07), buried as the **last bullet** of a commit titled
`feat(showcase/langgraph-python): add per-tool testids`. It shipped
silently in **v1.57.2** with **no CHANGELOG entry** and is still live
through 1.61.2.
Effect: in v2, **every** agent tool call that surfaces in chat without a
registered renderer paints a card exposing the tool name, raw arguments
JSON, and raw result JSON — in production, for all customers. This is
how internal A2UI plumbing tools (`generate_a2ui`, `render_a2ui`,
`log_a2ui_event`) leaked into deployed showcase demos, and it equally
leaks any customer's own un-rendered tools.
Pre-1.57.2 behavior was `return null` (invisible). This restores that
opt-in contract.
## Changes
- `use-render-tool-call.tsx`: resolver returns `null` when no
per-tool/wildcard renderer matches.
- Removed the now-dead `defaultToolCallRenderAdapter` + `__testOnly_`
export + unused imports.
- Rewrote `use-render-tool-call.test.tsx` to drive the real
`useDefaultRenderTool` → real `useRenderToolCall` through a notifying
mock core, asserting all 3 states (card / custom / nothing).
## Showcase impact
None required. The opt-in `tool-rendering-*-catchall` demos already call
`useDefaultRenderTool()`, so their cards (and e2e specs) are unchanged.
No a2ui/declarative spec asserts the card — those demos simply stop
leaking it in prod.
## Verification
- `nx test react-core`: 1368 pass (107 files), incl. the new 3-scenario
suite.
- `nx check-types react-core`: clean.
- `nx build react-core`: clean.
- oxfmt `--check`: clean.
Fixes#5773, a regression of #3872. The inspector event timeline stays
empty during runs — no `RUN_STARTED`, `TEXT_MESSAGE_*`, `TOOL_CALL_*`,
etc. — while connection status, agents, context and tools still
populate.
The cause is the same one #3872 fixed and that has since been dropped:
`useAgent` runs a per-thread *clone* of the registry agent, and clones
are never added to the registry. The inspector only subscribes to AG-UI
events via `onAgentsChanged`, so it never sees the clone that actually
runs, and every `recordAgentEvent` sits on the wrong instance. The
`onAgentRunStarted` bridge #3872 added is no longer declared on
`CopilotKitCoreSubscriber`.
Re-landing it against the current run path: `RunHandler` fires
`onAgentRunStarted` with the real run instance at the start of
`runAgent` (top-level only, so recursive tool follow-ups don't
re-subscribe) and `connectAgent`, and the inspector subscribes to that
instance. `subscribeToAgent` already keys by `agentId` and replaces, so
the clone cleanly supersedes the registry subscription and there's no
leak. Per-thread cloning is untouched.
Added a core regression test driving a run on a clone that isn't in the
registry and asserting the event fires once with that instance, plus
that recursive follow-up runs don't re-notify. Core and web-inspector
both typecheck; core suite green.
## Summary
- Adds the unified web-inspector thread debugger surface as a standalone
`cpk-thread-inspector` custom element backed by a
`ThreadDebuggerProvider` contract for thread metadata, messages, AG-UI
events, and state.
- Keeps the existing full Web Inspector shell compatible by making the
internal `cpk-thread-details` element extend the new shared inspector
implementation.
- Normalizes persisted AG-UI event history into a first-class Timeline
view with run/message/tool/state/raw rows, source-event links into the
Raw AG-UI Events tab, metadata pills, and raw-event preservation for
top-level fields.
- Improves timeline/state loading behavior for standalone and
runtime-backed usage: state stays lazy-loaded, provider/runtime/header
swaps refetch safely, stale async responses are ignored, and
message-backed or raw-only threads no longer show an empty first tab.
- Adds a standalone web-inspector dev harness with AG-UI events,
Messages only, and Raw event only scenarios for shared UI validation
outside an app shell.
- Updates package/dev configuration for the harness (`dev:standalone`,
Vite dev dependency/config allowlist) and bumps
`@copilotkit/web-inspector` to `1.61.3`.
## Tests and Checks
- Expanded `packages/web-inspector` tests for the provider contract,
normalized timelines, raw event rendering, lazy state loading, stale
request handling, header-driven refetches, runtime fallbacks, and
first-tab timeline population.
- PR checks currently passing include build, check-types, format,
oxlint, package-quality, unit on Node 20/22/24, Python unit tests, shell
script tests, Validate Showcase, bundle-size, config/binary checks, and
integration starter/showcase jobs.
- Manual validation path documented in
`packages/web-inspector/README.md`: `pnpm nx run
@copilotkit/web-inspector:dev:standalone`, then validate AG-UI events,
Messages only, Raw event only, source-event navigation, and State at
`http://127.0.0.1:5177/`.
## Companion PR
- Intelligence managed debugger integration:
https://github.com/CopilotKit/Intelligence/pull/461
<img width="928" height="724" alt="Screenshot 2026-06-29 at 2 50 47 PM"
src="https://github.com/user-attachments/assets/c7936f1c-0780-4c0c-b5d2-1890db611460"
/>
<img width="927" height="1325" alt="Screenshot 2026-06-29 at 2 49 50 PM"
src="https://github.com/user-attachments/assets/48134189-7def-4312-b4d1-4bad28a93a12"
/>
e733" />
<img width="927" height="408" alt="Screenshot 2026-06-29 at 2 49 58 PM"
src="https://github.com/user-attachments/assets/1773b1f7-10a1-4905-ab6e-418d13031a49"
/>
<img width="929" height="583" alt="Screenshot 2026-06-29 at 2 50 07 PM"
src="https://github.com/user-attachments/assets/8f38f91d-296b-41dd-8b50-8c60739b387d"
/>
The inspector subscribed to AG-UI events only through onAgentsChanged, which
skips per-thread clones, leaving the event timeline empty during runs
(regression of #3872). Subscribe to the run instance reported by
onAgentRunStarted so its events are recorded.
Add an onAgentRunStarted subscriber event fired from RunHandler.runAgent and
connectAgent with the instance that actually runs. Per-thread clones (from
useAgent) are not in the agent registry, so onAgentsChanged never fires for
them and subscribers that only track registry agents can't observe their run.
## What
The **Angular `<CopilotDrawer>` feature** for the CopilotKit SDK — a
ready, usable threads drawer for Angular apps, plus the active-thread
foundation it sits on. (This PR merges the originally-staged PR1+PR2 so
it ships a working feature, not just plumbing.)
**Foundation — active-thread coordination (no new store):**
1. **`CopilotChatConfiguration`** — an injectable, signal-based service
mirroring React's `CopilotChatConfigurationProvider`: owns
`agentId`/`threadId` resolution (controlled prop → override → minted
fallback) + the `hasExplicitThreadId` welcome flag; `setActiveThreadId`
/ `startNewThread` setters that no-op when host-controlled;
single-instance via `useExisting`.
2. **`connectActiveThread`** (internal connector) — reactively pins the
resolved thread onto `agent.threadId` and owns the connect lifecycle
(per-run `AbortController` on `HttpAgent`s, a single caught connect
chain, a staleness-guarded loading cursor, abort+`detachActiveRun()`
teardown), mirroring the standalone `connectToAgent` path. Clears
messages only on a genuine new-thread transition (never on
mount/agent-swap).
3. **`CopilotChat`** consumes the ambient config when present
(input-first precedence: `[agentId]`/`[threadId]` win), seeds the config
from a set `[threadId]`; standalone `[threadId]` usage unchanged when no
provider is present.
**The drawer — `<copilot-drawer>`:**
4. A standalone `OnPush` wrapper around the framework-agnostic
`copilotkit-drawer` Lit element (`@copilotkit/web-components`). Events
bind declaratively; element properties are set imperatively via
`viewChild`+`effect` (the `a2ui-activity-renderer` precedent). Routes
the element's events
(`thread-selected`/`new-thread`/`archive`/`unarchive`/`delete`/`filter-change`/`retry`)
to the config + `injectThreads` mutations (delete-of-active resets to a
fresh thread).
5. **`CopilotDrawerRow`** directive for per-row custom content
(`slot="row:{id}"`), `onThreadSelect`/`onNewThread` host escape-hatch
callbacks, and `<ng-content>` slot passthrough
(`launcher-icon`/`memories`).
6. **`listError`** added to `injectThreads` — a filtered error (genuine
list/mutation errors only, excluding developer/config errors like
"Runtime URL is not configured") so the drawer's error panel never shows
dev strings to end users. Mirrors react-core's `useThreads`.
**Always-licensed:** Angular SDK licensing is no longer a thing, so the
wrapper does not wire the element's `licensed`/`upsell` — no upsell
path. **Inline-chat-only:** no Layer-2 open-state coordination (the
element self-provides its mobile launcher).
## Why
Angular had no active-thread provider (only `injectThreads`) and no
drawer component. This lands both so Angular apps get thread switching +
a usable threads drawer at parity with the React vertical, reinterpreted
for Angular's inline-chat-only surface.
## Testing
`@copilotkit/angular`: tsc clean, `oxlint` 0 errors, `ng-packagr` build
green, **152 tests pass** (full package suite). New coverage: config
precedence/controlled-vs-uncontrolled + single-instance identity;
connector connect-on-switch / clear-only-on-real-transition / cursor
staleness / no-unhandled-rejection / abort+detach teardown (verified
against a real `HttpAgent`); drawer prop binding, all 7 event routings
(incl. delete-active reset), escape-hatch overrides, `renderRow`
projection, `<ng-content>` slot passthrough, and the `listError`
dev-error-exclusion.
Hardened through a full `cr-loop` (foundation: 5 rounds; wrapper: 2
rounds) — the wrapper CR caught + fixed a real bug (the error panel was
leaking dev/config errors; fixed via the filtered `listError`).
## Dependency / release gate
Depends on `@copilotkit/web-components` (the `copilotkit-drawer`
element) being published — the same release gate as the React drawer
work (#5707). `packages/angular` publishes alongside.
## Follow-ups (out of scope, tracked)
- A demo `routes/threads` in `examples/v2/angular/demo` (kept separate
to keep this a clean SDK-only change).
- Pre-existing OSS packaging: `zod` is in `devDependencies` but imported
by production source — should move to `dependencies`/`peerDependencies`.
- `packages/angular/src/lib/threads.spec.ts` uses `describe/it` vs the
flat-`test` convention of sibling specs (pre-existing).
- Minor polish backlog from CR (renderRow multi-row tests, a docblock
tidy) — non-blocking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add a `licenseUrl` property to the shared element (default
https://docs.copilotkit.ai/intelligence). The locked view's Upgrade CTA now
dispatches a cancelable `licensed` event carrying the url and, unless the
host calls preventDefault(), opens it in a new tab; a blank url suppresses
navigation. React and Angular wrappers expose an optional `licenseUrl` prop.
License-gate the Angular drawer like React: surface `licenseStatus` as a
signal on the CopilotKit service and gate the wrapper on
status valid|expiring && checkFeature("threads"), skipping the thread fetch
while unlicensed and showing the loading state (not the locked view) until
the status resolves. Reverses the earlier always-licensed Angular call.
Closes ENT-1027.
Final naming decision. Renames the public component + element across the
board: React/Angular CopilotDrawer -> CopilotThreadsDrawer, the Lit element
copilotkit-drawer -> copilotkit-threads-drawer (tag, CopilotKitThreadsDrawer
class, COPILOTKIT_THREADS_DRAWER_TAG, defineCopilotKitThreadsDrawer), the
@copilotkit/web-components/drawer subpath -> /threads-drawer (+ src dir),
the CopilotThreadsDrawerRow directive / copilotThreadsDrawerRow input, and
all prose/test references. Generic types (DrawerThread, DrawerFilter),
--cpk-drawer-* tokens, and ::part names are unchanged. Behavior unchanged.
Per review, settle the locked-view affordance on a neutral 'license'
name. Renames the event (unlicensed -> licensed), React prop
(onUnlicensed -> onLicensed), slot/part/class (licensed, licensed-cta),
LicensedDetail type, data-testid, render method, and identifier-referencing
comments/test names. The existing 'licensed' boolean gate is unchanged;
prose describing the not-licensed state still reads 'unlicensed'/'locked
view'. Behavior unchanged; Angular unaffected.
Per review, drop the 'upsell' monetization jargon. Renames the event
(upsell -> unlicensed), the React prop (onUpsell -> onUnlicensed), the
slot/part/class (upsell -> unlicensed, upsell-cta -> unlicensed-cta),
the UnlicensedDetail type, the data-testid, and all comments/test names.
Behavior unchanged. Angular is unaffected (always-licensed, no gate).
Wires up the previously-dormant pagination plumbing (ENT-1016):
- Element: render a 'Load more' button at the list bottom when hasMore
(and not fetching / not errored), emitting a new load-more event
(LoadMoreDetail). Distinct from retry{scope:'fetch-more'} (error
recovery); both advance pagination.
- React CopilotDrawer: add a limit prop (forwarded to useThreads) and
route load-more to fetchMoreThreads.
- Angular CopilotDrawer: add a limit input (forwarded to injectThreads)
and route load-more to fetchMoreThreads.
Tests: element load-more render + emit + precedence; React limit
forwarding + load-more routing; Angular load-more routing.
Mirrors the Angular wrapper + the copilotkit-drawer element's label
property: sets the drawer region aria-label and default header text,
defaulting to the element's built-in "Threads" when omitted.