3119 Commits

Author SHA1 Message Date
Tyler Slaton 290a8323ae fix(runtime): expose Inspector Learning without extra flags 2026-09-08 15:51:58 -07:00
Benjamin Taylor b0f349fcfb fix(a2ui): report a surface whose root component never resolves (closes OSS-1057)
Both renderers begin walking a surface at the component with id "root" and
treat an id they cannot find as not arrived yet, painting an animated
placeholder. That is right while operations stream. Once operations have
stopped it is not waiting, it is stuck — and every existing check calls it
healthy: the surface exists, processMessages does not throw, the component
type is never reached so the "Unknown component" branch cannot fire, and
surfaceHasRenderableContent says yes on the strength of components plus a
non-empty data model, so onReady fires and the never-painted report is
suppressed by its own guard. A complete, accepted payload therefore animates
a grey box forever with nothing in the console.

Reports it on the existing paint deadline, measured from the last operations
to land, so a root still missing when it expires is a root that is not
coming. The check reads the live components model rather than scanning the
operations for the id, which covers every way a root can fail to resolve —
not only a payload that never named one — and the message says which of the
two it is.

Keeps the fixed root id: A2UI v0.9 dropped v0.8's rootComponentId, and
createSurface carries only surfaceId, catalogId and theme, so a payload has
no way to declare its own entry point. Deriving one instead would pick
silently and wrongly whenever several components are unreferenced. The id
moves to a single ROOT_COMPONENT_ID constant so the three sites that
hard-coded the string, and the new report, agree by construction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 17:49:22 -05:00
Mike Ryan 8757ef0a80 fix(runtime): accept nullable frontend tool schemas 2026-09-08 15:45:17 -07:00
Benjamin Taylor 77dc28ea2c fix(a2ui): resolve a surface id the same way in both renderer paths (refs OSS-1048)
The two paths disagreed. React read a top-level `operation.surfaceId` first and
only then the nested v0.9 keys. The web-components path read the nested keys
only, via normalizeOperations, and never looked at a top-level id at all. So
one payload grouped under its own id in React and under "default" in the Lit
and Angular renderers.

Nested wins in both now, with a top-level id as the fallback when the payload
carries none.

Nested is the correct half of that choice, not a coin toss: MessageProcessor
creates the surface from the nested id. Grouping by a top-level id instead
files the operations against a surface that createSurface never made, and an
unknown surface id renders A2UIRenderer's null fallback. That is a card that
paints nothing, which is what the previous commit taught the renderer to
report.

So the old React order could produce the silence, and the missing-surface
report is what the new test uses to prove the grouping agrees with what got
created.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 17:42:52 -05:00
Benjamin Taylor 0f7ee3501a fix(react-core): report an A2UI card that receives operations and paints nothing (refs OSS-1048)
Two ways an A2UI surface can render nothing without saying a word.

The operations arrive and no paint follows. The renderer already waits 8s for
the surface to report its first paint before dropping the loader, so reaching
that fallback is itself the signal that nothing painted. Report it there, and
use what surfaceHasRenderableContent already knows to say which half is
missing: no updateComponents at all, or bound components whose updateDataModel
never carried a value.

The operations name a surface that was never created. A2UIRenderer renders its
fallback for an unknown surface id and that defaults to null, so the card is
absent and the log is empty. processMessages is synchronous, so a surface
still missing after it was never created.

The second report is deferred a task and re-checked, because operations stream
and a snapshot can reach the processor before the createSurface that gives it
somewhere to go. Removing both the deferral and the re-check makes the
mid-stream test fail.

Neither report covers a surface that exists and holds complete components and
still draws nothing. That case needs the component catalog, which lives in
@a2ui/web_core, and it stays silent for now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 17:42:52 -05:00
Benjamin Taylor 22fa691e80 fix(react-core): warn when a tool call has no renderer instead of rendering nothing (refs OSS-1048)
A tool call that matches no registered renderer returns null. Nothing else
happens: no console output, no empty-state card, and a finished turn. The only
signal is a blank message container in the chat, which a developer has to
notice in the DOM and then guess at.

Report it in development. The warning names the tool the agent called, lists
the renderer names that are registered, and points at useRenderTool and
useDefaultRenderTool. When the cause is a name that does not match, that is
the whole diagnosis.

Rendering behavior is unchanged. Auto-painting a default card would leak tool
names and raw args into production chat, which is why the resolver returns
null, and that decision stands.

The report is deferred one task past the commit that recorded the miss, then
re-checks the registry. useRenderTool registers from an effect in the
component that renders the chat, and React runs child effects before parent
ones, so at effect time the resolver can see an empty registry even though the
app did register a renderer. Removing that re-check makes two of the new tests
fail on exactly that false positive.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 17:42:52 -05:00
Benjamin Taylor 2031643fa1 fix(react-core): wait for the user on humanInTheLoop provider tools
The `humanInTheLoop` prop on `CopilotKitProvider` registered a handler that
warned and resolved `undefined` the moment the agent invoked it, and it
registered the renderer unwrapped, so the render never received a working
`respond`. A tool declared that way jumped straight to Complete over dead
controls while the agent was told the tool had succeeded. The placeholder is
unchanged since the first v2 provider commit (a2ef51aaf3), so this documented
prop has never waited on the user. It fails the same way on both transports.

Match the `useHumanInTheLoop` semantics: park the tool call until the render
calls `respond`, expose `respond` only while the status is Executing, and
reject with 'Human-in-the-loop interaction aborted' when the run is aborted so
core records an explicit error tool result (#5554). Pending interactions are
keyed by tool call id, and a wildcard registration keeps the invoked tool's
name rather than "*".

Two existing unit tests asserted the placeholder behavior (the console warning
and render identity). They now assert the waiting contract and that the wrapper
renders the caller's component.

Mirrors the Vue fix in #6527. Related: #4953 (the provider-prop half of that
report) and #4955.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 17:31:28 -05:00
Ben Taylor 407249d4fc fix(react-core): let a custom catch-all renderer return null (#6942)
## Summary

`useDefaultRenderTool`'s `render` was typed to return
`React.ReactElement`. A caller who wants to render only *some* tool
calls therefore could not return `null` to suppress the built-in default
for the rest — the value flowed through correctly at runtime, but the
type rejected it.

This widens the public `render` return type, and the wrapper local that
carries the user's value, to `React.ReactElement | null`.

```diff
-    render?: (props: DefaultRenderProps) => React.ReactElement;
+    render?: (props: DefaultRenderProps) => React.ReactElement | null;
```

The reference page hand-writes the same signature, so it is updated to
match, with one behavior bullet describing what `null` does.

## Scope, and its relationship to #6533

#6533 already widens the same return type to `React.ReactElement | null`
in `defineToolCallRenderer.ts` and `use-render-tool.tsx`. It does
**not** touch `use-default-render-tool.tsx`, which is the remaining gap
and the whole of this PR. There is **no file overlap**, so the two can
land in either order.

A structural sweep of `react-core/src/v2` for renders still typed `=>
React.ReactElement` with no `| null` confirms this leaves nothing behind
on this surface:

```
types/defineToolCallRenderer.ts:40,48,56   <- #6533
hooks/use-render-tool.tsx:41,72,109        <- #6533
hooks/use-default-render-tool.tsx:152      <- the deliberate bridge cast, below
hooks/use-interrupt.tsx:89                 <- different surface, out of scope
components/chat/CopilotChatMessageView.tsx:418  <- different surface, out of scope
```

The `as unknown as` cast into `useRenderTool` is deliberately left in
place: `useRenderTool` still requires a `ReactElement` return on `main`
(verified again after the rebase — `use-render-tool.tsx:41`). Once #6533
lands, that cast can be tightened. The bridge comment is updated to say
so.

`DefaultToolCallRenderer`'s own return type stays `React.ReactElement` —
the built-in default always renders an element.

The Vue counterpart needs no equivalent change: its `render` already
returns `VNodeChild`, which admits `null`, and
`reference/vue/hooks/useDefaultRenderTool.mdx` already matches.

## Why the guard is a type test, not a runtime test

TypeScript types are erased, so a `null`-returning render forwards
identically before and after the widening. The runtime test passes
against un-widened source, which makes it worthless as a guard for this
change. So the real guard is `use-default-render-tool-types.test-d.ts`,
using the `expectTypeOf` + `toEqualTypeOf` convention already documented
in `v2/__tests__/headless-type-exports.test-d.ts`.

`toEqualTypeOf` is required rather than assignability: a function
returning `ReactElement` **is** assignable to one returning
`ReactElement | null`, so an assignability check would pass against the
un-widened type and assert nothing.

The `.test-d.ts` basename is outside vitest's `include` globs, so
nothing there executes; `tsc --noEmit` (`check-types`) is what reads it.
Confirmed on this base:

```
$ vitest list --filesOnly | grep -c "test-d"
0
$ grep -n include -A4 packages/react-core/vitest.config.mjs
    include: [
      "src/**/__tests__/**/*.{test,spec}.{ts,tsx}",
      "src/**/*.{test,spec}.{ts,tsx}",
    ],
$ grep include packages/react-core/tsconfig.json
  "include": ["src/**/*"],
```

The runtime test is kept as well, since it still covers prop adaptation
and forwarding.

## Testing

All numbers below were re-measured after the rebase onto `main`
(`42494df`).

**Mutation check of the type guard** — revert the widening in the
source, confirm the guard goes red:

```
########## RUN A: rebased HEAD as-is ##########
total errors: 63
--- errors in touched files ---
  none

########## RUN B: MUTATION - widening reverted in source ##########
total errors: 65
--- guard file errors (expect FAIL) ---
use-default-render-tool-types.test-d.ts(26,3): error TS2344:
  Type '((props: DefaultRenderProps) => ReactElement<...> | null) | undefined'
  does not satisfy the constraint
  '"Expected: undefined, Actual: never" | "Expected: function, Actual: never"'.
use-default-render-tool.test.tsx(150,30): error TS2322:
  Type 'Mock<({ status }: DefaultRenderProps) => null>' is not assignable to
  type '(props: DefaultRenderProps) => ReactElement<...>'.
  Type 'null' is not assignable to type 'ReactElement<...>'.
```

The guard fails when the widening is reverted, and the two new errors
are exactly the guard plus the runtime test's own use of it. Nothing
else moves.

**Typecheck** (`tsc -p packages/react-core --noEmit`) — error set
byte-identical to pristine `origin/main` in the same worktree, none in
the touched files:

```
########## RUN C: pristine origin/main baseline ##########
total errors on pristine main: 63
=== diff: pristine-main errors vs HEAD errors ===
IDENTICAL -> the change introduces no new type errors
```

The 63 are pre-existing worktree noise: `react-core` resolves
`@copilotkit/core` and `@copilotkit/shared` from a sibling checkout's
`dist`, so unrelated exports read as missing. They are present on
pristine `origin/main` in the same worktree, which is what the diff
above shows.

**Target test file:**

```
 ✓ src/v2/hooks/__tests__/use-default-render-tool.test.tsx (13 tests) 38ms
 Test Files  1 passed (1)
      Tests  13 passed (13)
```

**Broader `src/v2/hooks` + `src/v2/types`** — failure counts identical
to pristine `origin/main` in the same worktree, plus exactly the one new
passing test:

```
=== BASELINE (pristine origin/main in this worktree) ===
 Test Files  22 failed | 17 passed (39)
      Tests  9 failed | 201 passed (210)

=== WITH my change ===
 Test Files  22 failed | 17 passed (39)
      Tests  9 failed | 202 passed (211)
```

**Lint:** `oxlint packages/react-core/src/v2/hooks/` — `Found 62
warnings and 0 errors.` (all pre-existing exhaustive-deps warnings, none
in the touched files).

**Formatting:** `oxfmt --check` on the three source/test files — `All
matched files use the correct format.` `oxfmt` does not process `.mdx`,
so the reference page is out of its scope.

**Public-API manifest:** no regeneration needed —
`scripts/release/public-api/manifest.v1.json` records no type signatures
and does not mention `useDefaultRenderTool` (`grep -c ReactElement` →
`0`).

## Provenance

Extracted from #5509 (@ataibarkai), which is 3915 commits behind `main`
and being closed. That PR also changed `defineToolCallRenderer`'s schema
default from `def.name === "*" && !def.args ? z.any() : def.args` to
`def.args ?? z.any()`. **That change is deliberately not carried here**
— it alters runtime behavior for named renderers declared without `args`
(from `args: undefined` to `args: z.any()`) and deserves its own PR and
its own verification.

🤖 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**
* Custom renderers can now return `null` to suppress output when no UI
should be displayed.

* **Documentation**
* Updated `useDefaultRenderTool` guidance to describe null-return
behavior and selectively rendering tool calls.

* **Tests**
* Added coverage confirming null-render behavior and forwarded renderer
properties.
  * Added compile-time validation for supported renderer return types.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 17:27:44 -05:00
Benjamin Taylor 82cd7c01f7 fix(react-core): let a custom catch-all renderer return null
`useDefaultRenderTool`'s `render` was typed to return `React.ReactElement`,
so a caller who wanted to render only some tool calls could not return
`null` to suppress the built-in default for the rest. The value already
flowed through correctly at runtime; only the type rejected it.

Widen the public `render` return type, and the wrapper local that carries
the user's value, to `React.ReactElement | null`. The `as unknown as` cast
into `useRenderTool` stays, because `useRenderTool` still requires a
`ReactElement` return on main; PR #6533 widens that hook, after which the
cast can be tightened.

Guarded by a `.test-d.ts` assertion rather than a runtime test: types are
erased, so a null-returning render forwards identically before and after
the widening and a runtime test would assert nothing.

Extracted from #5509, which is otherwise stale.

Co-authored-by: Atai Barkai <atai.barkai@gmail.com>
2026-09-08 16:39:06 -05:00
Benjamin Taylor 1a935861a8 fix(runtime): do not treat an unread stream as a pre-parsed body in the express bridge
`hasPreParsedBody` gates on `req.body` being set, then confirms the stream is
gone via `req.readableEnded || req.complete || _readableState.ended ||
_readableState.endEmitted`. The last three are set by the Node HTTP parser once
the socket holds every byte, whether or not anything read them, so they do not
establish that a parser ran.

That matters because `req.body` being set does not establish it either.
body-parser 1.x (Express 4) assigns `req.body = req.body || {}` before its own
`hasBody`/`shouldParse` checks, so a request it declines to parse — multipart
upload, text/plain — reaches the bridge with `req.body === {}` and a full,
unread stream. `req.complete` then satisfied the check, the bridge rebuilt the
request from `{}`, and the real payload was silently dropped.

Gate on `readableEnded` alone, which only becomes true after a parser drains the
stream to its end. Verified on express 4.22.2 / body-parser 1.20.6 that a
multipart POST behind a global `express.json()` arrives with `req.body === {}`,
`readableEnded === false`, `complete === true`, and that the genuinely parsed
JSON case is unaffected.

Same root cause as #6489, which fixed the equivalent check in the node-http
request handler. Kept as a separate local predicate rather than sharing one
helper, to avoid coupling `v2/runtime` to the v1 integration tree.
2026-09-08 16:32:38 -05:00
Ben Taylor 42494df607 docs(react-core): stop pointing v1 appendMessage at non-public sendMessage (#6940)
## Summary

The v1 `useCopilotChat` JSDoc tells readers to use `sendMessage` instead
of `appendMessage`. `sendMessage` is not part of the public v1 return
type, so following that advice does not compile.

`packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts:109`
explicitly omits it:

```ts
export type UseCopilotChatReturn = Omit<
  UseCopilotChatReturnInternal,
  | "messages"
  | "sendMessage"     // <- the JSDoc points readers here
  ...
```

`sendMessage` exists only on `useCopilotChatInternal`. The public hook
returns `appendMessage`, which is the working v1 programmatic-send path.

This replaces the misdirection with the v2 migration pointer and states
plainly what `appendMessage` is for. Comment-only change — no runtime
effect.

Found while closing #4215, where a user was told by our own docs to call
a method we do not export.

## The published page does not change yet

This fixes the source of truth. The generated page cannot be refreshed
until #6939 is resolved: running the generator today would also embed
the internal v1 deprecation banner ("AI CODING AGENTS: Never copy,
suggest, or generate these v1 APIs") into 20 public reference pages. I
deliberately excluded the regenerated `.mdx` files from this PR rather
than ship that. Once #6939 lands, a regenerate publishes this wording.

## Testing

**1. `oxfmt --check` — pass**

```
$ ./node_modules/.bin/oxfmt --check packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts
Checking formatting...
All matched files use the correct format.
Finished in 33ms on 1 files using 18 threads.
```

**2. Generator reads this file successfully (26/26)** — confirms the
JSDoc edit is picked up, and that the only thing blocking publication is
#6939, not this change:

```
$ ./node_modules/.bin/tsx scripts/docs/gen.ts
Successfully autogenerated showcase/shell-docs/src/content/reference/v1/hooks/useCopilotChat.mdx from packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts
All reference docs processed (26/26 succeeded)
```

The regenerated page contained the new wording as expected; I then
reverted the generated files per the section above.

**3. Diff is a single comment hunk** — verified with `git diff --stat`:
`1 file changed, 4 insertions(+), 1 deletion(-)`, all inside a JSDoc
block. No exported symbol, type, or runtime line touched, so no
typecheck or test surface is affected.

**4. Claim verified against `origin/main`,** not a local branch: `git
show
origin/main:packages/react-core/src/v1-deprecated/hooks/use-copilot-chat.ts`
confirms both the misdirecting line (`:70`) and the `Omit` (`:109-121`).

## Notes

- No changeset — comment-only.
- Branch name is a leftover misnomer
(`ben1/oss-docs-generator-v1-paths`); the change is the wording fix
only.
- @ataibarkai's #6653/#6654/#6655 stack would move this file back to
`packages/react-core/src/hooks/`. If that stack lands, this one-line
change needs carrying forward into the reapply.


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

## Summary by CodeRabbit

- **Documentation**
  - Clarified the deprecated `appendMessage` option in `useCopilotChat`.
  - Documented its role for programmatic sending in v1.
  - Clarified the migration path for AG-UI format users moving to v2.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 16:16:43 -05:00
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
Yann Jouanin 9d63fb00bc Merge branch 'main' into feat/mcp-apps-renderer 2026-09-08 15:02:24 +02: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
Benjamin Taylor 0217f45d73 docs(react-core): stop pointing v1 appendMessage at non-public sendMessage
The v1 `useCopilotChat` JSDoc told readers to use `sendMessage` instead of
`appendMessage`, but `UseCopilotChatReturn` omits `sendMessage` from the
public return type, so following that advice does not compile. Point at the
v2 migration path instead, and state that `appendMessage` is the public v1
programmatic-send path.

Reported via #4215.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 13:21:39 -05: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
Cursor Agent 6aa8725b83 fix(runtime): pass SSE MCP headers via requestInit
SSEClientTransport's second argument is options, not a raw header map.
Passing `{ Authorization: ... }` was silently ignored, so documented
mcpServers auth never reached the wire.

Fixes #6927

Co-authored-by: Zsanz3 <Zsanz3@users.noreply.github.com>
2026-09-07 02:16:05 +00: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
Yann Jouanin 1c2df1d64b Merge branch 'main' into feat/mcp-apps-renderer 2026-09-06 15:00:31 +01: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
Yann Jouanin 988fa60f70 Merge branch 'main' into feat/mcp-apps-renderer 2026-09-04 23:47:39 +01: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
yannj-fr a006eaa66b docs(mcp-apps-renderer): add docstrings + fix bundle-size package count
Address CodeRabbit's re-review of the review-response commit:
- correct the static_bundle_size.yml comment ("10" -> "11" in-scope packages)
  now that the renderer is in the measured pattern
- add docstrings to the functions flagged by the docstring-coverage check
  (buildSandboxHTML, MCPAppsRequestQueue#processQueue/#waitForAgentIdle, and the
  bindMcpApp flushPending/fetchResource/setup closures)

Comment/docstring only; 16 tests green, check-types + oxlint clean.
2026-09-04 23:01:33 +01:00
yannj-fr 65507be3a7 fix(mcp-apps-renderer): address review on the React-pilot PR
Bridge-free /activity (review blocker): the `./activity` entry pulled ext-apps'
LATEST_PROTOCOL_VERSION through constants.ts, statically dragging the App SDK +
zod/v4 (~+55 kB gzip) into react-core's eager chunk. Move MCP_APPS_PROTOCOL_VERSION
to the bridge side (session.ts), sourced from the /app-bridge subpath the session
already imports - single source of truth, no hand-maintained literal, zero extra
cost. dist/activity.mjs and dist/constants.mjs now carry no @modelcontextprotocol
edge.

Keep react-core's zod peer at >=3.25 (review blocker): the floor moved down to the
package, it did not go away; ext-apps + the MCP SDK both hard-require it, so >=3.0.0
is an ERESOLVE install failure once an MCP App renders.

Drop the <copilotkit-mcp-app> web component from this PR: nothing consumes it here
(the React adapter builds its own iframe) and it carried two open defects. It lands
with the Vue/Angular adapters, its first real consumers. Removes its files, exports,
tsdown entry, and the now-unused lit dependency.

Also: add content.serverId to the bind effect deps; delete drained thread entries
from the request queue's maps (bounded growth); register the package in
static_compat.yml (+ a compat-check script) and static_bundle_size.yml; publint
repository.url -> git+https. Manifest regenerated.
2026-09-04 22:25:38 +01:00
Yann Jouanin 4b01c7b21c Merge branch 'main' into feat/mcp-apps-renderer 2026-09-04 21:55:55 +01: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