mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
main
3119 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
290a8323ae | fix(runtime): expose Inspector Learning without extra flags | ||
|
|
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> |
||
|
|
8757ef0a80 | fix(runtime): accept nullable frontend tool schemas | ||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 (
|
||
|
|
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 --> |
||
|
|
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> |
||
|
|
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.
|
||
|
|
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 --> |
||
|
|
175e9c237c | chore: release angular v0.5.2 | ||
|
|
16514e9424 | chore: release monorepo v1.70.2 | ||
|
|
9d63fb00bc | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
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). |
||
|
|
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 --> |
||
|
|
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> |
||
|
|
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`. |
||
|
|
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. |
||
|
|
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> |
||
|
|
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>
|
||
|
|
1a2f5691b6 | Merge upstream/main into fix/copilotkit-6888-sse-binary | ||
|
|
0d46ef5fe7 | Merge branch 'main' into feat/angular-copilot-activity | ||
|
|
1c2df1d64b | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
5571eeafda | test(angular): await slot rendering with signal-based fixtures | ||
|
|
8100f08c5f | fix(angular): render slots with createComponent bindings | ||
|
|
43f1e81ce6 | test(channels-telegram): cover telegram-html edge cases (bold/italic/headings/bullets/code) | ||
|
|
1fdca1dc9e | fix(inspector): harden Learning review flows | ||
|
|
cfc4fd8e26 | fix(inspector): refine Learning onboarding flow | ||
|
|
c70502b137 | feat(inspector): add Learning view and workbench | ||
|
|
05fc4e05a4 | feat(runtime): expose Learning snapshots to Inspector | ||
|
|
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 -->
|
||
|
|
988fa60f70 | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
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 --> |
||
|
|
79fdeb64c2 | fix(runtime): encode SSE response chunks as bytes | ||
|
|
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.
|
||
|
|
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. |
||
|
|
4b01c7b21c | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
51f1f0f09a | chore(examples): declare the web-inspector dependency in the angular demo and storybook fixes | ||
|
|
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. |
||
|
|
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
|