Three review findings, all valid.
The secret-column callout claimed memory vectors are 'withheld at the
database grant, so no combination of scopes returns them', and the exports
section says an export returns them with memory:content. Both cannot be
true, and the callout was the wrong one: the embedding is not in the
model's neverProject list and migration 039 withholds columns only on
api_keys, query_api_keys, organization_oidc_connections,
managed_intelligence_sessions and channel_adapter_credentials.
That mattered beyond tidiness. It told a customer their vectors were
unreachable when an export is the documented way to take them, which is the
opposite of the point of exporting them at all. The callout now separates
credentials, which really are unreachable, from the vector, which is the
customer's own data withheld inline and available on export.
Also: 'Both have to allow the read' followed a list of three scope
families, so it named the scope and the licence without saying so. And the
success and error envelopes key their correlation ids differently,
meta.request_id versus a top-level requestId, which a client logging ids
for every response needs to know.
Lists all nine tools rather than the original four, and explains that
memory recall searches by meaning across every user in the project, since
the key is an organization credential that already lists those same rows.
Adds expand to the parameter table and a section covering the shape rules,
that an empty result is an empty array rather than an absent key, and that
each side is fetched separately so the expanded resource is gated by its own
scopes.
The page explained how to use a key and never how to obtain one, which made
the first step of every workflow the undocumented one.
Adds the CLI commands, the fact that the token is shown once and is not
recoverable, and that omitting --scopes gives every scope.
Adds the two learning resources that now carry data: skills split into
heading-path sections at publish time, and a record of which skill revision
reached an agent in which thread.
States the instrumentation caveat plainly: a zero-usage answer for a period
before the instrumentation existed is indistinguishable from a skill nobody
used, so the earliest occurred_at is worth checking before concluding a
skill was never loaded.
Adds the exports section: the three formats, the job lifecycle, the quota,
and why the memory vector is available there and nowhere else.
Also states that scopes apply on the SQL surface, which they now do:
a statement selecting a gated column with a key that lacks its scope is
refused before it runs, whether the column is named directly, reached
through SELECT *, used in a WHERE clause, or read inside a CTE.
Adds start and end to the list parameters, and states that a parameter the
API does not implement is refused by name rather than accepted and ignored,
so a request that returns rows is a request that did what was asked.
Explains how to read every piece of your own Intelligence data from a
script, a notebook, or an agent, without a browser session: the resource
catalog, the scope model and what :read and :content each unlock, list and
filter syntax, keyset paging, declarative aggregation, arbitrary read-only
SQL with frozen-result paging, and the four MCP tools.
States the things that are easy to get wrong and expensive to discover
late: that a measure's filter scopes to that measure alone rather than
narrowing the whole query, that the organization is never a parameter you
can pass so a SQL statement with no WHERE clause still reads one tenant,
that a field omitted for scope is named in the response while a field named
in `fields` that does not exist is an error, and that secret columns are
unreachable at any scope.
Every route, parameter, operator, aggregate, error code and header on this
page was exercised against a running deployment rather than read out of the
source.
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>