`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`.
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.
`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>
Announce recommendation selection without invalid active descendants, omit
missing controlled elements, and cover empty-result and result-slot behavior.
Report snippet expansion failures and reject partially staged content roots
before either search index is overwritten.
Validation: 829 tests pass; three Angular/Mastra content failures already
reported in PR #6887 remain. Typecheck and lint pass (existing lint warnings).
Browser-verified local search, keyboard selection, and recommendation navigation.
Closes OSS-1134.
## Problem
`useAgentContext` calls `JSON.stringify` on any non-string `value`
before the run leaves the browser
([`use-agent-context.tsx:29-34`](https://github.com/CopilotKit/CopilotKit/blob/main/packages/react-core/src/v2/hooks/use-agent-context.tsx#L29-L34)),
because the AG-UI protocol types `Context.value` as `z.string()` on both
ends. The Python SDK only calls `model_dump()`, so the value reaches
`state["copilotkit"]["context"]` as a JSON string with no parsing
anywhere in between.
The four reference pages have said so since b18f7054af (OSS-1003). The
**integration guides** — the pages a reader actually follows — did not,
and they walked the reader straight into the trap:
- register `colleagues`, an array of objects
- read `.get("value")` and interpolate it into an f-string
An f-string hides the type completely, because a JSON string formats
without complaint. A reader who wants `colleagues[0]["name"]` gets `'['`
instead, and the failure reads as "the frontend sent nothing" —
indistinguishable from an empty context.
## What changed
Documentation only. The wire format does not change: the string is the
protocol.
**One shared callout, eight pages.** New snippet
`snippets/shared/basics/agent-context-json-string.mdx`, imported by
every `agent-app-context` variant before its first code sample, so the
wording cannot drift. Three pages (`adk`, `crewai-flows`, `pydantic-ai`)
already stated the contract, but only *after* their first code sample,
where a reader skimming to the code misses it.
**Four examples were wrong, not just undocumented:**
| Page | Defect | Fix |
|---|---|---|
| langgraph (Python) | interpolated the raw string | `json.loads`, then
reads `c["name"]` so the reason to parse is visible |
| langgraph (TypeScript) | `find` predicate was `'The current user\'s
colleagues"'` — a stray quote that could never match | predicate
corrected, then `JSON.parse` |
| mastra | `JSON.stringify(item?.value)` on an already-encoded value →
double encoding | parses instead |
| ag2 | returned the raw string at three sites, one annotated `->
list[dict]` | all three parse |
The langgraph TypeScript predicate bug meant that example could not have
worked as printed, independently of the JSON issue.
Reference pages are untouched — all four already cover this correctly.
## Testing
**Acceptance criteria, checked mechanically against the final content:**
```
=== AC1: callout precedes the first code fence, all 8 variants
PASS adk PASS ag2 PASS built-in-agent PASS crewai-flows
PASS langgraph PASS mastra PASS ms-agent-fwk PASS pydantic-ai
=== AC2: no context value interpolated without a parse
PASS (all 8; every ag2 extraction assigns to `raw`, then json.loads(raw))
=== AC3: nothing stringifies an already-string value
PASS (swept every .mdx under docs/ and snippets/)
```
**The edited code actually runs.** Executed the Python and TypeScript
fragments as printed:
```
compiles: langgraph / ag2 get_readable / ag2 list_colleagues
round trip OK; None default still absorbed by the call site's `or []`
chat_node -> 'John Doe (Developer), Jane Smith (Designer)'
mastra -> 'John Doe (Developer), Jane Smith (Designer)'
pre-fix colleagues[0] was '[' (a single character)
pre-fix mastra output: "[{\"id\":1,\"name\":\"John Doe\",\"role\":\"D... (double encoded)
pre-fix langgraph TS find predicate matched: false (was always undefined)
```
The last three lines reproduce the reported failure and both latent
bugs, then show them fixed.
**MDX renders.** All 9 files compiled through the real pipeline
(`inlineSnippets` → `convertTablesInJSX` → `@mdx-js/mdx`), with zero
snippet-resolution warnings:
```
PASS × 9 snippet warnings: (none)
```
**Test suite — failure-set diff, not a bare pass.** This environment has
a known install-staleness gap (`@clerk/nextjs`), so I compared against a
pristine-content baseline in the same worktree rather than against zero:
```
baseline (pristine main content): Test Files 14 failed | 68 passed (82) Tests 35 failed
with this change: Test Files 14 failed | 68 passed (82) Tests 35 failed
FAILURE-SET DIFF: IDENTICAL — this change introduces no new failure
```
For reference, the same content passes cleanly where the install is
complete: 51 files / 351 tests, matching its own baseline exactly.
**Mutation-checked the probes** rather than trusting a green light:
breaking the snippet import path fails 1 page, and deleting the callout
text fails all 6 dependents — confirming the snippet is genuinely shared
and the checks can actually fail.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Added guidance across integration guides clarifying that agent context
values arrive as JSON strings.
* Added parsing examples for Python and TypeScript, including
colleague-list formatting.
* Added warnings about avoiding raw access and double-encoding context
values.
* Updated AG2, LangGraph, and Mastra examples to parse context values
before use.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## 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 -->
Leads the "Open, close, and feedback" page with the `open` /
`onOpenChange` pair and an example driving the sidebar from a nav button
outside it, which is the case #3334 asked about. The existing
`useCopilotChatConfiguration` route stays, now framed as the option for
callers who would rather not lift the state.
Also corrects `defaultOpen` on the CopilotSidebar and CopilotPopup
reference pages: both documented `false`, but both surfaces mount open.
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>
## Summary
- carry Intelligence thread, memory, and annotation requests through the
single Runtime endpoint
- advertise the bridge through an optional Runtime info capability
- reuse the existing REST route matcher, handlers, method checks, hooks,
and memory gate
- route Core memory and React annotation calls through the negotiated
Runtime fetch
- make single-route the documented Intelligence quickstart while keeping
multi-route supported
## Compatibility
- Multi-route behavior does not change.
- A new client uses the bridge only when a single-route Runtime
advertises it.
- An old client ignores the new optional capability.
- A new client keeps the old behavior with a Runtime that does not
advertise the capability.
## Validation
- `pnpm nx run-many -t check-types,build
--projects=@copilotkit/shared,@copilotkit/core,@copilotkit/runtime,@copilotkit/react-core`
- package pre-commit gate: tests, `publint`, and `attw` passed for all
affected packages
- Runtime focused suite: 102 tests passed
- Core focused suite: 108 tests passed
- React focused suite: 53 tests passed
- React full suite: 1,591 Vitest tests and 47 script tests passed
- Angular and React memory tests: 18 tests passed
- docs type-check and production build passed
- changed docs contract tests: 33 tests passed
## Local baseline notes
The full docs test command also reads Git LFS images and generated
cross-framework fixtures. It has six unrelated failures in this
checkout: three image-pointer checks, two Angular content checks, and
one Mastra content check. The changed docs tests pass, and the docs
production build passes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added single-route support for thread, memory, and annotation
operations.
- Runtime capability discovery now advertises single-route resource
support.
- Resource requests preserve paths, query parameters, headers, methods,
and request bodies.
- Memory and annotation operations consistently use the configured
runtime transport.
- **Documentation**
- Updated setup guides for single-route configuration, capability
negotiation, and compatibility.
- Added guidance for single-route LangGraph deployments.
- **Tests**
- Added coverage for transport behavior, validation, resource
operations, and error handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Why
Memories ships and is entitlement-gated, but has no conceptual or
activation
documentation on the public site.
The only existing coverage is the Angular headless guide, which shows
`injectMemories` without explaining what a memory is, and the Angular
public API
reference. There is nothing for React, nothing on the three kinds or the
scope
model, and nothing on what a deployment needs before the memory surfaces
exist
at all.
That last gap is the expensive one. A self-hosted operator reasonably
looks for
a `memory.enabled` value or a `MEMORY_ENABLED` variable, finds neither,
and has
no way to discover that access is granted by entitlement and that the
embedder
must be configured separately at startup. This is an initial pass at
closing
that, offered ahead of the planned docs work rather than instead of it.
## What the page covers
An explanation page at `/intelligence/memories`, following the structure
and
voice of `threads-explained.mdx`:
- What a memory is, and how it differs from a thread in lifetime and
purpose
- The three kinds (`topical`, `episodic`, `operational`), and that dedup
and
supersession are same-kind operations, so the kind is not cosmetic
- `user` and `project` scope, with `user` as the platform default
- That saving a near-duplicate absorbs it into the existing memory
rather than
creating a second one
- That update is a supersede and a **full replacement**, including that
omitting
`sourceThreadIds` resets rather than preserves them
- That removal retires rather than erases
- Activation: entitlement for self-hosted and managed, the four embedder
variables, the fail-loud startup behaviour, the pgvector requirement,
the
bundled in-cluster embedder and the external-provider switch
- That changing the embedding model changes the vector space, so it is a
migration rather than a config change
- The React (`useMemories`), REST, and MCP surfaces
## Validation
Every claim is taken from the implementation, not from intent. Notably:
- Activation is resolved through the license/entitlement checker,
fail-closed,
and `memory` ships in the enterprise plan
- The embedder variables, the mandatory 1024 dimensions, and app-api's
refusal
to start without valid configuration
- Route list, request limits (`content` 8192 chars, `sourceThreadIds`
100
entries, recall `limit` default 5 capped at 20), strict rejection of
unknown
fields, and the exact response shape (`id`, `kind`, `scope`, `content`,
`sourceThreadIds`, plus `score` on recall and `invalidatedAt` on the
list)
- `useMemories` semantics for `isAvailable` and `realtimeStatus`, and
the
supersede and retire behaviour, which match what the Angular guide
already
documents
Checks run:
- `npm run pretypecheck` in `showcase/shell-docs`; the page indexes as
"Memories & Recall" under the Intelligence section at
`/docs/intelligence/memories`
- Pre-commit `check-intelligence-env-names` passes, which independently
confirms
the documented environment variable names are canonical
- All in-page links point at paths already used elsewhere in the docs. I
deliberately did not link a generated `injectMemories` reference URL,
since no
such page exists in content; the Angular guide and public API reference
are
linked instead
## Notes for review
- I used `feature="learning"` on `IntelligenceOnboardingPrompt`, because
the
component's feature union is `"learning" | "threads"` and adding a third
value
felt out of scope for a docs change. Happy to add `"memories"` if you
would
rather it read that way.
- The Inspector memory surface is deliberately not described, only
referenced,
since that UX is changing.
- Nothing in the existing Angular guide is contradicted; where we
overlap, the
wording agrees.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Added documentation for the Memories & Recall feature, including
memory types, scopes, recall behavior, updates, and removal.
* Documented activation requirements and configuration for managed and
self-hosted deployments.
* Added guidance for using memories with React, Angular, REST, and MCP
integrations.
* Added the Memories page to the Intelligence documentation navigation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
`useAgentContext` calls `JSON.stringify` on any non-string `value` before the
run leaves the browser, because the AG-UI protocol types `Context.value` as
`z.string()` on both ends. The reference pages say so since b18f7054af, but the
integration guides -- the pages a reader actually follows -- did not, and they
walked the reader straight into the trap: register `colleagues`, an array of
objects, then read `.get("value")` and interpolate it into an f-string, which
hides the type completely because a JSON string formats without complaint.
A reader who wants `colleagues[0]["name"]` gets a single character instead, and
the failure reads as "the frontend sent nothing" -- indistinguishable from an
empty context.
Every one of the eight variants now carries the same callout before its first
code sample, from one shared snippet so the wording cannot drift. Three pages
(adk, crewai-flows, pydantic-ai) already stated the contract, but only after
their first code sample, where a reader skimming to the code misses it.
The examples now show the round trip honestly rather than hiding it:
- langgraph Python parses with `json.loads` and then reads `c["name"]`, so the
reason to parse is visible.
- langgraph TypeScript parses with `JSON.parse`. Its `find` predicate was also
`'The current user\'s colleagues"'` -- a stray quote that could never match
any description -- so the example could not have worked as printed.
- mastra passed the already-encoded value back through `JSON.stringify`,
producing double encoding. It now parses instead.
- ag2 returned the raw string from `get_readable` at three sites, one of them
annotated `-> list[dict]`, which the function never returned.
Documentation only. The wire format does not change: the string is the protocol.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pin Intelligence in the header and sidebar, add an Explore docs mega menu, and collapse top-level sidebar sections so people do not have to scroll past a long list to reach Intelligence.