Commit Graph

2992 Commits

Author SHA1 Message Date
MikeRyanDev 175e9c237c chore: release angular v0.5.2 2026-09-08 20:27:11 +00:00
MikeRyanDev 16514e9424 chore: release monorepo v1.70.2 2026-09-08 20:03:39 +00:00
lukasmoschitz 416e854dc6 fix(runtime): encode SSE response chunks as bytes (#6909)
Fixes #6888.

Refs #6919 — same root cause, reported as a Cloudflare workerd symptom.
Deliberately not closed by this change: that report lists two further
workerd blockers it does not address (createRequire(import.meta.url) at
module load, and AbstractAgent generating a UUID in global scope).
2026-09-08 14:20:07 +02:00
lukasmoschitz aff68853a4 test(channels-telegram): cover telegram-html edge cases (#6916)
Adds 10 tests for already-documented telegramHtml behavior (empty input,
__bold__, _italic_, headings, inline-code escaping, * / + bullets,
multiple fenced blocks, bold+italic coexistence). No source logic
changed, so no clash with open issue #6602 (language-tag handling
untouched). Verified: expected outputs computed by running the actual
implementation in node; oxfmt passes.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Tests**
- Added coverage for Telegram HTML formatting, including headings, bold
and italic text, bullet lists, inline code escaping, multiple code
blocks, empty input, and mixed formatting.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 13:47:40 +02:00
Doniyor 62c895fee2 fix(react-core): default attachment uploads to one at a time
`maxConcurrentUploads` defaulted to 3, which changed when a public
`onUpload` is called with no code change on the app's side: a handler
written when uploads were serial could suddenly see the next file start
before the previous one finished. Concurrency is now something the app
asks for, and `maxConcurrentUploads: 3` restores the pool.

Queueing the whole selection up front is kept at every limit — it shows
the user what they picked rather than changing a contract.

The default test now pins one-at-a-time; a separate test pins that
`maxConcurrentUploads: 3` really runs three. Docs, the `AttachmentsConfig`
JSDoc and the react-core skill reference say `1`.
2026-09-07 15:02:30 +05:00
Doniyor c237f29dbb fix(react-core): share the upload pool across processFiles calls
The worker pool was per `processFiles` call, so a paste landing while a
dropped selection was still uploading opened its own set of workers —
two overlapping selections could run 2× the limit, and
`maxConcurrentUploads: 1` gave one upload per call rather than one at a
time.

Move the queue and the worker count onto the hook: workers are counted,
not owned by a call, and a call tops the pool up to the limit instead of
starting a fresh one. Each call still resolves when its own files have
settled.

Also pin `Infinity` as "no limit" with a test, and say in the docs that
the limit covers everything in flight rather than each batch.
2026-09-07 15:02:30 +05:00
Doniyor 62067b76d1 feat(react-core): upload attachments concurrently
`processFiles` walked the valid files in a `for` loop and awaited each
upload inside it, so `onUpload` was called for one file only after the
previous had finished — attaching 8 files to a chat cost 8 sequential
round trips to whatever storage the app uploads to.

Queue the whole selection first, then drain it with a bounded worker
pool: `maxConcurrentUploads` on `AttachmentsConfig` sets the bound and
defaults to 3, and `1` restores one-at-a-time uploads for an endpoint
that wants them. `onUpload` may now be called concurrently.

Queueing up front also means a file waiting for a free slot is already
visible as `uploading` rather than appearing once its upload starts.

The Vue and Angular bindings read the same config type and still upload
serially; they can follow separately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 15:02:30 +05:00
Fnine59 1a2f5691b6 Merge upstream/main into fix/copilotkit-6888-sse-binary 2026-09-07 04:42:20 +08:00
Rainer Hahnekamp 0d46ef5fe7 Merge branch 'main' into feat/angular-copilot-activity 2026-09-06 21:53:17 +02:00
Murat Sari 5571eeafda test(angular): await slot rendering with signal-based fixtures 2026-09-06 11:09:39 +02:00
Murat Sari 8100f08c5f fix(angular): render slots with createComponent bindings 2026-09-06 10:35:48 +02:00
Ayush7614 43f1e81ce6 test(channels-telegram): cover telegram-html edge cases (bold/italic/headings/bullets/code) 2026-09-05 15:45:57 +05:30
Martha Kelly Schumann 1fdca1dc9e fix(inspector): harden Learning review flows 2026-09-04 17:30:44 -07:00
Martha Kelly Schumann cfc4fd8e26 fix(inspector): refine Learning onboarding flow 2026-09-04 17:12:34 -07:00
Martha Kelly Schumann c70502b137 feat(inspector): add Learning view and workbench 2026-09-04 17:11:59 -07:00
Martha Kelly Schumann 05fc4e05a4 feat(runtime): expose Learning snapshots to Inspector 2026-09-04 17:11:42 -07:00
Ben Taylor 428fcbd60d fix(core): send the whole RunAgentInput in the Intelligence run body (#6890)
Fixes OSS-1132

## Problem

`IntelligenceAgent` hand-built its REST run body by naming fields, and
`resume` was not one of them:

```ts
body: JSON.stringify({ threadId, runId, messages, tools, context, state, forwardedProps })
```

`HttpAgent` posts the whole `RunAgentInput`, so the self-hosted and SSE
paths carry `resume` correctly. Only the Intelligence transport dropped
it.

The two interrupt paths carry their resume payload in different fields:

| Path | Trigger | Resume travels as | Survived the Intelligence body |
| -- | -- | -- | -- |
| Legacy | `on_interrupt` CUSTOM event | `forwardedProps.command.resume`
| Yes |
| Standard | `RUN_FINISHED` with `outcome: "interrupt"` | top-level
`resume[]` | **No** |

So resuming a standard interrupt against an Intelligence runtime failed
silently: no error, no console output, and the graph simply re-entered
its gate. The server side was already correct — `RunAgentInputSchema`
declares `resume` and `parseRunRequest` parses with that schema.

Nothing the CLI scaffolds hits this combination today (it needs the
built-in agent plus Intelligence mode plus HITL), which is why it stayed
quiet.

## Change

Spread the input instead of naming fields, so a future protocol field
cannot be lost the same way:

```ts
body: JSON.stringify({ ...input, ...(mode === "connect" ? { lastSeenEventId } : {}) })
```

## Testing

Verified in a worktree with its own full `pnpm install` and freshly
built workspace `dist` output. The baseline is the same command with the
two changed files checked out from `origin/main`.

### Two new tests in
`packages/core/src/__tests__/intelligence-agent.test.ts`

- `carries the AG-UI resume array in the run body`
- `posts every RunAgentInput field, so no protocol field is dropped` —
iterates the input's own keys, so it fails on any future omission

### Whole-package suite

| | Test files | Tests |
| -- | -- | -- |
| Baseline (`origin/main`) | 69 passed | 830 passed |
| With this change | 69 passed | **832 passed** |

+2, exactly the new tests. No failures either side.

### Mutation check

Reverted the source fix to the hand-built field list and re-ran the
file:

```
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯
      Tests  2 failed | 59 passed (61)
```

Both new tests fail without the fix, so neither is self-fulfilling.

### Typecheck, lint, format

- `tsc --noEmit -p packages/core/tsconfig.json`: **0 errors**.
- `oxlint` on both files: 0 errors (2 pre-existing
`consistent-function-scoping` warnings in the test file, unchanged).
- Formatted with `oxfmt`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed agent run requests so all provided run input fields are
transmitted correctly.
  * Preserved resume information when starting an agent run.
  * Ensured no supported protocol fields are omitted from requests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 18:46:57 -05:00
Ben Taylor 8c629c147b docs(showcase): document frontend-driven activity cards (refs #3388) (#6904)
## What

Issue #3388 asked for a way to put a card into the chat transcript from
frontend code, without a tool call and without adding to the
conversation the model reads.

**That already ships.** A message with `role: "activity"` renders
standalone in the transcript, and `AbstractAgent.prepareRunAgentInput`
strips every activity message from the run payload:

```js
prepareRunAgentInput(e) {
  let t = structuredClone_(this.messages).filter(e => e.role !== `activity`);
  ...
}
```

The gap was documentation. `renderActivityMessages` is only documented
for **backend-emitted** activities (mastra background-tasks, a2a,
mcp-apps), so the frontend-driven path was undiscoverable.

This PR adds the missing guide page and a test that pins the behavior.

## Changes

| File | Why |
| --- | --- |
| `showcase/shell-docs/.../generative-ui/frontend-cards.mdx` | New
"Frontend-Driven Cards" guide |
| `showcase/shell-docs/.../generative-ui/meta.json` | Sidebar entry
(6-line insertion) |
| `packages/react-core/.../CopilotChatFrontendActivityCard.e2e.test.tsx`
| Pins both halves of the contract |

No source changes. Behavior is unchanged; this documents and locks what
already works.

## The non-obvious part

The card must be added via the agent returned by `useAgent()`. An agent
instance constructed and held outside React is **not** the instance the
chat renders, so messages added to it silently never appear. This cost
me a debugging round while verifying, and it is called out as a warning
callout in the docs.

## Testing

**1. New test passes against clean `origin/main`** (run in a worktree at
`96cf7aa55f`, with `@copilotkit/shared` and `@copilotkit/core` rebuilt
from the worktree so the test is not reading a stale dist):

```
✓ src/v2/components/chat/__tests__/CopilotChatFrontendActivityCard.e2e.test.tsx (2 tests) 72ms

 Test Files  1 passed (1)
      Tests  2 passed (2)
```

**2. Mutation-checked, so neither assertion is self-fulfilling.**

Drop the renderer registration → the render test fails:
```
× renders a card added from frontend code, with no tool call 1068ms
      Tests  1 failed | 1 passed (2)
```

Swap the card from `role: "activity"` to `role: "assistant"` → it
reappears in the payload, so the exclusion is real and specific to
`activity`:
```
AssertionError: expected [ 'user', 'assistant' ] to deeply equal [ 'user' ]
```

**3. Neighboring test unaffected on the same base:**

```
✓ src/v2/components/chat/__tests__/CopilotChatMessageView.test.tsx (16 tests) 53ms
      Tests  16 passed (16)
```

**4. Independent probe of the filter** against the pinned
`@ag-ui/client` 0.0.57:

```
agent.messages roles: [ 'user', 'activity' ]
run input roles     : [ 'user' ]
```

**5. `tsc --noEmit`** — zero errors in the new file. Remaining errors in
this workspace are in files this PR does not touch
(`MCPAppsActivityRenderer.tsx`, `CopilotKitInspector.tsx`) and are
artifacts of a hand-assembled local `node_modules`; CI has the real
install.

**6. `oxfmt --check`** — clean.

**7. Docs checks** — `meta.json` validated as JSON; internal link uses
the house `/generative-ui/...` form (no `/docs` prefix); `Callout
type="warn"` matches the dominant existing usage; import paths verified
against the real `@copilotkit/react-core/v2` barrel exports.

## Follow-up

Leaving #3388 open until this lands, then closing it with a pointer to
the new page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added support for frontend-driven activity cards that render in chat
transcripts without being sent to the agent or language model.
- Added documentation covering activity card renderers, schemas,
registration, payload filtering, snapshots, and limitations.
- Added a new “Frontend-Driven” section to the Generative UI
documentation navigation.

- **Tests**
- Added end-to-end coverage for activity card rendering and payload
exclusion.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 17:32:15 -05:00
Fnine59 79fdeb64c2 fix(runtime): encode SSE response chunks as bytes 2026-09-04 22:05:57 +00:00
Ben Taylor a4adf38683 fix(shared): keep Node-only telemetry out of browser build graphs (#6846)
## What does this PR do?

`@copilotkit/shared` re-exported `telemetry/telemetry-client.ts` from
its root entry. That module imports `@segment/analytics-node`, which
imports `node-fetch`, which imports the Node built-ins `stream`, `http`,
`https` and `zlib`. Browser bundlers resolve the whole static module
graph before they tree-shake, so every browser build of a dependent
package printed `Module ... has been externalized for browser
compatibility` warnings, even when the consumer never touched telemetry.

This PR keeps that edge out of the browser-facing entry:

- `isTelemetryDisabled` moves into
`src/telemetry/telemetry-disabled.ts`, so the root entry can keep
exporting it without reaching the client.
- The root entry keeps `isTelemetryDisabled`, the `lambdaClient`
surface, the sampling helpers, and the `TelemetryCapture` /
`TelemetryIdentity` types. The types are exported with `export type`, so
they are erased and add no runtime edge.
- `TelemetryClient` is now reachable at `@copilotkit/shared/telemetry`,
a new export subpath.
- A new test walks the value-level import graph from `src/index.ts` and
fails if it reaches a Node-only package.

Deferring the import does not fix this, which is what PR #5482
attempted. A dynamic import defers evaluation but keeps the graph edge,
so `vite:resolve` still reaches `node-fetch`. The measurement is in
https://github.com/CopilotKit/CopilotKit/pull/5482#issuecomment-5509823707.

## Export surface change

`TelemetryClient` is no longer on the `@copilotkit/shared` root entry,
or on the `CopilotKitShared` UMD global. It is reachable at
`@copilotkit/shared/telemetry`.

```diff
- import { TelemetryClient } from "@copilotkit/shared";
+ import { TelemetryClient } from "@copilotkit/shared/telemetry";
```

This is a public export in the packaging sense only. `TelemetryClient`
is our internal metrics client, so no application code is expected to
import it, and nothing that works today is expected to stop working.
`packages/runtime/src/v1-deprecated/lib/telemetry-client.ts` is the only
in-repo consumer and is updated here. There is no root shim on purpose:
a runtime re-export would reintroduce the graph edge and the bug.

`typesVersions` carries the subpath for `moduleResolution: "node"`
(node10) consumers, which `packages/runtime` still uses. Without it,
`tsc` cannot see the subpath's types.

`scripts/release/public-api/manifest.v1.json` is regenerated for the new
entry point. The manifest tracks entry points rather than symbols, so
the change there is the added `./telemetry` record.

## Related PRs and Issues

- Fixes #4151
- Supersedes #5482

## Testing

### The reported symptom, before and after

Vite 7.3.2, minimal app whose entry imports only browser-safe symbols
from `@copilotkit/shared`, pointed at a real tsdown build of the
package.

| | `vite build` warnings | modules transformed |
| --- | --- | --- |
| `main` | 4 (`stream`, `http`, `https`, `zlib`) | 663 |
| this branch | **0** | 451 |

After, verbatim:

```
vite v7.3.2 building client environment for production...
transforming...
✓ 451 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html                0.12 kB │ gzip: 0.12 kB
dist/assets/index-EEiKsU3u.js  2.43 kB │ gzip: 1.29 kB
✓ built in 267ms
```

The dev-server dependency scanner is fixed too. `vite optimize --force`
before this change pre-bundled `@ag-ui/client, @segment/analytics-node,
chalk, graphql, partial-json, uuid, zod`; after it pre-bundles
`@ag-ui/client, graphql, partial-json, uuid, zod`.

### The new export surface, exercised in Node

```
=== CJS require of subpath ===
TelemetryClient: function
isTelemetryDisabled: function true
lambdaClient: object
segment instantiated: Analytics
=== ESM import of subpath ===
esm TelemetryClient: function disabled: true
=== root entry ===
root TelemetryClient: undefined
root isTelemetryDisabled: function
root lambdaClient: object
root computeSamplingMeta: function
root firstNonBlankTelemetryId: function
```

### Subpath type resolution, both resolution modes

```
### moduleResolution node10 (what packages/runtime uses) ###
(clean)
### moduleResolution node16 ###
(clean)
```

Before adding `typesVersions`, node10 failed as expected, which is why
the field is there:

```
probe.ts(1,33): error TS2307: Cannot find module '@copilotkit/shared/telemetry' or its
corresponding type declarations.
  There are types at '.../dist/telemetry/index.d.mts', but this result could not be
  resolved under your current 'moduleResolution' setting.
```

### The regression guard is not self-fulfilling

Mutation-checked both ways. Restoring `export * from "./telemetry"` on
the root entry:

```
× root entry browser safety (#4151) > does not reach Node-only packages through value imports
  → expected [ '@segment/analytics-node' ] to deeply equal []
```

Turning the type-only re-export into a value re-export fails it as well,
and restoring the file makes both tests pass again.

### The gate that went red on the first push

`scripts/release/lib/public-api-manifest.test.ts` compares the committed
public API manifest to a freshly generated one, and a new export subpath
has to be recorded there. Regenerated with `pnpm
generate:public-api-manifest`; the failing test and its whole suite now
pass:

```
scripts/release/generate-public-api-manifest.ts --check
  scripts/release/public-api/manifest.v1.json is current

vitest run scripts/release
  Test Files  14 passed (14)
       Tests  162 passed (162)
```

### Package gates

```
@copilotkit/shared: tsc --noEmit          clean
@copilotkit/shared: vitest run            18 files, 404 tests passed
@copilotkit/shared: tsdown                Build complete
@copilotkit/shared: verify-cjs-exports    exit 0
@copilotkit/shared: es-check es2022       55 files, ES13 compatible
@copilotkit/shared: es-check es2018 (umd) 1 file, ES9 compatible
@copilotkit/shared: publint               only the pre-existing repository.url suggestion
@copilotkit/shared: attw --profile node16 all green, including "@copilotkit/shared/telemetry"
```

### Not run locally

`@copilotkit/runtime:build` and the workspace-wide pre-commit gate. My
local install is missing `type-graphql@2.0.0-rc.1` from the pnpm store,
so the runtime build fails on `Cannot find module 'type-graphql'` on
`main` as well, with or without this change. The runtime change here is
one import line, and I verified that it resolves under both node10 and
node16. CI runs the real gate. This commit was made with `--no-verify`
for that reason.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added a dedicated `@copilotkit/shared/telemetry` entry point for
server-side telemetry functionality.
- Added support for disabling telemetry when
`COPILOTKIT_TELEMETRY_DISABLED` or `DO_NOT_TRACK` is set to `true` or
`1`.

- **Improvements**
- Improved browser compatibility by preventing Node-only telemetry
dependencies from being included in browser bundles.
- Existing browser-safe telemetry utilities remain available from the
main shared package entry point.
- Full telemetry client functionality is now accessed through the
dedicated telemetry entry point.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 15:42:53 -05:00
Ben Taylor 5fd08a824e feat(react-core): controlled open/onOpenChange props for CopilotSidebar and CopilotPopup (#6905)
Closes #3334 (OSS-524).

## Problem

v1 `<CopilotSidebar>` exposed `open` and `onSetOpen`. Those props let a
host open and close the chat from its own UI. v2 shipped only
`defaultOpen`. The reporter wanted a button in their own nav bar to
close the sidebar.

The reporter's stated root cause is now stale. `shouldCreateModalState`
no longer exists. Since CPK-7152 the provider syncs both directions:
`setAndSync` upward, and an effect downward. A host that wraps its
layout in `<CopilotChatConfigurationProvider>` and calls `setModalOpen`
therefore does drive the sidebar on current `main`. I verified that
before writing any code.

Two things are genuinely missing. The first is the ergonomic API that v1
had. The second is documentation for the outer-provider pattern that
already works.

Two earlier community attempts (#3729, #6418) were closed unmerged.

## What changed

`open` and `onOpenChange` on `<CopilotSidebar>` and `<CopilotPopup>`:

- `open` pins what the surface renders, from the first frame.
- `onOpenChange` reports every request to open or close: the toggle
button, click-outside, Escape, and the drawer's mobile mutual-exclusion.
It fires with or without `open`, so it also works as a plain
notification on the uncontrolled path.
- `defaultOpen` is unchanged. If both are passed, `open` wins.

Two design choices are worth review.

**1. A context-overriding scope, not a fourth mode in the provider.**
`ControlledModalOpenScope` replaces `isModalOpen` and `setModalOpen` for
the subtree below the provider that owns the state. The resolution chain
inside `CopilotChatConfigurationProvider` stays untouched: own state,
parent sync, drawer mutual-exclusion, and the `ɵregisterModalCloser`
stack. The scope's setter still calls the underlying one, so those side
effects keep running. It also registers itself as the modal closer, so
the drawer's mobile exclusion reaches the host instead of flipping state
that nothing displays. The alternative was a controlled branch threaded
through `resolvedIsModalOpen`, `setAndSync`, and the sync effect. That
adds a fourth interacting mode to the code CPK-7152 just stabilized.

**2. The props reach the views by context, not as props.**
`<CopilotSidebar>` hands its view to `<CopilotChat>` as a memoized
`chatView` component. Adding `open` to that memo's deps mints a new
element type per toggle, and React then remounts the whole chat subtree.
That is the same class of bug #6173 fixed for popup resize. There is a
regression test for it.

Scope note: I included `<CopilotPopup>` because it shares the mechanism
and the same docs page. The issue named only the sidebar.

## Testing

**New suite, 15 tests** (`CopilotSidebar.controlledOpen.test.tsx`). It
covers the controlled contract, the unchanged uncontrolled path, and the
remount guard.

```
✓ src/v2/components/chat/__tests__/CopilotSidebar.controlledOpen.test.tsx (15 tests) 155ms
  Test Files  1 passed (1)
       Tests  15 passed (15)
```

**Mutation-checked.** I broke each mechanism to confirm that the tests
really fail.

| Mutation | Result |
| --- | --- |
| Drop `ControlledModalOpenScope`, keep only the seeded default | 5
failed: both `onOpenChange` reports, both host-driven open/close cases,
the popup report |
| Implement through the memoized override instead (add `open` to the
`useMemo` deps) | 1 failed: the remount guard, `expected 4 to be 1`, one
extra mount per flip |
| Drop the `open ?? defaultOpen` seeding | 1 failed: "stays put when the
host stops controlling open" |

I also mutation-checked the pre-existing two-way sync before I started.
That confirmed the outer-provider workaround really works on `main`,
instead of only appearing to.

**Full `@copilotkit/react-core` suite.** No regressions.

```
Test Files  141 passed | 1 skipped (142)
     Tests  1604 passed | 2 skipped (1606)
EXIT=0
```

**Adjacent suites re-run explicitly**: sidebar position, sidebar and
popup slots, popup resize-remount, drawer launcher, and the provider's
own 43 tests.

```
Test Files  6 passed (6)
     Tests  117 passed (117)
```

**Typecheck.** `tsc --noEmit` in `packages/react-core` gave `exit=0`
with no output. The tsconfig includes `src/**/*`, so the new test file
is typechecked too.

**Format and lint.** `oxfmt --check packages/react-core/src/v2` reported
"All matched files use the correct format." `oxlint` on the touched
files reported 0 errors.

**Pre-commit hooks.** They ran for real on both commits.

```
NX   Successfully ran targets test, publint, attw for 2 projects and 20 tasks they depend on
✔️ test-and-check-packages (15.33 seconds)
```

## Docs

- `prebuilt-components/chat-controls.mdx` now leads with the controlled
pair. Its example drives the sidebar from a nav button outside it, which
is the shape #3334 asked about. The `useCopilotChatConfiguration` route
stays, reframed as the option for callers who prefer not to lift the
state.
- `reference/components/CopilotSidebar.mdx` and `CopilotPopup.mdx` gain
`open` and `onOpenChange`. Both pages documented `defaultOpen` as
`false`, but both surfaces mount open, so I corrected that. A new test
per surface pins the real default.

## Not in this PR

- Vue and Angular parity for the same props.
- The `width` prop of `<CopilotSidebar>` still sits in the memo deps of
the `chatView` override. A live-resized sidebar therefore remounts the
chat subtree, the way the popup did before #6173. That is pre-existing
and out of scope here.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Added controlled open-state support for chat popups and sidebars
through `open` and `onOpenChange`.
- Preserved uncontrolled usage with `defaultOpen`, while allowing
externally managed visibility and toggle requests.
  - Improved coordination between modal and mobile drawer behavior.

- **Documentation**
- Added usage guidance and reference details for controlled and
uncontrolled open-state management.

- **Tests**
- Added coverage for initial visibility, toggle callbacks, controlled
updates, default behavior, and preserving the chat subtree.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 15:39:28 -05:00
Ben Taylor 603bc16cf3 fix(react-core): restore code block line breaks in packaged CSS (#3330) (#6902)
## What does this PR do?

Fixes #3330 — fenced markdown code blocks render as one collapsed line
in the packaged v2 React UI.

### Root cause

streamdown renders one `<span>` per source line inside
`pre[data-streamdown="code-block-body"] > code`, and leaves **no newline
characters** in the text. The line break comes entirely from the raw
Tailwind utility `block` on that span:

```js
// streamdown 1.6.11, dist/code-block-*.js
var v = cn("block", "before:content-[counter(line)]", ...);
```

CopilotKit builds Tailwind with `@import "tailwindcss" prefix(cpk)`, so
`.block` is never emitted into `dist/v2/index.css` — only `.cpk\:block`
is. Every line therefore renders inline and the block collapses onto one
row.

The line spans carry no `data-streamdown` attribute, so the rule has to
be scoped structurally, the same way the table action controls were in
#5944:

```css
[data-copilotkit] [data-streamdown="code-block-body"] > code > span {
  @apply cpk:block;
}
```

### Why the earlier attempts did not work

Three previous PRs (#3441, #3615, #5387) added `whitespace-pre` to the
`<pre>`. That is a no-op: the UA stylesheet already applies
`white-space: pre` to `<pre>`, nothing in the packaged CSS overrides it,
and there are no newlines in the text for it to preserve.

### Knowingly not fixed here

- **Line-number gutter.** streamdown's `before:content-[counter(line)]
before:w-4 before:mr-4 …` utilities are unprefixed too, so the gutter
never renders. That is cosmetic, and the repo's existing scoped rules do
not port it either.
- **The pre-highlight loading skeleton** (`space-y-4`, `divide-y`,
`animate-spin`) is unprefixed as well — a brief flash of unstyled
skeleton before shiki resolves.
- **The broader class of bug.** Every unprefixed streamdown utility has
to be hand-ported like this. streamdown 2.x adds a `prefix` prop that
would fix the whole surface at once, and #5147 proposes removing the
bundled renderer entirely. Both are larger calls than this bug fix.

## Testing

**1. Live browser verification.** Built `dist/v2/index.css` from
`origin/main` and from this branch, rendered streamdown 1.6.11's actual
code-block DOM against each, and measured layout in Chromium:

| | `white-space` on `<pre>` | line-span `display` | distinct rendered
rows | `<pre>` height |
|---|---|---|---|---|
| main | `pre` | `inline` | **1** | 52px |
| this PR | `pre` | `block` | **5** | 112px |

Indentation is preserved after the fix (`spans[1].textContent` starts
with two spaces).


**2. Compiled CSS.** `tailwindcss -i src/v2/styles/globals.css -o … -m`
emits exactly:

```css
[data-copilotkit] [data-streamdown=code-block-body]>code>span{display:block}
```

**3. Tests** — `pnpm -C packages/react-core exec vitest run
src/v2/styles`

```
 ✓ src/v2/styles/__tests__/streamdown-styles.test.ts (3 tests) 2ms
 ✓ src/v2/styles/__tests__/streamdown-table-controls.test.tsx (1 test) 37ms
 ✓ src/v2/styles/__tests__/streamdown-code-block-lines.test.tsx (1 test) 430ms

 Test Files  3 passed (3)
      Tests  5 passed (5)
```

Two tests, following the split established by #5944 — a source-string
test that the selector exists, and a DOM test that streamdown still
renders the structure that selector assumes (so a streamdown markup
change fails loudly instead of silently un-fixing this).

**4. Mutation-checked both tests.** Removing the CSS rule fails the
string test:

```
   × Streamdown styles > ships a scoped display rule for code block lines (#3330) 3ms
      Tests  1 failed | 2 passed (3)
```

Pointing the DOM test at a selector streamdown does not render fails it:

```
   × Streamdown code block lines DOM (#3330) > renders one line span per source line 428ms
      Tests  1 failed (1)
```

**5. Formatting** — `oxfmt --check` clean on all three files; `git diff
--check` clean.

`tsc --noEmit` in this worktree reports 58 pre-existing errors, all from
a stale cross-package `@copilotkit/core` dist; none are in the changed
files (which are CSS plus tests).

## Related PRs and Issues

Fixes #3330
Supersedes #3441, #3615, #5387, #5996 (all added a no-op
`whitespace-pre`)

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation (not applicable: scoped visual bug fix)
- [x] "Allow edits by maintainers" is checked


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Fixed fenced code blocks collapsing into a single line in the packaged
UI.
- Code lines now render vertically as separate rows with the correct
layout styling.

- **Tests**
- Added regression coverage to verify code-line rendering and scoped
styles for code blocks.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 15:38:59 -05:00
Ben Taylor dc737a80f1 fix(react-textarea): stop Cmd+K insert from deleting adjacent text (#6900)
Closes #2192

## Problem

`CopilotTextarea`'s Cmd+K hovering editor destroys text when the user
clicks **Insert**. `HoveringToolbar` deleted the selection and then
reused the same `selection` range for the insert:

```ts
Transforms.delete(editor, { at: selection });
Transforms.insertText(editor, insertedText, { at: selection });
```

Two separate defects come out of those two lines:

1. **Expanded selection.** The range is stale after the delete, and
`Transforms.insertText` already deletes an expanded range before
inserting. The delete inside `insertText` therefore runs a second time,
at the same offsets in the now-shorter document, and eats the characters
that followed the insertion point.
2. **Collapsed caret** (plain insert, no selection). `Transforms.delete`
resolves a collapsed range to a point and deletes one character
*forward*, so inserting at the caret always destroyed the next
character.

The reporter described it as "Insert did not fill in the content". What
actually happens is that the insert lands but neighbouring text
disappears with it, which is data loss in the user's document.

`@copilotkit/react-textarea` is a deprecated v1 package with **no 1:1 v2
replacement**, so affected users have nowhere to migrate. That is why
this is worth a one-line fix rather than a won't-fix.

## Change

Drop the `Transforms.delete` call. `Transforms.insertText` is correct
for both cases on its own: it deletes an expanded range through point
refs and inserts at its start (`slate@0.94.1`,
`TextTransforms.insertText`), and it inserts in place when the range is
collapsed.

The two `import type` lines in the diff are the pre-commit `oxlint
--fix` hook's own autofix on the touched file, not a hand edit.

No new API surface, no runtime dependency change, no test-harness added
to a deprecated package.

## Testing

The package has no component/slate test setup (only two trivial unit
test files), so verification is behavioural, against a standalone Vite +
React 18 app that mounts `BaseCopilotTextarea` with a stub
`insertionOrEditingFunction` that streams the literal
`HELLO_FROM_SUGGESTION` (no LLM in the loop). Each run: set the
selection, Cmd+K, type a prompt, generate, click **Insert**, then read
back both the editor DOM text and the controlled `value` mirrored
outside the component.

**Before** — `@copilotkit/react-textarea@1.70.1` from npm (identical to
`main`):

| Case | Start | Result |
| --- | --- | --- |
| select `bravo` | `alpha bravo charlie delta echo` | `alpha
HELLO_FROM_SUGGESTIONlie delta echo` — ate ` char` |
| caret at offset 6, no selection | `alpha bravo charlie delta echo` |
`alpha HELLO_FROM_SUGGESTIONravo charlie delta echo` — ate `b` |

**After** — `packages/react-textarea/dist` built from this branch (`nx
run @copilotkit/react-textarea:build`) and swapped into the same app:

| Case | Start | Result |
| --- | --- | --- |
| select `bravo` | `alpha bravo charlie delta echo` | `alpha
HELLO_FROM_SUGGESTION charlie delta echo`  |
| caret at offset 6, no selection | `alpha bravo charlie delta echo` |
`alpha HELLO_FROM_SUGGESTIONbravo charlie delta echo`  |
| select last word `echo` | `alpha bravo charlie delta echo` | `alpha
bravo charlie delta HELLO_FROM_SUGGESTION`  |
| select all (3 lines) | `one alpha\ntwo bravo\nthree charlie` |
`HELLO_FROM_SUGGESTION`  |
| select `alpha\ntwo bravo` (mid, spans lines) | `one alpha\ntwo
bravo\nthree charlie` | `one HELLO_FROM_SUGGESTION\nthree charlie`  |

The last three cases were already correct before the change (the stale
range clamps at the document end), and they stay correct — the fix does
not regress them.

Repo checks, in the worktree at `origin/main`:

```
$ pnpm nx run-many -t test,publint,attw --projects=@copilotkit/react-textarea
 ✓ src/lib/utils.test.ts (1 test) 1ms
 ✓ src/esm-compat.test.ts (1 test) 1ms
 Test Files  2 passed (2)
      Tests  2 passed (2)
NX   Successfully ran targets test, publint, attw for project @copilotkit/react-textarea and 19 tasks it depends on

$ pnpm exec tsc --noEmit        # in packages/react-textarea
exit: 0

$ pnpm exec oxlint packages/.../hovering-toolbar.tsx
Found 5 warnings and 0 errors.   # all pre-existing `import type` warnings elsewhere in the file
```

`nx run @copilotkit/react-textarea:check-types` also builds the
dependency project `@copilotkit/runtime-client-gql`, which fails locally
on missing generated GraphQL modules (`../graphql/@generated/graphql`).
That is a pre-existing local codegen gap in an untouched package, not
from this change; the direct `tsc --noEmit` above covers this package.

Pre-commit hooks ran on the commit (`check-binaries`, `lint-fix`,
`test-and-check-packages`, `check-intelligence-env-names`, `commitlint`)
and all passed.

## Not in scope

The screenshot on #2192 also shows the Cmd+K popup rendered far from the
selection (bottom-left of the viewport). That is a separate positioning
bug; PR #3679 attempted it and was closed. Not touched here.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved text insertion behavior in the hovering toolbar, including
replacement of selected text and insertion at the current caret
position.
* Prevented issues caused by outdated selection ranges during insertion.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 15:38:36 -05:00
Ben Taylor 41a31b99b0 fix(runtime): build the logger without pino redact so edge runtimes work (#6899)
Closes #2355 (OSS-547).

## The bug

`@copilotkit/runtime` cannot build its logger on Cloudflare Workers. The
deployed worker throws:

```
⨯ Error: pino – redact paths array contains an invalid path (pid)
```

## The cause, which is not what earlier triage said

The support-bot answer on the issue said `pid` is rejected because
Workers have no process ID, and suggested dropping `pid` from the array.
That is wrong, and the reporter said it did not help.

pino 9 hands `redact.paths` to `fast-redact`, whose validator checks
each path by **calling `Function(...)`**:

```js
// fast-redact/lib/validator.js
try {
  ...
  Function(`
      'use strict'
      const o = new Proxy({}, ...);
      o${expr}
      ...`)()
} catch (e) {
  throw Error(ERR_INVALID_PATH(s))   // <- swallows the real EvalError
}
```

Cloudflare Workers forbid code generation from strings, so the
`Function(...)` call throws an `EvalError`. The `catch` swallows it and
reports the **first path in the array**, which happens to be `pid`.
Every path fails, not just `pid` — removing `pid` only moves the error
to `hostname`.

## The fix

Use pino's own `base` switch, which is what the `redact` block was
emulating. It omits `pid` and `hostname`, produces identical output, and
needs no code generation.

```diff
-      redact: {
-        paths: ["pid", "hostname"],
-        remove: true,
-      },
+      base: null,
```

The file lives under `v1-deprecated/`, but v2 still reaches into it:
`v2/runtime/core/runtime.ts` and
`v2/runtime/handlers/shared/sse-response.ts` both call `createLogger`
when `debug.enabled`. So this fixes both major versions. In v1 the call
is at module scope (`v1-deprecated/lib/integrations/shared.ts:90`), so
the import throws unconditionally, which is why the reporter hit it on
v1.

## Testing

**1. Reproduced the exact reported error.** `node
--disallow-code-generation-from-strings` applies the same restriction
Workers apply. Run against pino 9.2.0 + pino-pretty 11.2.1 with the
pre-fix logger options:

```
$ node --disallow-code-generation-from-strings current.js
Error: pino – redact paths array contains an invalid path (pid)
    at .../fast-redact/lib/validator.js:29:15
    at Array.forEach (<anonymous>)
    at validate (.../fast-redact/lib/validator.js:12:11)
    at handle (.../pino/lib/redaction.js:113:3)
    at redaction (.../pino/lib/redaction.js:16:29)
    at pino (.../pino/pino.js:129:33)
```

**2. Confirmed `pid` is not special.** Same flag, other paths:

```
hostname-only FAILED: pino – redact paths array contains an invalid path (hostname)
unrelated path FAILED: pino – redact paths array contains an invalid path (req.headers.authorization)
```

**3. Confirmed the fix builds the logger under the same restriction**,
with the post-fix `createLogger` verbatim (both the plain and the
`component` child path):

```
$ node --disallow-code-generation-from-strings final.js
[13:18:27.986] DEBUG: debug line
    component: "copilotkit-debug"
[13:18:27.988] ERROR: error line
OK: createLogger works with code generation disabled
exit=0
```

**4. Confirmed output parity.** `base: null` emits exactly what the
`redact` block emitted:

```
--- redact variant ---          {"level":30,"time":1788545869311,"msg":"x"}
--- base:null variant ---       {"level":30,"time":1788545869311,"msg":"x"}
--- child component ---         {"level":30,"time":...,"component":"copilotkit-debug","msg":"y"}
```

**5. Confirmed the fix is safe on pino 10** (which this monorepo pins
via a pnpm override), same flag:

```
{"level":50,"time":1788545913844,"msg":"p10 base:null ok"}
```

**6. New unit test, mutation-checked.**
`packages/runtime/src/v1-deprecated/lib/__tests__/logger.test.ts`.
Passing:

```
 ✓ src/v1-deprecated/lib/__tests__/logger.test.ts (4 tests) 2ms
 Test Files  1 passed (1)
      Tests  4 passed (4)
```

Restoring the `redact` block fails it, so it is not self-fulfilling:

```
 FAIL  ... > createLogger > suppresses pid and hostname with base instead
AssertionError: expected undefined to be null
 Test Files  1 failed (1)
      Tests  2 failed | 2 passed (4)
```

**7. Full `@copilotkit/runtime` suite:**

```
 Test Files  1 failed | 153 passed (154)
      Tests  3 failed | 2273 passed (2276)
```

The one failing file is
`v1-deprecated/service-adapters/google/google-genai-adapter.test.ts`. It
fails identically on pristine `origin/main` with this change reverted
(`Test Files 1 failed (1) / Tests 3 failed (3)`), so it is pre-existing
and unrelated.

**8. Pre-commit hook green** — `check-binaries`, `lint-fix` (0 warnings,
0 errors), `check-intelligence-env-names`, and `test-and-check-packages`
(`test`, `publint`, `attw` across 7 projects) all passed on the commit.

`tsc --noEmit --strict` on the changed file exits 0. `oxfmt --write`
produced no changes.

## What this does not claim

This removes one proven blocker, not "Cloudflare support". I did not
deploy to workerd end to end, so `pino-pretty` and other Node
dependencies in the runtime may block separately.

## Separate observation, not fixed here

`packages/runtime/package.json` declares `"pino": "^9.2.0"`, but the
root `pnpm.overrides` pins `pino@<=10.1.1: 10.1.1`. pino 10 swapped
`fast-redact` for `@pinojs/redact`, which uses no code generation. So
this class of bug is invisible to local dev and CI here and only appears
in published installs. Worth closing that gap, but it is a major-version
bump for a published package and does not belong in this fix.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved logger compatibility in edge runtimes by preventing startup
failures caused by logger configuration.
* Preserved existing log output while suppressing process-specific
details such as process ID and hostname.
* Maintained default and environment-configured log levels, including
component-specific child loggers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 15:38:19 -05:00
Ben Taylor 4da2d7e34e fix(core): hydrate self-hosted threads whose /connect replay contains an errored run (#6528)
Hydrating an existing thread through `/connect` fails on a
**self-hosted** runtime whenever that thread's history contains a run
that ended in `RUN_ERROR`.

## The defect

A `/connect` response is a *replay* of a thread's history, so it can
legitimately carry several past runs back to back — including an errored
run followed by a later `RUN_STARTED`. The base `AbstractAgent` connect
pipeline pushes that stream through `verifyEvents`, which enforces
AG-UI's **single run** lifecycle rules and rejects the sequence
outright:

```
Cannot send event type 'RUN_STARTED': The run has already errored with 'RUN_ERROR'. No further events can be sent.
```

The user-visible effect is the one reported in #4943:
`agent_connect_failed` on reload, and the existing thread never hydrates
its prior messages.

`IntelligenceAgent` already omitted `verifyEvents` from its connect
pipeline for exactly this reason (its JSDoc spells it out). But
`ProxiedCopilotRuntimeAgent.connectAgent` only takes that path in
`RUNTIME_MODE_INTELLIGENCE` — self-hosted (`RUNTIME_MODE_SSE`) fell
through to `super.connectAgent()` and inherited the single-run
verification. So the managed product was fine and self-hosting was not.

## The fix

`ɵconnectWithoutEventVerification`
(`packages/core/src/utils/connect-replay.ts`) holds the
verifyEvents-free pipeline, and **both** paths now use it.
`transformChunks` is still applied — message reassembly is needed either
way.

This is a de-duplication rather than a third copy:
`IntelligenceAgent.connectAgent` drops ~100 lines of hand-replicated
base pipeline (including its private-field `any` escape hatch) and keeps
only its canonical-run-id handling before delegating. Net
`intelligence-agent.ts` change is −101 lines.

### Fidelity to the base implementation

The helper was diffed statement-by-statement against the **real**
`AbstractAgent.connectAgent` in `@ag-ui/client@0.0.57` (recovered from
the shipped source map), not just against `IntelligenceAgent`'s replica.
`verifyEvents` is the only intended difference.

That diff caught a defect in the first push: the base special-cases
`AGUIConnectNotImplementedError` (swallow → `EMPTY`) and the replica did
not. `IntelligenceAgent` never needed it — it always implements
`connect()` — so the gap was invisible there, but on the SSE path it is
load-bearing: `run-handler.ts:447-450` documents that `await
agent.detachActiveRun()` only stopped deadlocking because that error
path still reaches the pipeline's finalize block. Routing it through
`onError` would also fire run-failure callbacks on every subscriber for
a benign condition. Restored, with a regression test.

Also confirmed that dropping `verifyEvents` cannot alter a well-formed
replay: it is a pure gate — 18 `return of(event)` pass-throughs, 42
error paths, and zero `endWith` / `startWith` / `tap` side effects. It
only removes the single-run rejection.

The existing upstream TODO still stands and is carried over:
`@ag-ui/client@0.0.57`'s `connectAgent(parameters?, subscriber?)` takes
no option to skip verification, so this override is still the only way
to express "this stream is a replay, not a run."

## On the second half of #4943

The issue also reports that the legacy chat path doesn't copy the
resolved `threadId` onto the agent before connect/run. **That half is
already fixed on `main`** — the #5041/#4739 fix put `agent.threadId =
resolvedThreadId` in v2 `useAgent`, and `useCopilotChatInternal`
delegates to that same hook. Nothing more was needed.

It was untested, though, and untestable from the suite that looked like
it covered it: `use-copilot-chat-internal-connect.test.tsx` mocks
`useAgent` wholesale, so it cannot observe threadId propagation at all.
This PR adds `legacy-chat-explicit-threadid.test.tsx`, which drives the
legacy hook through the **real** `useAgent` under a real `<CopilotKit>`,
covering both the explicit-threadId case and the "don't adopt a
non-explicit placeholder UUID" case.

It reads the agent off `useCopilotChatInternal()`'s own return value
rather than calling `useAgent` in the probe. That distinction matters:
the first version of this test did call `useAgent`, so the probe itself
performed the assignment under test and the test passed **even with
`useCopilotChatInternal()` removed entirely**. The current version is
mutation-checked — disabling the assignment in v2 `useAgent` fails it
(`expected 'dc051f13-…' to be 'cookie-backed-thread'`).

Contributor PR #4969 proposed a manual assignment for this half; it is
now redundant.

## Testing

Worktree caveat, stated up front: this worktree symlinks the primary
checkout's `node_modules`, so `@copilotkit/shared` and
`@copilotkit/core` resolve to that checkout's **stale `dist`**. That
produces failures unrelated to this change; each is baselined against
clean `main` in the same environment below. CI installs fresh and is the
authoritative gate.

**1. Reproduces the reported failure before the fix.** The new core
test, run on unmodified `origin/main`, fails with the exact error from
the issue:

```
FAIL  src/__tests__/proxied-connect-replay-multi-run.test.ts > hydrates a thread whose replayed history contains an errored run
AssertionError: promise rejected "Error: Cannot send event type 'RUN_STARTE…" instead of resolving
Caused by: Error: Cannot send event type 'RUN_STARTED': The run has already errored with 'RUN_ERROR'. No further events can be sent.
```

**2. Passes after the fix**, hydrating both runs' messages (`["msg-1",
"msg-2"]`):

```
✓ src/__tests__/proxied-connect-replay-multi-run.test.ts (1 test) 11ms
Test Files  1 passed (1)
```

**3. Connect-not-implemented guard, fail-first.** With the guard
removed, the new second test fails exactly as the base contract
predicts:

```
× swallows AGUIConnectNotImplementedError instead of failing the run
AssertionError: promise rejected "Error: Connect not implemented. This meth…" instead of resolving
```

**4. Full `@copilotkit/core` suite** — this is the evidence the
`IntelligenceAgent` extraction is behavior-identical, since
`intelligence-agent.test.ts` exercises that path heavily:

```
Test Files  59 passed (59)
      Tests  635 passed (635)
```

(excludes `core-inspector-metadata.test.ts`; its 20 failures are the
stale-`shared`-dist artifact — verified identical on clean `main`: `20
failed | 2 passed`, missing export `InspectorMetadataV1`)

**5. `@copilotkit/react-core` — new + adjacent existing suites:**

```
✓ src/hooks/__tests__/use-copilot-chat-internal-connect.test.tsx (7 tests)
✓ src/hooks/__tests__/legacy-chat-explicit-threadid.test.tsx (2 tests)
✓ src/components/copilot-provider/__tests__/v1-explicit-threadid-bridge.test.tsx (5 tests)
Test Files  3 passed (3)
      Tests  14 passed (14)
```

Full react-core suite: `8 failed | 1492 passed (1500)`. All 8 are in
`use-interrupt` / `use-pin-to-send` / `CopilotChatView.pinToSend` — none
touch connect replay or threadId, and clean `main` in this worktree
fails the identical 8 (`8 failed | 37 passed (45)` for those three files
alone).

**6. `@copilotkit/vue`** (affected via core): `100 passed (100)` files,
`1072 passed (1072)` tests.

**7. Types, lint, format:**

```
tsc -p packages/core/tsconfig.json --noEmit   → no errors in any changed file
oxlint  <5 changed files>                     → Found 0 warnings and 0 errors
oxfmt --check <5 changed files>               → All matched files use the correct format
```

The only remaining `tsc` errors are 4 pre-existing stale-dist ones in
`agent-registry.ts` / `types.ts` (`InspectorMetadataV1`), untouched by
this PR.

Fixes #4943


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved thread hydration when reconnecting to histories containing
multiple runs, including runs that previously ended in error.
* Prevented unsupported connection errors from being reported as run
failures.
* Ensured connection state is properly finalized after replaying a
thread.
* Legacy chat components now correctly reuse an explicitly provided
thread ID while preserving generated IDs when none is provided.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-04 15:37:42 -05:00
Benjamin Taylor 91abd959f7 feat(react-core): controlled open/onOpenChange props for sidebar and popup
v1 exposed `open` + `onSetOpen`, so a host could open and close the chat
from its own UI. v2 shipped only `defaultOpen`, leaving the open state
reachable exclusively from inside the chat subtree. Restores the
controlled pair on `<CopilotSidebar>` and `<CopilotPopup>`:

- `open` pins what the surface renders, from the first frame.
- `onOpenChange` reports every request to open or close (toggle button,
  click-outside, Escape, the drawer's mobile mutual-exclusion). It fires
  with or without `open`, so it also works as a plain notification.

Implemented as `ControlledModalOpenScope`, which overrides the chat
configuration context for the subtree, rather than as a fourth mode
inside CopilotChatConfigurationProvider's modal-state resolution. The
provider's own state, parent sync, drawer mutual-exclusion and
modal-closer registry are untouched: the wrapped setter still calls the
underlying one, so those side effects keep running, and it registers
itself as the modal closer so the drawer reaches the host.

The props travel to the views by context, not through the memoized
`chatView` override. Threading a changing `open` through that override
would mint a new element type per toggle and remount the whole chat
subtree, which is the class of bug already fixed for popup resize.

Closes #3334
2026-09-04 15:11:41 -05:00
Benjamin Taylor 3672d007ae docs(showcase): document frontend-driven activity cards, lock the behavior with a test
Activity messages (role: "activity") already render standalone in the
transcript and are stripped from the run payload by
AbstractAgent.prepareRunAgentInput, so frontend code can put a card in the
chat without a tool call and without polluting the conversation. That was
only ever documented for backend-emitted activities, so the frontend-driven
path was undiscoverable — issue #3388 asked for a feature that already ships.

Adds a Generative UI guide page for the pattern and a react-core test that
pins both halves of the contract: the card renders, and it never reaches the
agent.

The non-obvious part, and the reason this needs documenting rather than a
one-line answer: the card must be added via the agent from useAgent(). An
agent instance constructed and held outside React is not the instance the
chat renders, so messages added to it silently never appear.

Refs #3388

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 15:05:30 -05:00
Benjamin Taylor b5a6d05ae9 fix(react-core): restore code block line breaks in packaged CSS (#3330)
Fenced code blocks rendered as a single collapsed line in the packaged v2
UI. streamdown emits one <span> per source line inside
`pre[data-streamdown="code-block-body"] > code` and leaves no newline in
the text, so the line break comes entirely from the raw Tailwind utility
`block` on that span. CopilotKit builds Tailwind with `prefix(cpk)`, so
`.block` never reaches `dist/v2/index.css` and every line ran inline.

Scope the display rule structurally, because the line spans carry no
`data-streamdown` attribute of their own.

Adding `whitespace-pre` to the <pre>, as earlier attempts did, changes
nothing: the UA stylesheet already sets `white-space: pre` there and
there are no newlines left to preserve.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 14:57:50 -05:00
Murat Sari 51f1f0f09a chore(examples): declare the web-inspector dependency in the angular demo and storybook fixes 2026-09-04 21:48:27 +02:00
Benjamin Taylor 9dea76ca81 fix(runtime): type the pino mock so the logger test typechecks
`tsc --noEmit` rejected reading `calls[0]` off an untyped `vi.fn()`,
whose call tuple is empty: TS2493. Give the mock pino's own
(options, stream) signature so the tuple carries real element types,
and drop the cast that was hiding it.
2026-09-04 13:58:46 -05:00
Benjamin Taylor cb8814c7ce fix(react-textarea): stop Cmd+K insert from deleting adjacent text
The Cmd+K hovering editor deleted the selection and then reused the same
`selection` range to insert:

    Transforms.delete(editor, { at: selection });
    Transforms.insertText(editor, insertedText, { at: selection });

The range is stale after the delete, and `Transforms.insertText` deletes
an expanded range itself before inserting, so the second delete ran at
the same offsets in the already-shortened document and ate the
characters that followed the insertion point. With a collapsed caret it
was worse: `Transforms.delete` resolves a collapsed range to a point and
deletes one character forward, so a plain insert always destroyed the
character after the cursor.

`Transforms.insertText` alone is correct for both cases. It deletes an
expanded range through point refs and inserts at its start, and it
inserts in place when the range is collapsed.

Closes #2192
2026-09-04 13:51:08 -05:00
Benjamin Taylor 1ad6b6fc79 fix(runtime): build the logger without pino redact so edge runtimes work
pino 9 validates every `redact.paths` entry by calling `Function(...)`
through fast-redact. Cloudflare Workers and other edge runtimes forbid
code generation from strings, so the runtime threw while building its
logger. The validator swallows the real EvalError and blames the first
path in the array, which made the failure read as "redact paths array
contains an invalid path (pid)" and sent earlier triage after `pid`
itself. Every path fails, not just `pid`.

`base: null` is pino's own switch for omitting `pid` and `hostname`, it
produces identical output, and it needs no code generation.

Closes #2355

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 13:47:53 -05:00
Mike Ryan c34f7abfc1 fix(runtime): retain resource context on method errors 2026-09-04 10:49:22 -07:00
Mike Ryan c276befc13 fix(runtime): harden single-route resource requests 2026-09-04 10:39:10 -07:00
Mike Ryan 2cde6b97f7 fix(runtime): preserve single-route resource context 2026-09-04 10:36:27 -07:00
Mike Ryan 840ad3c14a feat(runtime): support Intelligence over one route 2026-09-04 10:36:27 -07:00
Benjamin Taylor b06ea361c1 send the whole RunAgentInput in the Intelligence run body
The Intelligence transport hand-built its REST body by naming fields, which
silently dropped the AG-UI `resume` array. A standard interrupt (RUN_FINISHED
with outcome "interrupt") carries its resume payload at the top level, so
resuming one against an Intelligence runtime never reached the server and the
graph re-entered the same gate with no error. The legacy `on_interrupt` path
was unaffected because it travels inside forwardedProps.

Spread the input instead of naming fields, so a future protocol field cannot
be lost the same way, and add a test that fails if any field is dropped.

Fixes OSS-1132

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 09:09:43 -05:00
Maximiliano Korp 6f1d0824be fix(runtime): accept marketplace entitlement source 2026-09-03 16:59:37 -07:00
tylerslaton 6adbc6a4b6 chore: release angular v0.5.1 2026-09-03 20:31:11 +00:00
Tyler Slaton c41e319863 feat(web-inspector): unify locked feature pages (#6859)
## Summary

- give locked Threads and Learning a shared conversion-focused layout
- add streamlined Loom demos, setup and engineer CTAs, and detailed
capability sections
- remove the disabled Threads preview so both locked experiences use the
same full-page treatment

## Why

Users without Intelligence enabled should immediately understand what
each feature provides and have clear paths to configure it or talk with
the team.

## How

- render both gates through one responsive locked-feature surface
- use minimal Loom embeds with feature-specific videos
- align content on a single centered rail with theme-aware styling
- add product-specific copy, capability icons, accessibility labels,
telemetry coverage, and fixture assertions
- verify with `pnpm nx run @copilotkit/web-inspector:test` and `pnpm nx
run @copilotkit/web-inspector:check-types`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added unified locked views for Threads and Learning with feature
videos, capability outlines, setup guidance, and a “Talk to an Engineer”
call to action.
* Added responsive and dark-theme styling for locked-feature overviews.
  * Updated self-hosted and setup messaging.

* **Bug Fixes**
* Prevented unavailable Threads content, examples, controls, and
metadata actions from appearing.
  * Removed legacy runtime entitlement diagnostics from the interface.
  * Standardized locked-state telemetry and CTA behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-03 19:19:57 +02:00
Benjamin Taylor ea0d8ceab1 fix(runtime): reject a blank Intelligence API key at construction (closes OSS-1095)
`CopilotKitIntelligence` performed no validation on `apiKey`. It assigned the
value and sent it verbatim as a Bearer credential, so a blank key produced
`Authorization: Bearer ` and surfaced much later as a 401 that named nothing.

`apiKey: string` is required on the config type, so TypeScript catches a missing
property. It does not catch an empty one, and the shape that actually happens is
a `process.env` read TypeScript is told to trust: `?? ""` in the starter wiring
block, `!` in this file's own JSDoc examples. Both yield a blank key when the
variable is unset.

Throw at construction instead. Every caller builds the client during boot, so the
error lands at startup rather than on a user's first message. The message names
`CPK_INTELLIGENCE_API_KEY` and `copilotkit project select`, and echoes none of the
key value — the rule `parseProjectIdFromApiKey` already follows for a malformed
key.

This mirrors `configuredUrl`, which already treats a blank `apiUrl`/`wsUrl` as
unset for the same `?? ""` reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 11:32:35 -05:00
tylerslaton 71b2f481f9 chore: release monorepo v1.70.1 2026-09-03 15:49:49 +00:00
Tyler Slaton 830d6f51b0 chore: release channels v0.9.2 (#6861)
## Release channels v0.9.2

**Scope:** `channels` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `channels` packages to `0.9.2`
   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 `channels` packages to npm at version `0.9.2`
   - Creates git tag `channels/v0.9.2`
   - 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.
2026-09-03 17:47:47 +02:00
Tyler Slaton 452356b5e8 feat(web-inspector): unify locked feature pages 2026-09-03 08:37:15 -07:00
Alem Tuzlak 8a5a976f1a feat(core): expose webmcp-enabled frontend tools to browser agents (#6847)
## What does this PR do?

Hooks can now expose a frontend tool to browser agents through the
WebMCP browser API, next to the normal agent registration. Set `webmcp:
true`, or pass `{ annotations }` for WebMCP hints:

```ts
useFrontendTool({
  name: "searchOrders",
  description: "Search the signed-in user's orders by status",
  parameters: z.object({ status: z.enum(["open", "shipped", "delivered"]) }),
  handler: async ({ status }) => searchOrders(status),
  webmcp: { annotations: { readOnlyHint: true } },
});
```

How it works:

1. `FrontendTool` in `@copilotkit/core` gains the `webmcp` option. A new
`WebMCPRegistry` registers the tool on `document.modelContext` with its
name, description, input schema, and annotations. `execute` runs the
tool's own handler. The handler context has no `agent` there.
2. Every tool registry change in `RunHandler` reconciles the WebMCP
registrations. The same availability rules apply as for the agent tool
list. Removing a tool aborts its registration signal, and the browser
then unregisters it.
3. Each adapter picks the option up from core: v2 `useFrontendTool`
(React, Vue, React Native), the v1 `useCopilotAction` and
`useFrontendTool` wrappers (React, Vue), and Angular's
`registerFrontendTool`. Where WebMCP is not available (SSR, React
Native, browsers without the API), registration is a no-op.

The `webmcp` prop is documented on the React, Vue, and Angular reference
pages in shell-docs.

## Related PRs and Issues

- None.

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [ ] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)

## Testing

**Commands run**

- `pnpm nx run-many -t check-types
--projects=@copilotkit/core,@copilotkit/react-core,@copilotkit/vue,@copilotkit/angular`
— all pass.
- Full test suites: core (829 tests), vue (103), and angular pass.
react-core passes standalone (1589 tests). Under the lefthook pre-commit
hook, react-core flakes on pre-existing e2e tests (A2UI, MCP Apps) that
do not touch this code. Those tests pass when run alone.

**Manual test**

Requires Chrome 149+ with the WebMCP origin trial, or the testing flag.

1. Enable `chrome://flags/#enable-webmcp-testing`, then relaunch Chrome.
2. In an app that uses CopilotKit, register a tool with `webmcp: true`.
3. Run `await document.modelContext.getTools()` in DevTools. The tool is
listed with its schema and annotations.
4. Unmount the hook. Run the command again. The tool is gone.

**How this PR makes testing easy**

The behavior has automated tests on this branch:

- `packages/core/src/core/__tests__/run-handler-webmcp.test.ts` — 15
tests with a `document.modelContext` stub: registration, annotations,
unregistration, availability rules, name collisions, stale-rejection
races, and handler execution.
-
`packages/react-core/src/v2/hooks/__tests__/use-frontend-tool-webmcp.test.tsx`
and the mirrored
`packages/vue/src/v2/hooks/__tests__/use-frontend-tool-webmcp.test.ts` —
pass-through, re-registration, and agent-scoped cases at the hook level.
- `packages/vue/src/hooks/__tests__/use-frontend-tool-webmcp.test.ts` —
reactive `webmcp` getters through the v1 Vue API.

## Risk / rollback

Low. The feature is opt-in per tool. Without `webmcp`, no code path
changes. Where WebMCP is unsupported, registration is a no-op. Revert
this PR to roll back.

## Public API change

New optional `webmcp` prop on frontend tool registrations. Existing call
sites do not change.

**Before**

```ts
useFrontendTool({
  name: "searchOrders",
  description: "Search orders by status",
  parameters: z.object({ status: z.string() }),
  handler: async ({ status }) => searchOrders(status),
});
```

**After**

```ts
useFrontendTool({
  name: "searchOrders",
  description: "Search orders by status",
  parameters: z.object({ status: z.string() }),
  handler: async ({ status }) => searchOrders(status),
  webmcp: { annotations: { readOnlyHint: true } },
});
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Tools can now be exposed to browser agents through WebMCP.
* Added support for custom annotations and automatic parameter schema
generation.
* WebMCP registrations stay synchronized as tools are added, removed,
enabled, or updated.
  * Available across Angular, React, and Vue tool APIs.
* WebMCP reuses existing handlers and safely does nothing when
unavailable.

* **Documentation**
* Added usage guidance and examples for configuring WebMCP-enabled
tools.
  * Documented that WebMCP invocations do not include an agent context.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-03 14:15:29 +02:00
Tyler Slaton 49faaebceb fix(shared): drop the retired "premium" tier name from published packages (#6842)
#6818 renamed the Intelligence docs from `/premium/` to `/intelligence/`
and left the published packages alone. This cleans them up.

## The console notice

`@copilotkit/shared` printed this to every developer who used
`useCopilotChatHeadless_c` without a license key:

> To enable this **premium** feature, add your public license key…
> To learn more about **premium** features, read the documentation here:
docs.copilotkit.ai/**premium**/overview

"Premium" is not a tier we have. The notice now uses the sentence the
Headless UI docs page already uses — "Headless UI requires a CopilotKit
Intelligence license key" — so a developer who reads the notice and then
opens the docs finds the same words.

## One link was broken, not just stale

The "Show me how" button on the missing-public-API-key error opened
`/premium/overview#getting-access`. That heading was deleted on
2026-06-16 in 449237af0c, so the redirect carried the fragment to a page
that has no such anchor and the button landed at the top. It had been
doing that for two and a half months. It now points at
`#plans-and-access`, the section that answers how to get a key.

This was not in the ticket. It came out of tracing the links rather than
replacing them.

## The rest

Stale but working links move from `/premium/*` to `/intelligence/*` in
`react-core`, `web-inspector` and the runtime skill reference (two
byte-identical copies). Every one of them resolved through the redirects
from #6818; each cost a hop and named a retired tier. Four test files
assert these hrefs, so they move with the strings.

Not touched: `seo-redirects.ts` in `showcase/` keeps `/premium/`, since
those entries are what makes the old links work. `user_type: 'premium'`
in the provider examples is invented customer metadata, not a tier.

## Verification

- `nx run-many -t check-types,test` for `shared`, `react-core`,
`web-inspector`, `runtime`, `runtime-client-gql`: all green (17, 33,
152, 138 and 5 test files)
- On a cold worktree, `nx affected` first reported
`runtime-client-gql:check-types`, `runtime:check-types`, `runtime:test`
and `runtime:generate-graphql-schema` as failures. Cause: `check-types`
runs `tsc` concurrently with the `build` that generates
`src/graphql/@generated/*`, so the first run type-checks against files
that do not exist yet. Running `build` first makes all of them pass, and
Nx labels the same four as flaky. Unrelated to this change.

## Blocked on the docs promote

`docs.copilotkit.ai` has not been promoted to production since #6818
merged. Right now production serves `/premium/*` directly with a 200 and
returns 404 for every `/intelligence/*` path. Staging serves the new
paths with a 200 and redirects `/premium/overview` to
`/intelligence/overview` in one hop, so the code is right and the deploy
is pending.

CodeRabbit flagged this and it is correct. Promote `shell-docs` to
production before the next monorepo release, or these links 404 for
anyone who clicks them. Merging is safe on its own, because the packages
only reach users through a release.

## Companion change

The Intelligence repo carries the same rename for the CLI onboarding
prompt and the verify hint: CopilotKit/Intelligence#1117. The two ship
independently.

Refs OSS-1085
2026-09-03 01:39:53 +02:00
tylerslaton 8feaa1e196 chore: release channels v0.9.2 2026-09-02 23:19:41 +00:00
tylerslaton ddfc2605ff chore: release channels v0.9.1 2026-09-02 22:09:52 +00:00
github-actions[bot] a31daf3c78 style: auto-fix formatting 2026-09-02 15:21:27 +00:00
Alem Tuzlak 808113b923 fix(core): keep webmcp registrations stable across stale rejections and reactive changes 2026-09-02 17:19:34 +02:00