mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
main
722 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
633b0a65be | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
92f704e3b4 | chore: release monorepo v1.71.1 | ||
|
|
b6b60d4a6f | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
6c25273afe |
refactor(skills): replace nine knowledge skills with two entry points
The packaged skills had grown into a second copy of the documentation. `runtime` and `react-core` were roughly 60% transcribed API surface, and most of their remaining "Common Mistakes" prose already existed on a docs page. A cached copy of an API goes stale silently: four claims in the deleted skills contradicted the source they cited, and one of them reached a shipped PR before it was caught. Replace them with two skills that look the answer up instead of restating it: - `copilotkit` — the four search tools and two explore tools of the bundled `copilotkit-docs` MCP server, which corpus answers which question, and the instruction not to answer from memory. - `copilotkit-cli` — the CLI, led by `copilotkit verify --json`. Since #1180 `verify` covers version skew, CORS, and transcription, which is what most of the old `copilotkit-debug` skill described by hand. Deleted: copilotkit-setup, copilotkit-develop, copilotkit-integrations, copilotkit-debug, copilotkit-upgrade, copilotkit-agui, copilotkit-contribute, copilotkit-self-update, and the three package-generated skills (react-core, runtime, a2ui-renderer). The `skills` directory is dropped from the `files` field of the three packages that shipped one, so the tarballs no longer carry a copy. `public-skill-drift.test.ts` guarded wording in files that no longer exist. It is now a link guard: every `docs.copilotkit.ai` path named by a packaged skill has to resolve to a page in this repo, and the two entry points have to stay free of a transcribed API surface. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c4dc58102b | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
35c1e801ae |
fix(react-core): stop useInterrupt re-renders on agent updates (#6969)
Closes #6934. The three red langgraph-* checks are unrelated: ag-ui commit 24545e119c (2026-09-09) added a guard rejecting Git LFS pointer files, and this repo's test_e2e-dojo.yml checks out ag-ui without lfs: true. The same test passed on 2026-09-08. Tracked separately. |
||
|
|
f8520c9804 |
fix(skills): audit react-core claims, and stop documenting Cloud keys as the Intelligence path (#6997)
Follow-up to #6993. That PR fixed two `react-core` references; four of their six citations were stale, so this audits the other 13. ## Method Three passes, because the first two are cheap and the third is the only one that finds real defects. 1. **Mechanical** — every `packages/...:NN-MM` citation: does the path exist, is the span in range? 71 citations, 57 with spans. 2. **Symbol** — every `useXxx(` and `<CopilotXxx` in the docs against the 389 identifiers actually exported from `v2/src`. 3. **Semantic** — read the cited lines and check they support the claim. This is where the rot lives: the lines exist, they just say something else. ## Findings fixed | File | Defect | | --- | --- | | `capabilities.md` | Cited `runtime/src/agent/index.ts:821-829,883-887` for "shallow-merges capabilities at the category level". Those are factory-mode config types and sampling params (`frequencyPenalty`, `stopSequences`). The mechanism is `:940-947` — whose own doc comment says **shallow-merged** — and `:999-1012`. | | `provider-setup.md` | Claimed the provider resolves `publicLicenseKey \|\| publicApiKey`. Only one of four sites does. Citation also pointed at an unrelated line (`copilotkit.tsx:172` is `source: "agent"`). | | `custom-message-renderers.md` | Cited lines 73-95 of a 93-line file. The iterate-and-break it describes is at `:68-91`. | | `suggestions.md` | `useFeatureFlag("suggestions")`, twice, with no import and no definition. CopilotKit exports no such hook. | | `threads.md` | `useThreadSelection()`, same problem. | ### The license-key one is a product finding, not just a docs bug Precedence when both keys are set is inconsistent in the code: | Site | Order | | --- | --- | | `CopilotKitProvider.tsx:487` | `publicApiKey ?? publicLicenseKey` | | `copilotkit.tsx:111` | `publicApiKey \|\| publicLicenseKey` | | `copilotkit.tsx:217` | `publicLicenseKey \|\| publicApiKey` | | `copilotkit.tsx:883` | `publicApiKey \|\| publicLicenseKey` | Three prefer `publicApiKey`, one prefers `publicLicenseKey`. So which wins depends on which path runs. Rather than document one order as if it were the contract, the doc now says to write the canonical name and not to set both — true regardless of path. **The underlying inconsistency is untouched here and probably wants its own issue.** ## Findings I retracted Recording these because two of my three automated passes produced false positives, and the ratio matters for anyone repeating this. - **50 "missing paths."** My regex alternated `(?:ts|tsx)`, so it matched `ts` first and truncated every `.tsx`. All 50 were my own artifact. Real count: zero missing paths. - **41 "unsupported spans."** A heuristic checking whether the claim's backticked identifiers appear in the cited lines. It attributes each `Source:` to the nearest paragraph above, which is the wrong one in multi-paragraph gotchas. Useful as a reading list, worthless as a finding. - **"13 uses of the deprecated provider."** Most were the *filename* `CopilotKitProvider.tsx` inside `Source:` citations, not component usages. There are 2 real JSX usages, both passing only `runtimeUrl`, both valid — and `provider-setup.md` already carries a callout explaining that `CopilotKit` is the v1/v2 bridge and `CopilotKitProvider` "is a perfectly good choice if you do not need the v1 bridge". No defect. ## Testing ``` $ python3 audit_citations.py citations: 71 mechanically broken: 0 with a line span: 57 path-only: 14 $ tsx scripts/sync-plugin-skills.ts --check plugin skill mirror in sync $ oxfmt --check skills/react-core/references/*.md All matched files use the correct format. ``` Symbol pass after the fixes leaves only legitimate non-exports: React's own hooks, two helpers the docs define inline (`useAvailableAgents`, `useMyFeatureFlag`), and `useAgents`, which `switching-agents.md` correctly documents as **not existing**. Edits were made in `packages/react-core/skills/` and mirrored by the sync script. ## Coverage, honestly The mechanical and symbol passes cover all 15 files completely. The semantic pass does not: I read roughly 15 of the 57 spans closely, prioritising the files documenting APIs that have moved most. The five defects above are what that subset produced. A full semantic read of the remaining ~40 spans would likely find more, and the same audit has not been run on the `runtime` skill (26 references) or `a2ui-renderer`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Updated React guidance with current source references across capabilities, message renderers, attachments, chat components, client-side tools, debugging, human-in-the-loop rendering, tool calls, agent access, and agent switching. - Clarified that production SPAs require `runtimeUrl`, with Intelligence configured server-side on the runtime using `CPK_INTELLIGENCE_API_KEY`. - Removed outdated `publicLicenseKey` guidance from setup and API documentation. - Corrected feature-flag examples to use an application-defined hook. - Updated thread-selection examples to derive the active thread ID from application state. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ec249f81fd |
fix(skills): correct four defects found reviewing the previous commit
The repo's own guard caught the worst of them. `scripts/validate-intelligence-env-names.ts` retires `INTELLIGENCE_API_KEY`: the canonical name is `CPK_INTELLIGENCE_API_KEY`, and the retired one "produced an undefined key for a reader who followed it with a CLI-provisioned project" (OSS-881). The rewrite reintroduced it at seven sites. Corrected, and the validator's 22 rule tests pass. The same file also stops listing `INTELLIGENCE_API_URL` and `INTELLIGENCE_GATEWAY_WS_URL` as things to set. Both default to the managed hosts when omitted, so any value a reader supplies can only replace a correct default with a worse one. `provider-setup.md` called `agents__unsafe_dev_only` and `selfManagedAgents` "aliases for the same dev-only mechanism". They are not: the first is the free local-dev escape hatch, the second is an Enterprise Intelligence tier feature that warns when used without a license key. I inherited that sentence and made it more confident while editing it. `copilotkit-setup/SKILL.md` Step 6 was left incoherent by the previous commit. Removing the provider example stranded an instruction to "set the public license key and pass it to the provider" above an example that no longer did. The step now ends with `copilotkit verify`, its credential table lists the one server-side credential, and the security note no longer claims a client-side value exists. Two claims of my own were also too absolute: the provider throws only when `runtimeUrl`, a Cloud key, AND dev-only agents are all absent, and `publicLicenseKey` does have a second advisory role gating `selfManagedAgents`. Both now say so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
57eb071cda |
fix(skills): stop documenting Cloud keys as the Intelligence path
`publicApiKey` and `publicLicenseKey` route a runtime-less client at CopilotKit Cloud -- `api.cloud.copilotkit.ai`, header `X-CopilotCloud-Public-Api-Key`. Five skills presented them as the way to connect CopilotKit Intelligence, which they have never done. Intelligence is configured on the runtime. The CLI writes `INTELLIGENCE_API_KEY` into the server environment and it never reaches the browser, so no client-side key is involved at all. What was wrong, beyond the mislabelling: - `provider-setup.md` had a section titled "SPA with CopilotKit Intelligence (no self-hosted runtime)". There is no client-only path: the provider throws in production without `runtimeUrl`, a key, or dev-only local agents. It now says a runtime is required and why. - `telemetry-setup.md` told readers to expose the key with a `NEXT_PUBLIC_` or `VITE_` prefix and claimed removing the prop disconnects Intelligence. Neither is true. Rewritten around the CLI flow, the server-side variables, and `copilotkit verify`. - `error-patterns.md` attributed `MISSING_PUBLIC_API_KEY_ERROR` to Intelligence. It is a Cloud error code. - `copilotkit-setup/SKILL.md` called it the "CopilotKit Intelligence public license key" in its props table and put it in the provider example. - The `react-core` and `runtime` SKILL.md invariants advertised `publicLicenseKey` as canonical, and `react-core` offered it as the SPA alternative to `runtimeUrl`. The naming gotcha comparing the two props is gone rather than corrected: neither belongs in Intelligence guidance. Three mentions remain and all three name Cloud -- two migration-table rows in `copilotkit-upgrade`, which were already right, and one line steering readers away. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
588ea74eb7 |
fix(skills): re-anchor 24 drifted citations across react-core references
Completes the semantic pass: every one of the 57 line-spanned citations in
the 15 references was read against the tree it points at.
24 pointed at lines that had drifted to unrelated code. The claims were
almost all correct -- the pointers were not. Examples:
- `signal` in a tool handler cited `core/src/types.ts:24-30`, the tail of an
export list. It is at `:39-41`.
- The `followUp !== false` check cited `run-handler.ts:607`
(`const agentRunInput = {`). It is at `:970` and `:1119`.
- `FrontendTool.followUp` typed `boolean` cited `types.ts:39`, which is
`signal`. It is at `:78`.
- `useRenderToolCall` takes no arguments cited `use-render-tool.tsx:37-40`,
a config type in the wrong file. The hook is
`use-render-tool-call.tsx:159`.
- Four of five citations in `threads.md` had drifted, one into a JSDoc
example block.
- `showDevConsole` no longer gating the Inspector cited the component's
destructuring; the `@deprecated` prop is at `:197-200`.
Verified correct and left alone, including several my own checker flagged:
`v2/index.ts:1` (`"use client"`), `:3` (`import "./index.css"`),
`use-agent-context.tsx:30-35` (the `JSON.stringify` memo, cited twice and
right both times), `use-render-tool.tsx:8-20` (camelCase status union),
`use-configure-suggestions.tsx:59-62` (`available: "disabled"` normalizing
to null).
All 71 citations now resolve to a real path and an in-range span.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
cac7cde862 | chore: release monorepo v1.71.0 | ||
|
|
26d347cc7b |
fix(skills): correct claims and citations across react-core references
A claim audit of the 13 react-core references not covered by #6993, prompted by four of six citations being stale in the two files that were. Five defects, each verified against the tree: - `capabilities.md` cited `runtime/src/agent/index.ts:821-829,883-887` for "shallow-merges capabilities at the category level". Those lines are factory-mode config types and sampling parameters. The mechanism is at `:940-947`, whose own doc comment says shallow-merged, and `:999-1012`. - `provider-setup.md` claimed the provider resolves `publicLicenseKey || publicApiKey`. Only one of four sites does that: `CopilotKitProvider.tsx:487` and two of the three v1-bridge sites prefer `publicApiKey`. The guidance is now "write the canonical name, do not set both", which is true regardless of path. Its citation also pointed at an unrelated line. - `custom-message-renderers.md` cited lines 73-95 of a 93-line file. The iterate-and-break it describes is at `:68-91`. - `suggestions.md` used `useFeatureFlag("suggestions")` twice with no import and no definition. CopilotKit exports no such hook, so an agent could try to import it. Renamed and annotated as the reader's own. - `threads.md` used `useThreadSelection()`, also fictional. Replaced with reader-owned state. All 71 citations in the 15 references now resolve to a real path and an in-range span. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
c98b7b0b7d |
fix(skills): correct react-core agent access against the shipped API (#6993)
Refs #6125. ## Summary The bundled `react-core` skill documented a pre-`registerProxiedAgent` architecture. It ships inside the `@copilotkit/react-core` tarball via `"files": ["dist", "skills"]`, so it reaches every consumer with no install step and no telemetry. Three classes of defect, all in the same root cause. ### 1. A compile error, five times over `useAgent({ agentId, threadId })` appeared five times in `agent-access.md`, including as the **first example in the file**. `use-agent.tsx:126-137` states there are exactly two valid shapes and that this is not one of them; three runtime guards at `:160-198` reject it for callers who bypass the types. `runtimeAgentId` — the key that makes thread scoping work — did not appear once in the file. Now documented as the two shapes the hook admits, plus the rule that each surface needs its own local `agentId` because a duplicate throws `already registered` (`agent-registry.ts:486-491`). ### 2. A caching mechanism that does not exist Two files claimed per-thread clones are cached "in a module-level WeakMap keyed by `(registryAgent, threadId)`". There is no such WeakMap. What actually happens: - `useAgent` either binds the shared registry instance, or registers a private `ProxiedCopilotRuntimeAgent` under the local `agentId` and unregisters it on unmount (`use-agent.tsx:239-254`). - `CopilotChat` binds the **shared** agent with `useAgent({ agentId, throttleMs })` (`CopilotChat.tsx:138-141`) and then writes `agent.threadId = resolvedThreadId` (`:395`). That is why two chats naming one `(agentId, threadId)` both submit — one instance, one thread, no clone. ### 3. A CRITICAL gotcha describing a guard that was removed `agent-access.md` claimed `useAgent` calls `source.clone()` and throws `clone() must return a new, independent object`. `useAgent` never clones, and that message exists nowhere in the repo — I searched `packages/**` source for it. Returning `this` is still wrong, and worse than the doc said: nothing validates the result, so it fails silently. `suggestion-engine.ts:244-249` clones the provider agent and then writes a suggestion thread id, seeded messages, and seeded state onto the copy — given `this`, it writes all three onto the live agent the user is talking to. Its "Correct" example also constructed from `this.config`. `AbstractAgent` takes `config` as a constructor parameter and does not retain it (`agent.ts:136` spreads it), so that property does not exist. The example now says to keep what the subclass needs on a field of its own, and points at `ProxiedCopilotRuntimeAgent.clone` (`agent.ts:448-472`) as the reference implementation. ## On the issue #6125 asked for two things. The second, "fix `agent-access.md` to match the shipped API", is this PR. The first was a public headless thread-switching API, because the reporter was relying on an unblessed `agent.threadId = crypto.randomUUID()` mutation. That now exists in supported form as `useAgent({ agentId, runtimeAgentId, threadId })`, which is what the corrected doc describes. I have used `Refs` rather than `Closes` so a maintainer can confirm that satisfies them before closing. ## Testing Docs-only, so the evidence is the gates plus the verification method. Gates: ``` $ tsx scripts/sync-plugin-skills.ts synced 3 package skill(s) $ tsx scripts/sync-plugin-skills.ts --check plugin skill mirror in sync $ oxfmt --check skills/react-core/references/*.md All matched files use the correct format. ``` Edits were made in `packages/react-core/skills/` (the source of truth) and mirrored to `skills/` by the sync script, not hand-edited in both. Verification: every claim and every `Source:` citation in both touched files was checked against the tree at this commit. | Citation | Verdict | | --- | --- | | `use-agent.tsx:78-119` (WeakMap, ×2 files) | stale — now prop docs. Replaced. | | `use-agent.tsx:58-69` (clone guard) | stale — now `UseAgentThreadScopedProps.agentId`. Replaced. | | `use-agent.tsx:36-48` (throttling) | correct, unchanged | | `use-agent.tsx:226-290,465-481` (provisional agent, `isReady`) | correct, unchanged | I also swept the whole skill set for the same defect elsewhere: `useAgent({ ... threadId ... })` without `runtimeAgentId` appears in no other reference, and the WeakMap claim had exactly the one other home, in `chat-components.md`, fixed here. Not done: I did not re-audit citations in the other 13 `react-core` references. Given four of six were stale in these two files, that sweep is probably worth its own pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Clarified supported `useAgent` configurations for shared agents and private agents scoped to specific threads. - Updated cloning guidance, including field-by-field construction and validation behavior. - Documented compile-time and runtime safeguards for invalid agent/thread combinations. - Explained how multiple chat components sharing an agent and thread can interfere through duplicate connections, shared abort handling, and competing message updates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7265e2af12 |
fix(skills): correct two mechanism claims I did not verify
Self-review of the previous commit. Both were claims I restated or softened instead of checking. The duplicate-chat gotcha said two instances "both submit". The original said "submit duplicate messages". Neither is established. What actually happens on one shared instance: each mounts its own connect effect so the thread is connected twice, each assigns `agent.abortController` so the later mount can have its request aborted by the other's unmount, and each calls `setMessages` so the last connect to resolve wins. The bookkeeping that would prevent it, `lastConnectedThreadId` and `activeConnectCountRef`, is per component. The clone gotcha said the suggestion engine clones the provider agent, without qualification. It clones only on the stateful path; with `suggestions: true` on a multi-route runtime it builds a fresh `HttpAgent` and never clones, so the fault is configuration dependent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a44fad1d37 |
fix(skills): correct react-core agent access against the shipped API (#6125)
The bundled `react-core` skill documented a pre-`registerProxiedAgent`
architecture. It ships inside the `@copilotkit/react-core` tarball, so it
reaches every consumer with no install step.
`useAgent({ agentId, threadId })` appeared five times, including as the very
first example in the file. That shape is a compile error, and three runtime
guards reject it for callers who bypass the types. `runtimeAgentId` -- the key
that makes thread scoping work -- did not appear once. The two shapes the hook
admits are now documented, with the local-agentId uniqueness rule.
The per-thread WeakMap the file described in two places does not exist.
`useAgent` either binds the shared registry instance or registers a private
`ProxiedCopilotRuntimeAgent`; `CopilotChat` binds the shared agent and writes
`agent.threadId` onto it, which is why two chats on one (agentId, threadId)
both submit.
The CRITICAL clone gotcha claimed `useAgent` calls `clone()` and throws
`clone() must return a new, independent object`. `useAgent` never clones and
that message exists nowhere in the repo. Returning `this` is still wrong, and
worse than documented: nothing validates the result, so the suggestion engine
seeds a thread id, messages, and state onto the live agent instead of a copy.
Its example also constructed from `this.config`, which `AbstractAgent` takes as
a parameter and does not retain.
Every `Source:` citation in both files re-verified against the tree; four were
stale and are corrected, and the two that were right are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
edee011099 | Merge branch 'main' into refactor/rn-render-tool-hooks | ||
|
|
bba4113b8e | fix(react-core): improve Inspector message shortcuts | ||
|
|
b2b6bfbc3e |
refactor(react-native)!: converge render-tool hooks onto react-core
React Native's `useRenderTool` was not react-core's `useRenderTool`. Its entire
body forwarded to a different hook, `useFrontendTool`, while wearing the other
one's name. core has two: `useFrontendTool` registers a tool AND its renderer
via `addTool`; `useRenderTool` registers a renderer ONLY via
`addHookRenderToolCall`, and special-cases `"*"` into a schema-less fallback.
Because RN's alias took the `addTool` path, `name: "*"` registered a frontend
tool literally named `*` and advertised it to the model. Eight further symptoms
share that single cause: a render-only registration was advertised and shadowed
a same-named server tool; `addTool` evicted an existing same-named
`useFrontendTool` handler with only a `console.warn`; `description` and
`parameters` were required though both are tool fields; `followUp` and
`available` were accepted upstream but not forwarded; `handler` dropped core's
second (context) argument, leaving `stopAgent()`'s abort signal unreachable;
`agentId` changes never re-registered; two structurally incompatible
`RenderTool*Props` families shipped side by side; and `hooks/index.ts` was a
dead barrel with no build entry, no exports mapping and no importer.
Deletes RN's hook and re-exports core's two instead, so each capability has
exactly one implementation and RN carries no render-tool API of its own.
Deleting rather than re-pointing the name is deliberate. Re-pointing is the
dangerous shape: a `{ name, parameters, render }` call with no handler would
keep compiling and silently stop registering the tool.
Two of the nine are documented rather than fixed, so this is not a clean sweep:
`agentId` is still absent from both core hooks' re-registration check, so the
`deps` workaround stands; and `useDefaultRenderTool` keeps a narrower render
return than the hooks converged here.
Adds the guard the entry-surface suite was missing. It asserted only that
`useRenderTool` was *present* on the headless entry, never which hook it was, so
it would have stayed green through this entire change while verifying nothing
about it — proven by mutation: an RN-local hook re-grown under the name fails the
new identity assertion while 18 other guards in that file stay green.
Also rewrites the RN render-tool suite around what RN still owns. The previous
file tested forwarding into `useFrontendTool` through a double that modelled so
little its own header comment recorded that deleting the `deps`, `handler` and
`agentId` forwarding left it fully green. Assertions now read core's observable
state: `getTool`, `core.tools`, a real `runTool` rejection, painted DOM through
the real `useRenderToolCall`, and the tool list core hands an agent on a real
run.
BREAKING CHANGE: removes `useRenderTool`, `RenderToolProps`,
`RenderToolFunction` and `UseRenderToolOptions` from @copilotkit/react-native.
A tool-plus-renderer registration becomes `useFrontendTool` with an otherwise
identical object; renderer-only registration and the `"*"` wildcard become
core's `useRenderTool`; render props rename `args` to `parameters`. The
migration table, and the analysis of which call shapes fail loudly versus
silently, live in
showcase/shell-docs/src/content/reference/react-native/hooks/useRenderTool.mdx —
this repo's release-note collector reads only commit subjects, so a footer is
not a consumer-facing channel (see #6479) and the docs page is.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
dce8ee7578 |
refactor(react-core): let a tool renderer return null
`ReactToolCallRenderer.render` is a `React.ComponentType`, and a function component returns `ReactNode` — of which `null` is a member. So `null` already worked at the point a renderer is actually invoked; only the two build-side helpers forbade it, leaving core stricter on the way in than on the way out. Widens the render return to `React.ReactElement | null` at six annotations: `use-render-tool.tsx` (both overloads plus the implementation config) and `types/defineToolCallRenderer.ts` (both overloads plus the implementation signature). Both files are required — widening only the hook yields TS2322, because it passes `config.render`'s result straight into the helper. Non-breaking. Widening what a caller-supplied callback may return can only accept more code than before, so every renderer returning an element still compiles. Tested in both directions: a renderer returning `null` registers and paints nothing, and one returning an element behaves as before. Note that vitest transpiles without type-checking, so `tsc --noEmit` (which CI runs via static_quality.yml's check-types job) is the only guard against a re-narrowing. Scope: this widens `useRenderTool` and `defineToolCallRenderer` only. It does NOT make core uniformly permissive — `useDefaultRenderTool` still requires `ReactElement`, so core's two wildcard entry points now differ. That divergence fails loudly (TS2322) rather than silently, and is left to its own change since that hook carries four narrow annotations plus an existing cast. Note `defineToolCallRenderer` is re-exported from @copilotkit/react-native, so this change reaches React Native consumers too — it is not web-internal. Not purely type-level: oxlint's consistent-type-imports rule converted this file's `ToolCallStatus` import to `import type`, which removes a runtime `@copilotkit/core` import from the emitted JS. Harmless, since core is imported from many other react-core modules and no initialisation order changes — but worth stating rather than filing under "types". Also re-homes a coverage pin that @copilotkit/react-native's suite carried: despite living in RN it tested core's `JSON.stringify(extraDeps)` comparator, and no react-core test covered the case, so removing RN's copy would have left it unpinned repo-wide. It records a documented sharp edge, not behaviour worth preserving. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
dedfa6ea37 | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
7972bfadfd |
fix(mcp-apps-renderer): make the UMD and CJS entry points valid (Codex review)
Address two build-config findings from the Codex review of #6884. UMD (P2-1): react-core's UMD build externalized @copilotkit/mcp-apps-renderer/activity with no matching global, so it referenced an undefined global (_copilotkit_mcp_apps_renderer_activity) and every script-tag consumer broke at provider init - even without rendering an MCP app. Ship a UMD build of the bridge-free /activity entry (dist/activity.umd.js, es2018, global CopilotKitMcpAppsRendererActivity) and map it in react-core's two UMD builds. Script-tag consumers load activity.umd.js before react-core's UMD; documented in the package README and the react-core tsdown config. CJS (P2-2): the package advertised a CommonJS root whose dist/session.cjs emitted a synchronous require() of @modelcontextprotocol/ext-apps/app-bridge, which is ESM-only, failing with ERR_REQUIRE_ESM. Make the root ESM-only (bindMcpApp is loaded via dynamic import(), which resolves ESM from any context) and keep the bridge-free ./activity entry dual ESM+CJS (what react-core's CJS build requires). attw ignores no-resolution for the intentional ESM-only root; publint clean. Also: es2018 UMD covered by compat-check; public API manifest regenerated. |
||
|
|
d7846ba6ca |
fix(release): make GitHub Release notes actually ship (#6830)
## What's broken
Every GitHub Release this repo has ever cut has a body of `Release
<tag>` and nothing else — `v1.70.0`, `v1.69.3`, `channels/v0.6.0`,
`angular/v0.4.0`, all of them. The `#engr` Slack announcement links
"Release notes" at that page, so that link has always pointed at a blank
release.
The notes *were* being generated. The `angular/v0.5.0` create-pr run
logged:
```
Raw release notes written to release-notes.md
Generating AI-enhanced release notes...
AI-enhanced release notes written to release-notes.md
```
They just never left the runner. `release-notes.md` was gitignored
(`.gitignore:72-73`), so `peter-evans/create-pull-request` skipped it,
the file never reached the release branch, and `publish-release.yml`'s
`readFileSync("./release-notes.md")` missed and fell through to `body =
\`Release ${name}\``.
The same ignore rule severed the Notion round-trip:
`release-notes-notion.json` was ignored too, so the publish job could
never read an edited draft back. That path had never run either.
Meanwhile the repo carried **29 changelog files that no tooling had
written since April**. They are changesets-era leftovers, and nothing in
`scripts/` or `.github/` reads or writes them:
```
$ git grep -n "CHANGELOG" -- 'scripts/**' '.github/**' '*.json' ':!*/CHANGELOG.md'
(no matches)
```
They stopped at `1.55.2` while the monorepo lane shipped `1.69.3`, and
`packages/angular/CHANGELOG.md` still claimed `1.54.3` — a version from
before angular split onto its own `0.x` line. So the only changelog a
reader could find in the tree named the wrong version for the wrong
lane.
## What this changes
**1. The notes become a source-controlled changelog, one file per
release lane.**
| lane | file |
|---|---|
| `monorepo` | `CHANGELOG.md` |
| `angular` | `packages/angular/CHANGELOG.md` |
| `channels` | `packages/channels/CHANGELOG.md` |
Per lane rather than one root file because the lanes version
independently: a shared file would interleave `1.70.0`, `angular/0.5.0`
and `channels/0.9.0` into one sequence where no reader can follow any
single line. (Concurrent writes are *not* the reason —
`stable-release.yml` already fails if any release PR is open.)
The flow: `prepare-release.ts` writes the raw notes,
`generate-ai-release-notes.ts` polishes them, **`write-changelog.ts`**
prepends them as this version's section, `create-pull-request` commits
the changelog (a tracked file, so `git add -A` always stages it), and
**`extract-release-notes.ts`** reads that section back in the publish
job as the GitHub Release body.
The changelog is therefore both the durable record and the review
surface: edit the top section on the release PR to change what ships.
`release-notes.md` goes back to being gitignored scratch, so the same
notes never exist as two editable copies with no rule about which one
wins.
**2. The 29 stale changelogs are deleted**, and a test pins the tracked
changelog set to exactly the three lane files, so they cannot creep back
and contradict the real versions again. Their content stays recoverable
from git history (`git show v1.69.3:packages/core/CHANGELOG.md`).
**3. Notes are selected per PR, scoped to the lane.** Selection was
`--no-merges` over every commit since the scope's tag. Two bugs:
- *No path filter* — a scope inherited every other lane's work.
- *`--no-merges` is backwards here* — this repo merges PRs as merge
commits, so the merge **is** the unit of change and the only commit
carrying `(#1234)`. `--no-merges` dropped every PR boundary and kept the
intermediate branch commits.
Now: `--first-parent` over the scope's package directories, minus
commits no consumer would read about (`test`/`ci`/`style`, `chore`
except `chore(deps)`, and the release commit itself).
| scope | before | after |
|---|---|---|
| `angular` v0.5.0 | 159 entries | **4** |
| `channels` (unreleased) | 600+ entries | **9** |
**4. Breaking-change footers still survive.** `--first-parent` alone
silently dropped `BREAKING CHANGE:` footers written on branch commits
rather than in the PR description — measured at **2 of 2 lost** across
`v1.60.0..HEAD`. Each merge's branch messages are now folded into its
body before extraction, so the entry list stays one-per-PR while the
footer scan sees the whole PR. Re-measured: **0 lost**.
**5. The AI prompt is scoped and the API call is correct.** It was
passing a repo-wide `git log -50` as "context" and asserting the release
was "CopilotKit vX.Y.Z, an open-source AI agent framework for React
applications" — wrong commits, wrong framing, and wrong release title
for any non-monorepo lane. Now it gets the lane's own commits, the names
of the packages actually being published, and an instruction to write
about nothing else. Also fixed in the same call: `max_tokens: 2048`
(truncates a large release mid-section, and the truncated text is what
ships as the body), and a response reader that took `content[0].text`
rather than selecting the text block by type. The model pin is left
alone — `main` already carries a current, undated id.
**6. Notion is removed**, not repaired — the release PR is already the
review surface.
### Failure behavior on the publish side
`extract-release-notes.ts` runs **after** `npm publish`, so it never
exits non-zero: failing there would leave the packages published and the
tag unpushed. A missing section prints a `::error::` annotation and
falls through to the workflow's existing `Release <tag>` fallback. Worst
case is the blank body we have today, never a half-finished release.
## Testing
Baseline on `main`: `15 files / 162 tests`. On this branch: **`16 files
/ 197 tests`**.
```
$ npx vitest run --config scripts/release/vitest.config.mts
Test Files 16 passed (16)
Tests 197 passed (197)
```
**The whole lane round-trips end to end.** A real `prepare-release.ts
--scope channels --bump minor` run (versions reverted afterward), then
the two new halves:
```
$ pnpm tsx scripts/release/write-changelog.ts 0.10.0 channels
Recorded 0.10.0 in packages/channels/CHANGELOG.md
$ rm release-notes.md
$ pnpm tsx scripts/release/extract-release-notes.ts 0.10.0 channels
Release body written to release-notes.md from packages/channels/CHANGELOG.md (861 chars)
```
The extracted body is the 9 PR-numbered entries under Features / Fixes /
Other, with **no duplicated version heading** (`grep -c '^## '
release-notes.md` → `0`) — the raw generator's own `## v0.10.0
(channels)` line is stripped when the section heading is written. The
miss path was exercised too:
```
$ pnpm tsx scripts/release/extract-release-notes.ts 9.9.9 channels
::error title=Release notes::No section for 9.9.9 in packages/channels/CHANGELOG.md. ...
exit: 0
```
**The staging behavior is verified against the pinned action, not
assumed.** `peter-evans/create-pull-request@5f6978f` stages with `git
add -A` when `add-paths` is unset. In-repo, after a real notes run:
```
$ git add -A --dry-run | grep -iE "changelog|release-notes"
add 'packages/channels/CHANGELOG.md'
$ git check-ignore -v release-notes.md
.gitignore:77:release-notes.md release-notes.md
```
The changelog is staged; the scratch file is invisible to the commit.
The publish job checks out `ref: main` at `fetch-depth: 0`, and the
release PR merges the changelog into main, so the section is present
when the extractor runs.
**The selection reproduces a hand-curated list exactly.**
`angular/v0.5.0`'s release body was written by hand from its four real
PRs. Running the new selection over that same range returns exactly
those four, release commit correctly dropped:
```
#6098 feat(runtime): use managed Intelligence authority (#6098)
#6756 chore(deps): bump @ag-ui/* to 0.0.59 (#6756)
#6773 feat(angular): add registerComponent ... (refs OSS-1034) (#6773)
#6586 fix(angular): resolve human-in-the-loop results without the bus envelope (#6586)
```
**Breaking-change regression measured, not assumed** — differential
comparison of extracted notes, old selection vs new, over two ranges:
```
range v1.60.0..HEAD old: 2 new: 2 LOST: 0
range v1.50.0..HEAD old: 2 new: 2 LOST: 0
```
(Before the fold was added, the same probe reported `LOST: 2` — that is
how the bug was caught.)
**Every new test was mutation-checked** — the mechanism was broken and
the test confirmed failing:
| mutation | result |
|---|---|
| `--first-parent` → `--no-merges` | 2 failed |
| drop the pathspec filter | 2 failed |
| `isNoiseCommit` always false | 2 failed |
| `parsePrNumber` always null | 2 failed |
| `withBranchMessages` → no-op | 1 failed |
| code-fence tracking disabled | 1 failed |
| `stripVersionHeading` → no-op | 3 failed |
| `prependSection` appends instead | 1 failed |
| `extractSection` keeps the heading | 5 failed |
| `upsertSection` stops replacing | 1 failed |
| re-ignore a lane changelog | 1 failed |
| re-ignore all `packages/*/CHANGELOG.md` | 2 failed |
| an orphan changelog creeps back | 1 failed |
| a lane changelog goes missing | 1 failed |
| *(restored)* | **all green** |
One of those mutations found a bug **in the test itself**: `git
check-ignore <path>` reports nothing for a path that is already tracked,
so the ignore assertion passed against a rule that would still strand
the next lane's file. It now runs `git check-ignore --no-index`, and the
mutation fails as it should. The flagless form is why the row above
exists at all.
Also run: `verify-release-scope-dropdowns.sh` (all OK), YAML parse of
both edited workflows, `oxfmt` (no-op after formatting), `oxlint` (0
warnings, 44 files).
**Not verified:** the live Claude API call. No `ANTHROPIC_API_KEY` was
available locally, so only the no-key fallback path (raw changelog) and
the CLI arg validation were exercised. A generation failure is already
caught and falls back to the raw notes, so the worst case is un-polished
notes rather than a blank body.
The commit is `--no-verify`: the pre-commit nx lane cannot run in this
worktree (`packages/core` and `packages/channels-ui` have no
`node_modules`, and `nx run @copilotkit/core:build` fails identically
with the tree clean). The only change under `packages/**` is deleting
orphan markdown that no build or test reads. CI on this PR runs the real
lane.
## Not in this PR
Slack-side drafting/massaging in a dedicated channel, with write-back to
the release body. Deliberately separate — that lane needs its own
channel and webhook, and must not run through `#engr`. The `notify` job
and the `#engr` announcement are untouched here.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Release notes are now organized by release lane and recorded in
dedicated changelogs.
* GitHub Releases can automatically use the matching lane changelog
section.
* Release notes are scoped to packages included in each release lane.
* **Documentation**
* Added guidance for supported release lanes and changelog workflows.
* **Changes**
* Historical package and example changelog entries were removed or
replaced with the lane-based format.
* Notion-based release-note drafting and PR links are no longer used.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
1422019862 |
test(react-core): import the A2UI renderer once, not inside every test
The first test in A2UIMessageRenderer.test.tsx timed out on the
Node 24 / React 19 unit shard while the same commit passed on every other
shard. The test body is about ten milliseconds of work.
The cost was the `await import("../a2ui/A2UIMessageRenderer.js")` inside
the test. That import pulls in the whole @copilotkit/a2ui-renderer graph,
which vitest.config.mjs inlines, and vitest charges the one-time transform
to whichever test runs first. Measured locally: the first test took 502ms
of the 5000ms default timeout, and the other seventeen took 0 to 15ms
each. Under CI load the same cost reached 4798ms on a passing shard.
All eleven dynamic imports named the same module, and the file calls no
vi.resetModules(), so every one already resolved to a single cached
instance. The laziness bought nothing and cost the first test its budget.
One static import moves the work to collection, which no test timeout
bounds. Measured after the change: the first test takes 53ms, and the
import phase grows from 27ms to 468ms, which is where that work belongs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
2c05ed6885 |
chore(release): keep release notes in one CHANGELOG.md per release lane
The notes now land in a source-controlled changelog instead of a scratch file that rides the release branch. One file per lane, because the lanes version independently: a shared file would interleave `1.70.0`, `angular/0.5.0` and `channels/0.9.0` into one unreadable sequence. monorepo -> CHANGELOG.md angular -> packages/angular/CHANGELOG.md channels -> packages/channels/CHANGELOG.md `write-changelog.ts` prepends this release's section on the release branch, create-pull-request commits it (a tracked file, always staged), and `extract-release-notes.ts` reads the section back in the publish job as the GitHub Release body. The changelog is therefore both the durable record and the review surface: editing a section on the release PR changes what ships. release-notes.md goes back to being ignored, so the same notes never exist as two editable copies. Also deletes 29 changesets-era changelogs that no tooling had written since April. They stopped at 1.55.2 while the lane shipped 1.69.3, and packages/angular/CHANGELOG.md still claimed 1.54.3 from before that lane split onto its own 0.x line. Their content stays recoverable from git history. A test pins the tracked changelog set to the lanes so they cannot creep back and contradict the real versions. Extraction never fails the publish job: it runs after npm publish, so a miss annotates loudly and falls through to the existing bodyless-release fallback rather than stranding the tag. Committed with --no-verify: the pre-commit nx lane cannot run in this worktree (packages/core and packages/channels-ui have no node_modules, and `nx run @copilotkit/core:build` fails identically with the tree clean). The only change under packages/** is deleting orphan markdown that no build or test reads. |
||
|
|
5af412b5b9 | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
e2887b4528 |
fix #6934: stop useInterrupt re-renders on agent updates
useInterrupt only needs the agent handle - interrupt events arrive via its own direct agent.subscribe(). But useAgent() with no update filter force-updates the consumer on every message/state/run-status change. Pass updates: [] so the re-render subscription is skipped entirely (use-agent early-returns when the list is empty) while the agent handle still resolves. |
||
|
|
a38a3a7e92 |
fix(react-core): register v1 readables before sibling effects run
useCopilotReadable published its context in a useEffect. React flushes passive effects child-first in tree order, so a consumer mounted before the readable runs its own useEffect against an empty context store. That is the cross-page-navigation failure: a page mounts the chat and its readable-publishing components in one commit, the chat's connect effect fires first, and the connect request carries no context. Register in useLayoutEffect instead. Layout effects run during commit, ahead of every passive effect regardless of tree order, which closes the window. Register and cleanup stay in the one effect, so both sides remain in the same phase. This matches the v2 siblings useAgentContext and useFrontendTool, the latter fixed the same way in |
||
|
|
274983d4f8 |
fix(docs): make the redirect suffix-aware and repair dead links the regeneration surfaced
Self-review follow-ups on the reference-docs regeneration.
The LangGraphAgent redirect only covered the bare path. A raw Markdown request
reaches redirects before the .md/.mdx rewrite, so a request for
/reference/v1/sdk/python/LangGraphAgent.md would have 404'd for the LLM routes.
Use permanentRedirectsWithSuffixes, which is what the rest of the redirect
table does.
Refreshing the pages also republishes their JSDoc links, and three of those
pointed at pages that do not exist. They were invisible while the pages were
frozen; regenerating makes them live 404s, so fix them at the source:
- use-coagent-state-render.ts linked to /coagents/videos/perplexity-clone, a
legacy URL with no content, no redirect and no rewrite. Point at
/generative-ui/state-rendering, the canonical guide the published page
already named.
- copilotkit-props.tsx linked to
/coagents/shared/guides/langgraph-platform-authentication, which likewise
does not exist. Point at /auth, which is how the rest of the docs link to
that guide.
- use-copilot-chat.ts was flipped to
/reference/v2/hooks/useCopilotChatHeadless_c by the URL canonicalization in
|
||
|
|
862ff3c180 |
fix(react-core): name the agent on CopilotKitProvider, warn when threads meet single-route (#6892)
Fixes OSS-1133
Rebased onto `main`. The branch was 205 commits behind, and two of its
three changes did not survive contact with current `main`. Both are
corrected here, so this description replaces the original one rather
than adding to it.
## What changed on `main` under this branch
**The single-route warning is gone.** The branch added a `useThreads`
development warning on the premise that "the thread routes live outside
the single-route envelope, so the list stays empty". That premise is no
longer true. `main` now carries thread, memory, and annotation
operations through the single-route endpoint with a `resource/request`
envelope (`fetch-handler.ts:405`), advertises it as
`singleRoute.resourceOperations` (`get-runtime-info.ts:177`), and the
client reads the thread endpoints from there (`agent-registry.ts:1367`).
A current single-route runtime serves threads, so the warning fired on a
working configuration.
Narrowing it does not rescue it either: when the transport is `single`
and the endpoints are still unavailable, the cause is a missing
Intelligence or thread backend, not the transport — the client cannot
tell those apart. The hook already surfaces the knowable fact through
`threadEndpointsError`. The warning, its three tests, and the
`useThreads.mdx` callout are dropped; `use-threads.tsx` and its test
file are now byte-identical to `main`.
**The docs' `useSingleEndpoint` claim is stale.** Both pages said
released versions of `<CopilotKit>` pin the flag to `true`. `dc73af1dc4`
removed that pin and is an ancestor of the `v1.70.2` release, which is
what `npm` serves today. Corrected on both pages.
## What this PR does
### `agentId` on `CopilotKitProvider`
```tsx
<CopilotKitProvider runtimeUrl="/api/copilotkit" agentId="my_agent">
```
`CopilotKitProvider` carries no agent prop at all, so the only way to
name an agent at the provider level is the v1 compatibility component.
The reporter had to read the installed type definitions to find that
`agentId` lives on `<CopilotChat>` instead.
The prop publishes a bare string context (`CopilotKitAgentIdContext` in
`src/v2/context.ts`) that is the **last** fallback before
`DEFAULT_AGENT_ID`. Five resolution sites consult it:
`CopilotChatConfigurationProvider` (which covers everything nested
inside a chat), `CopilotChat`, `CopilotThreadsDrawer`, `useAgent`, and
`useSuggestions`. An explicit `agentId` still wins at every one of them.
### Why not a root `CopilotChatConfigurationProvider`
The original branch published the default by rendering a
`CopilotChatConfigurationProvider` at the root. That provider also owns
a thread: it resolves a `threadId` (minting a UUID when none is given),
and the top-most one owns the imperative active-thread override.
Wrapping the application in one hands every descendant chat the same
inherited `threadId`, so two sibling chats share a transcript.
Measured on the original branch with `randomUUID` mocked to increment:
| | sibling chat 1 | sibling chat 2 |
| -- | -- | -- |
| `<CopilotKitProvider>` | `uuid-1` | `uuid-2` |
| `<CopilotKitProvider agentId="my_agent">` | `uuid-1` | `uuid-1` |
A bare string context carries the agent default and nothing else, so the
second row now matches the first.
### Docs
- `reference/components/CopilotKit.mdx`: the callout now says it is the
v1 provider and points at `CopilotKitProvider`, followed by the
agent-prop table. The `useSingleEndpoint` row is replaced by a sentence
saying both providers negotiate the transport, with the pre-1.70.2
behavior named as history.
- `docs/backend/runtime-endpoints.mdx`: the prop rename (`agent` →
`agentId`), and the transport table and its surrounding prose corrected
for the removed pin.
- `reference/hooks/useThreads.mdx`: back to `main` (see above).
I did not rewrite the integration quickstarts that show `<CopilotKit
agent=...>`. They already carry a "Which provider goes with which
handler?" callout and pass `useSingleEndpoint={false}` explicitly, so
they are correct as written; swapping the provider in all of them is a
docs sweep of its own.
## Testing
This worktree has its own full `pnpm install` and a rebuilt workspace
`dist`, so these numbers come from a clean environment on the rebased
tree.
### Whole-package suite
Both rows are real runs in this worktree on the same rebase base, taken
by checking `main`'s `packages/react-core/src` in and out around the
run:
| | Test files | Tests | Failed |
| -- | -- | -- | -- |
| `origin/main` (
|
||
|
|
e823409f96 |
fix(react-core): name the agent on CopilotKitProvider
`@copilotkit/react-core/v2` re-exports the v1 `<CopilotKit>` provider, and that was the only provider carrying an agent prop. So a v2 application that wanted to name its agent at the provider level had to reach for the v1 compatibility component, and the reporter had to read the installed type definitions to find that the v2 equivalent lives on `<CopilotChat agentId>` instead. Accept `agentId` on `CopilotKitProvider`. It publishes a bare string context that is the last fallback before `DEFAULT_AGENT_ID`, so `<CopilotChat agentId>`, `<CopilotChatConfigurationProvider agentId>`, and an explicit `agentId` argument to `useAgent`/`useSuggestions` all still win. The default deliberately does NOT arrive through a root `CopilotChatConfigurationProvider`. That provider also owns a thread: it resolves a threadId, minting a UUID when none is given, and the top-most one owns the imperative active-thread override. Wrapping the application in one hands every descendant chat the same inherited threadId, so two sibling chats share a transcript. A test renders two sibling chats under the provider and pins that they keep their own threads. Docs: say plainly on the `CopilotKit` reference page that it is the v1 provider, and note the prop rename on the provider-and-handler-pairs page. Both pages claimed that released versions of `<CopilotKit>` pin `useSingleEndpoint` to `true`; that pin was removed in 1.70.2, so both providers now negotiate the transport when the prop is omitted. Fixes OSS-1133 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fb4f352032 |
chore: release monorepo v1.70.3 (#6960)
## Release monorepo v1.70.3 **Scope:** `monorepo` | **Bump:** `patch` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `monorepo` packages to `1.70.3` and generated AI-enhanced release notes. 2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build) must pass before merging. This is the review gate. 3. **Review the release notes** in `release-notes.md` in this PR. If a Notion draft was created, you can edit the release notes there before merging. 4. **When this PR is merged**, the `release / publish` workflow automatically: - Builds all packages - Publishes the `monorepo` packages to npm at version `1.70.3` - Creates git tag `monorepo/v1.70.3` - Creates a GitHub Release with the final release notes ### Before merging - [ ] CI is green (tests, lint, types, build) - [ ] Version bumps look correct - [ ] Release notes are accurate (edit in Notion if a draft was created) --- > **Do not merge until CI is fully green.** The full test suite runs automatically on this PR. |
||
|
|
69a940c70e | chore: release monorepo v1.70.3 | ||
|
|
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> |
||
|
|
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 (
|
||
|
|
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> |
||
|
|
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 --> |
||
|
|
16514e9424 | chore: release monorepo v1.70.2 | ||
|
|
9d63fb00bc | Merge branch 'main' into feat/mcp-apps-renderer | ||
|
|
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> |
||
|
|
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 --> |
||
|
|
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 | ||
|
|
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 --> |