Commit Graph

16234 Commits

Author SHA1 Message Date
Maximiliano Korp 1819cc953c fix(runtime): use first user message when thread naming fails 2026-09-09 16:54:40 -07:00
Ben Taylor 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 -->
2026-09-09 16:26:53 -05:00
Benjamin Taylor 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>
2026-09-09 15:57:44 -05:00
Benjamin Taylor 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>
2026-09-09 15:45:12 -05:00
Benjamin Taylor 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>
2026-09-09 15:27:24 -05:00
Ben Taylor cf191b5525 fix(channels-telegram): emit language- form for tagged code fences (#6622)
## Summary
- Tagged fenced code blocks (```js ... ```) now emit Telegram's
`<pre><code class="language-js">` form instead of leaking the language
token into the code body
- Untagged fences stay plain `<pre>`; single-line ``` ```code``` ```
fences become inline `<code>`
- Fixes #6602

## Test plan
- New vitest cases in `telegram-html.test.ts` (tagged/untagged/inline
fences)
- Verified standalone via `node --experimental-strip-types`
2026-09-09 15:25:22 -05:00
Tyler Slaton b46749300e chore: release monorepo v1.71.0 (#6996)
## Release monorepo v1.71.0

**Scope:** `monorepo` | **Bump:** `minor`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `monorepo` packages to `1.71.0`
   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 `CHANGELOG.md`
   in this PR. Edit the top section on this branch to change what ships:
   the publish job reads that section back as the GitHub Release body.

4. **When this PR is merged**, the `release / publish` workflow
automatically:
   - Builds all packages
   - Publishes the `monorepo` packages to npm at version `1.71.0`
   - Creates git tag `monorepo/v1.71.0`
   - 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 `CHANGELOG.md` on this branch)

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
v1.71.0
2026-09-09 22:24:22 +02:00
github-actions[bot] 461259e18f style: auto-fix formatting 2026-09-09 22:24:05 +02:00
tylerslaton cac7cde862 chore: release monorepo v1.71.0 2026-09-09 22:24:05 +02:00
Benjamin Taylor 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>
2026-09-09 15:17:10 -05:00
Ben Taylor 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 -->
2026-09-09 15:13:59 -05:00
Benjamin Taylor e5730de1c2 fix(channels-telegram): only a language token becomes a class name
The info string was passed through to `class="language-..."` unfiltered, so a
fence tagged `<script>` emitted `class="language-&lt;script&gt;"`. It is escaped,
so nothing is injectable, but Telegram has no use for it and it is not a
language. Gate the token on /^[\w+#.-]+$/ and fall back to plain <pre> when it
does not match, which keeps c++, objective-c and asp.net working.

Also refresh the module docstring, which still described the single fence form
after this PR split it into three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 15:10:14 -05:00
Tyler Slaton c6bd253dcf feat(web-inspector): add Learning setup escape actions (#6995)
## Summary

- Add a Go back action from Learning setup to the original Learning
overview and video.
- Let users copy the setup prompt again without leaving the setup flow.
- Show a successful copy confirmation and reset the action after two
seconds.

## Why

Copying the onboarding prompt moves users into setup, where they
previously could not return to the overview or recover if the clipboard
contents were replaced. This keeps the Learning onboarding flow
reversible and makes repeated copying clear.

## How

- Clear the Learning setup marker when Go back is selected.
- Track initial-copy and re-copy feedback independently, including
clipboard failure feedback.
- Cancel stale clipboard work and confirmation timers during navigation
and teardown.
- Add component and Inspector integration coverage.
- Verify the behavior in the standalone Inspector workbench.
- Verify all 691 web-inspector tests and typechecks pass.

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

## Summary by CodeRabbit

* **New Features**
* Added a “Copy prompt again” action after setup, with “Copied!”
confirmation and automatic reset.
* Added a “Go back” action during setup to return to the landing
preview.
  * Added feedback for copy failures.

* **Bug Fixes**
* Improved handling of repeated copy actions and navigation during setup
to prevent stale status updates.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 22:06:33 +02:00
Ben Taylor 2eadccf590 fix(showcase): port sales-analyst pills to 8 declarative-gen-ui demos (#6886)
Fixes #6791.
2026-09-09 15:05:02 -05:00
Benjamin Taylor 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>
2026-09-09 15:02:02 -05:00
Tyler Slaton 87e5dd031b feat(web-inspector): add Learning setup escape actions 2026-09-09 12:54:02 -07:00
Maxim 851081e093 refactor(react-native)!: converge render-tool hooks onto react-core (#6533)
Closes #6438's deferred review finding **5b**. #6438 shipped 5a —
retracting the false claim that React Native supported wildcard
renderers, and documenting the gap as a known limitation. This is the
actual fix.

## The defect: one cause, not nine

React Native's `useRenderTool` was **not** react-core's `useRenderTool`.
Its entire body forwarded to a *different* hook while wearing the other
one's name:

```ts
useFrontendTool<T>({ name, description, parameters, handler, agentId, render }, deps);
```

react-core has two hooks:

| hook | registers | wildcard `"*"` |
| --- | --- | --- |
| `useFrontendTool` | a tool **and** its renderer (`addTool`) | no
special case |
| `useRenderTool` | a renderer **only** (`addHookRenderToolCall`) |
special-cased into a schema-less fallback |

RN aliased the first under the second's name. Because that took the
`addTool` path, `name: "*"` registered a frontend tool **literally named
`*`**.

**Correction from review.** Earlier revisions of this description said
that tool was advertised to the model. It was not, and @BenTaylorDev
caught it: core filters the wildcard name out of the advertised list in
`run-handler.ts`, and that filter landed in `31aa1e2162` — before this
PR was opened, but after the base it was originally written against. The
1273-commit rebase moved the code forward and left the prose behind. All
seven code and doc sites have been corrected.

**What it actually did is worse.** A tool named `*` is core's catch-all
*handler*. When a tool call has no exact-name frontend tool and no
result yet, core reaches for the wildcard tool and runs
`executeWildcardTool` — and the tool-result splice sits *outside* the
`if (wildcardTool?.handler)` guard. So an RN app whose author wanted a
display-only fallback was also auto-answering every otherwise-unanswered
tool call with an empty tool result and requesting a follow-up turn.
Measured by driving a real turn through both paths: the old handler-less
`*` produced **2 turns and a spliced `{ toolCallId, content: "" }`**;
the shim's route produces 1 turn and no result.

Eight further reported symptoms share that single cause: a render-only
registration was advertised and shadowed a same-named server tool;
`addTool` evicted an existing same-named handler with only a
`console.warn`; `description` / `parameters` were required though both
are *tool* fields; `followUp` / `available` were not forwarded;
`handler` dropped core's 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.

## The fix

RN's own implementation is gone. `useRenderTool` and `useFrontendTool`
are now re-exported from react-core, so each capability has exactly one
implementation — **and `"*"` works as a real wildcard on React Native
for the first time.**

Deleting rather than re-pointing the name was deliberate: re-pointing
would let a `{ name, parameters, render }` call with no handler keep
compiling while silently ceasing to register the tool.

## The landing is soft, on purpose

Following @BenTaylorDev's review, `useRenderTool` **still exists on
React Native** as a deprecated shim for one minor. It routes by shape
and warns in development, once per tool name:

| call shape | routes to | why |
| --- | --- | --- |
| `name: "*"` | core's `useRenderTool` (renderer-only) | **unconditional
— wildcard always wins**, even with old tool fields present |
| carries `handler` or `description` | `useFrontendTool` | that is what
the old hook actually did |
| anything else | core's `useRenderTool` | renderer-only was the intent
|

The wildcard rule is deliberately not the obvious reading. The old hook
made `description` **required**, so anyone who ever attempted a wildcard
renderer necessarily wrote it *with* the old tool fields. Routing "has
old fields" to `useFrontendTool` would have recreated the original
`*`-named-tool bug for exactly the people who tried hardest to use the
wildcard.

**Why a shim rather than a clean break.** `@copilotkit/react-native`
sits in the `monorepo` release scope — 16 packages versioned in
lockstep, all currently `1.68.1`. A major would take every
`@copilotkit/*` package to `2.0.0`. So this ships in a minor and the `!`
marker buys a release-note line, nothing more. That leaves the compiler
as the only signal reaching consumers, and there are shapes it cannot
see: a hoisted config whose `render` ignores its props, old fields
arriving via a spread, and untyped or `@ts-nocheck` call sites. In all
three, core's hook silently ignores `description` and `handler`, and
because the bridge spreads `{ ...props, parameters: props.args }`, an
old `render: ({ args }) => …` keeps painting — the screen looks
unchanged while the tool stops being advertised and the handler never
runs. The shim's warning is the only mechanism that reaches that
population.

**What the shim does not restore:** the old *render prop* name. A
renderer reading `args` still fails `TS2339`. The registration keeps
working; the render body still needs the rename.

## ⚠️ BREAKING — needs product ratification

Removes `RenderToolProps`, `RenderToolFunction` and
`UseRenderToolOptions` from `@copilotkit/react-native`. `useRenderTool`
is deprecated, not removed, and is scheduled for removal next minor.

**@tylerslaton — this needs your explicit sign-off.** @BenTaylorDev has
clarified that his deferral on #6438 was not a precedent, and would
rather both removals be ratified together.

### Migration

| Before (RN) | After |
| --- | --- |
| `useRenderTool({ name, description, parameters, handler, render },
deps)` | `useFrontendTool({ …identical object }, deps)` — the shim keeps
this working with a warning this release |
| renderer-only registration: impossible | `useRenderTool({ name,
parameters, render, agentId? }, deps)` |
| wildcard: registers a tool named `*` | `useRenderTool({ name: "*",
render })` — works, same as web |
| render props `{ args, status, … }` | `{ parameters, status, … }` —
**not** covered by the shim |
| `RenderToolProps<T>` (args-shaped) | `RenderToolProps<S>`
(parameters-shaped, generic over schema) |
| bare `RenderToolProps` | now `TS2314` — core's has no default type
argument; supply the schema type |
| `UseRenderToolOptions<T>` | gone — write the config inline
(`RenderToolConfig` is core-internal) |
| `RenderToolFunction<T>` | gone — see `FrontendToolRenderFunction<T>`
below |

The table also lives on the docs page, deliberately: the release-note
collector reads `git log --format=%H %s` into a `{ hash, subject }`
type, so a `BREAKING CHANGE:` footer has nowhere to land (#6479).

## Restoring the React Native render-type safety

Ben caught a property the migration silently traded away. RN's deleted
`RenderToolFunction` was the only thing narrowing an RN render to
`React.ReactElement | null`. `useFrontendTool`'s `render` is a
`React.ComponentType`, so it returns `ReactNode` — meaning a bare string
now compiles and then throws *Text strings must be rendered within a
`<Text>` component* on device. Before the rename that call site was
rejected.

`FrontendToolRenderFunction<T>` is now exported from `/headless` for
that. It derives its props from core's canonical `ReactToolCallRenderer`
unchanged and narrows only the return type, so it cannot drift from the
contract. It is **opt-in** — annotate your renderer to get the
protection — and the hazard is now documented on `useFrontendTool.mdx`,
which previously had zero mentions of `ReactElement`, `ReactNode` or
`FlatList`.

## `status` changes vocabulary, and that is NOT a break

RN's `status` moves from the `ToolCallStatus` enum to core's
string-literal union. A string-enum member is assignable to its own
literal type, so `status === ToolCallStatus.Complete` compiles **and
narrows** against the union — verified in all four combinations. No
migration work follows.

This PR also deletes a false claim #6438 shipped: that comparing the
enum to a bare string literal does not typecheck. Ben raised exactly
this in #6438's review; it was corrected in the migration note and left
standing in the guide bullet.

## The react-core half (2 files, 6 lines)

`ReactToolCallRenderer.render` is a `React.ComponentType`, and a
function component returns `ReactNode` — of which `null` is a member. So
`null` already worked where a renderer is *invoked*; only the two
build-side helpers forbade it. Widened to `React.ReactElement | null` in
`use-render-tool.tsx` and `types/defineToolCallRenderer.ts`. Both are
required — widening only the hook yields `TS2322`.

Three things this does **not** claim:

- **Not type-only.** oxlint's `consistent-type-imports` converted
`defineToolCallRenderer.ts`'s `ToolCallStatus` import to `import type`,
removing a runtime `@copilotkit/core` import from the emitted JS.
Harmless, but not a types-only diff.
- **Not uniform.** `useDefaultRenderTool` still requires `ReactElement`,
so core's two wildcard entry points differ. That divergence fails loudly
(`TS2322`) and gets its own change.
- **Not web-only in reach.** `defineToolCallRenderer` is re-exported
from `@copilotkit/react-native`.

It lives in its own commit and lifts out cleanly if you would rather
this PR stay RN-only.

## Two symptoms are documented, not fixed

- **`agentId` re-registration** — both core hooks omit `agentId` from
the effect's dependency check, so the `deps` workaround stands.
- **`useDefaultRenderTool`'s narrower render return**, per above.

## Testing

- **Red-green on the wildcard.** Before the deletion, `getTool({
toolName: "*" })` returned a real tool object — `{ name: "*",
description: undefined, parameters: undefined }` — the defect verbatim.
Green after: the `"*"` renderer paints for an unmatched call **and** no
tool named `*` is registered. The shim has its own version of this test,
since its wildcard rule exists to prevent that exact regression. Note
per the correction above that the **registry** assertions are the
discriminating ones; the `advertised` assertions are kept as forward
guards against someone making `*` advertisable later, and their comments
now say so rather than claiming to be load-bearing.
- **All three shim routing rules**, asserted on core's observable state
— `getTool`, `core.tools`, and the advertised tool list core hands an
agent on a real run.
- **The identity guard was replaced, not weakened.**
`headless-entry-surface.test.ts` previously asserted only that
`useRenderTool` was *present*, never which hook it was, so it would have
stayed green through this entire change. The identity assertion closed
that; the shim made identity false, so it is now a delegation test plus
a graph-wide deny-list on `addTool` / `removeTool` /
`addHookRenderToolCall` / `renderToolCalls` / `createContext`. Five
mutations were run against it: re-growing a local registry fails both
new guards; an early-return shim that still *looks* delegating fails 14
behavioural tests; removing the wildcard rule reproduces the original
bug; removing the production gate and the warn-once dedup each fail
their own test.
- **A test whose name asserted the opposite of its behaviour** was
replaced — it claimed to prove agent scoping and never checked that a
scoped renderer does not paint under the default agent.
- The RN suite was rewritten around what RN owns. The old file tested
forwarding through a double so thin that its own header comment recorded
that deleting the `deps`/`handler`/`agentId` forwarding left it fully
green.
- **A coverage pin was re-homed, not dropped.** An RN test pinning
core's `JSON.stringify(extraDeps)` comparator was testing core, and no
react-core test covered the case.
- **`FrontendToolRenderFunction` is mutation-proved both ways** —
widening the return to `ReactNode` or loosening the props each break the
type test.
- `packages/react-native/USAGE.md` was still teaching the deleted API.
It **ships in the published tarball** (no `files` array, no
`.npmignore`).

### Results

```
check-types (--skip-nx-cache)   2 projects + 34 dependency tasks   
react-native vitest             298 passed / 23 files              
react-core src/v2 vitest        1503 passed / 120 files            
react-core test:scripts         47 pass / 0 fail                   
react-native test:scripts       26 pass / 0 fail                   
```

vitest transpiles without type-checking, so `tsc --noEmit` is the only
guard against a re-narrowing of the six widened annotations.
`static_quality.yml`'s `check-types` job runs it, and `nx.json`'s
`targetDefaults.check-types.dependsOn` builds react-core's dist first,
so the RN-side typecheck is meaningful rather than stale.

### What is NOT covered

Worth stating plainly: **nothing here proves the package works on a real
device.** The RN suite runs in jsdom with `react-native` aliased to a
52-line stub whose `FlatList` is a mock; no CI job runs Metro, a
simulator, or Detox; and `examples/v2/react-native/demo` is not built or
typechecked in CI. What this PR verifies is types, export surface, and
behaviour against core's real registry. Separately, #6438's finding that
RN's dist-relative `sideEffects` globs strip the polyfill import from
the built entries is still live and still out of scope.

## Follow-ups (not in this PR)

- Widen `use-default-render-tool.tsx`'s four render annotations, or
document why core's two wildcard entry points differ.
- Remove the shim next minor — tracked in #6976 and Linear OSS-1148, and
cited in the shim's JSDoc.
- Build or typecheck the RN example app in CI — the cheapest real-RN
signal available.
- `assert-headless-purity.mjs`'s `isCompleteLiteralArgument` reads
`code` rather than `masked` — @BenTaylorDev's non-blocking ask on #6438,
never actioned.
- #6346 (@davidmckayv) is still open; Ben asked for it to be closed with
a pointer when #6438 landed.

🤖 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**
- Renderers can now return `null` to intentionally suppress tool-call
UI.
- React Native supports renderer-only tool-call registration, including
named and wildcard renderers.
  - Added type guidance for React Native-safe renderer return values.

- **Bug Fixes**
- Improved compatibility routing for existing React Native
`useRenderTool` configurations.
- Prevented unnecessary renderer re-registration when dependencies are
non-serializable.

- **Documentation**
- Updated React Native guidance for `useFrontendTool`, `useRenderTool`,
shared rendering, migration, and deprecation behavior.
- Clarified renderer props, status values, and `null` rendering
behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 21:36:06 +02:00
Benjamin Taylor 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>
2026-09-09 14:26:22 -05:00
Maxim edee011099 Merge branch 'main' into refactor/rn-render-tool-hooks 2026-09-09 21:20:48 +02:00
Tyler Slaton 5d084123ea fix(react-core): improve Inspector message shortcuts (#6974)
## Problem

Inspector message shortcuts do not consistently follow Inspector
visibility.

## Why

The shortcuts should respect local development restrictions and
visibility settings.

## Fix

- Add a wrench shortcut with a hover menu and a hide-until-reload
action.
- Add `CopilotChat.inspectorTools`, with provider settings taking
precedence.
- Keep shortcuts in sync with Inspector visibility and restrict
Inspector to local development.

Includes tests and API documentation.
2026-09-09 21:18:23 +02:00
Sam Julien baa2be051d docs: curate the default LLM index (#6963)
## Summary

The same onboarding prompt supports greenfield projects, brownfield
applications or agent backends, and existing CopilotKit OSS projects
connecting to Intelligence. The index states these starting points
explicitly and explains where to run the prompt.

Replace the exhaustive default `/llms.txt` expansion with a curated
index that exposes the canonical onboarding prompt, then routes readers
to their existing agent framework. The prompt is imported from the same
template used by the docs copy button, with instructions to create a
fresh run ID per onboarding session. Research-only readers are directed
to the documentation links.

Every visible external framework in the docs registry receives an
overview and quickstart link in a dedicated section. This includes the
LangGraph variants, Google ADK, Claude Agent SDK, Strands, Microsoft
Agent Framework, and other published integrations; hidden integrations
remain excluded.

The following section covers chat, generative UI, human-in-the-loop
workflows, Rich Threads, Automatic Learning, Intelligence, Channels, and
thread imports. Built-in Agent quickstart, model selection, and server
tools are explicitly labeled. Readers are directed to framework-specific
implementation guides instead of assuming root instructions apply to
every backend. `/llms-full.txt` remains available for exhaustive
retrieval.

Channels has two prominent canonical entries, with descriptions covering
native messages, approvals, and managed connection availability.
Repeated framework-specific channel guides remain in the full index.
Regression coverage now requires Slack and Teams entry points instead of
excluding every channel route.

## Validation

- Focused index tests cover the shared onboarding prompt, deterministic
output, every visible framework's canonical overview and quickstart,
hidden exclusions, ordering, and duplicates.
- Explicit TypeScript check passed after adding the onboarding prompt.
- Changed-file formatting and diff checks passed.
- Both canonical Slack and Teams documentation URLs returned HTTP 200.
- The initial implementation passed typecheck, lint, production build,
and commit hooks.

## Existing verification failures

The broader Nx `verify-shell-docs:fast` check reported existing unknown
snippet regions, internal dead links, and essential-content findings.
The unchanged Mastra LLM rendering test also expects a `createTool`
import absent from an existing generated snippet. These failures are
recorded separately from the passing index contract tests.

Linear: PDX-332


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

## Summary by CodeRabbit

- **New Features**
- Updated `/llms.txt` with a curated documentation index covering key
capabilities, interfaces, agent workflows, persistence, and supported
channels.
- Added dynamically generated entries for supported external agent
frameworks and integration quickstarts.
- Added onboarding instructions for connecting CopilotKit with a coding
agent.
  - Clarified built-in agent documentation labels.
  - Linked to `/llms-full.txt` for the complete documentation inventory.

- **Bug Fixes**
- Improved index consistency through validated ordering, uniqueness, and
visibility checks.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 11:58:44 -07:00
Maxim f99378d34f Merge branch 'main' into refactor/rn-render-tool-hooks 2026-09-09 20:57:50 +02:00
Tyler Slaton bba4113b8e fix(react-core): improve Inspector message shortcuts 2026-09-09 11:49:42 -07:00
Sam Julien 46b8e67c83 docs: align promoted API guidance (#6962)
## Summary

- replace the legacy Next.js adapter example in the current CopilotKit
v2 component reference with `createCopilotRuntimeHandler` from
`@copilotkit/runtime/v2`
- align the public setup skill's source inventory with the repository's
flat package layout and current runtime factories
- extend focused drift guards so the promoted reference example and
setup source paths cannot silently regress

## Classification and scope

This PR fixes two stale forms in actively promoted, default-discovery
surfaces:

- `showcase/shell-docs/src/content/reference/components/CopilotKit.mdx`
was current v2 reference content but still taught
`copilotRuntimeNextJSAppRouterEndpoint`
- `skills/copilotkit-setup/sources.md` was current public setup guidance
but still pointed at retired `packages/v2/*` layouts and endpoint names

The docs landing implementation is deliberately unchanged. Its two
remaining `copilotRuntimeNextJSAppRouterEndpoint` occurrences in
`showcase/shell-docs/src/components/landing-sample-tabs.tsx` are
deferred to
[PDX-347](https://linear.app/copilotkit/issue/PDX-347/make-the-existing-docs-landing-page-the-authoritative-copilotkit),
owned by Tyler Slaton.

## Validation

Passed:

- `pnpm exec oxfmt --check` on the changed files
- `pnpm exec oxlint` on the changed TypeScript test files
- `pnpm check:plugin-skills`
- focused public-skill drift tests: 8/8
- shell-docs typecheck
- `git diff --check
origin/main...origin/codex/pdx-319-promoted-path-drift`
- source-inventory path existence check at the feature commit
- landing-page boundary check: no diff, exactly two deferred
legacy-adapter occurrences

Also run:

- `nx run @copilotkit/showcase-scripts:verify-shell-docs:fast` — fails
on broad pre-existing, untouched shell-docs debt (unknown snippet
regions, dead links, and quality-gate gaps). This PR does not expand
into that cleanup.

Linear:
[PDX-319](https://linear.app/copilotkit/issue/PDX-319/remove-current-api-and-terminology-drift-from-docs-and-public-skills)

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

* **Documentation**
* Updated the CopilotKit component reference with v1 compatibility
details, provider behavior, agent naming options, and transport
negotiation guidance.
* Updated the backend example to use the current v2 runtime handler API.
* Refreshed setup references with current source locations, handler
names, TypeScript links, and generated date.

* **Tests**
* Expanded source-inventory validation to cover debugging and setup
references.
* Added checks ensuring the CopilotKit documentation uses the current v2
runtime entry point.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 11:48:35 -07:00
Sam Julien 102599afa5 docs: align promoted API guidance 2026-09-09 13:41:32 -05:00
Ben Taylor 8b2d036cc6 chore: delete the stale changeset files (#6827)
Nothing reads `.changeset/*.md`. Releases are built from conventional
commit
subjects by `scripts/release/`, `@changesets/cli` is not a dependency,
CONTRIBUTING.md says not to add one, and `check-binaries` fails any PR
that
does.

Two inert files were still sitting in `.changeset/` anyway, and that is
enough
to make the convention look alive. Someone opening the directory finds
it
populated, reasonably concludes a changeset is expected, writes one, and
gets a
red check for following what the repo appeared to be doing. That
happened on
#6826.

This deletes the two files so the directory stops contradicting the
docs. The
CI check filters on added and modified paths only, so a PR that deletes
stale
changesets still passes.

Package `CHANGELOG.md` history is untouched — it keeps the entries those
changesets produced when the tooling was still in use.


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

## Summary by CodeRabbit

* **Chores**
  * Removed pending release notes for previously planned updates.
* No user-visible features, fixes, or behavior changes are included in
this change.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 13:41:10 -05:00
Ben Taylor 55981b3f64 docs: fix MCP endpoint and native Codex setup (#6955)
The documented Codex command connects `mcp-remote` to the server root,
which returns 404 for both HTTP and SSE. Use the working `/mcp` endpoint
with Codex's native Streamable HTTP support. Correct the same root-URL
error in the shared HTTP/stdio examples, and add `-y` to the remaining
npx bridge commands.

The guide now explains how to replace the old configuration and verify
an actual search, since `codex mcp list` only verifies registration.

Validation:
- Reproduced the old command failing with `Cannot POST /` and SSE 404.
- Live `/mcp` initialization and tool discovery succeeded.
- Codex CLI 0.153.4 successfully called `search-docs` and `explore-docs`
against `/mcp` (only those read tools preapproved for the noninteractive
smoke test).
- Docs content generation and 47 tests passed across docs-render, .NET
guidance, frontend-tool coverage, and setup-concept suites.
- Updated MDX compiles and every JSON example parses; commit hooks
passed.


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

## Summary by CodeRabbit

* **Documentation**
  * Updated MCP setup examples with the correct endpoint URL.
  * Added automatic confirmation to relevant npx commands.
* Revised Codex instructions to use Streamable HTTP MCP servers
directly.
* Updated Codex configuration examples to use URL-based server entries.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 13:40:24 -05:00
Sam Julien 5a62ca1f79 docs: clarify onboarding starting points in LLM index 2026-09-09 11:15:18 -07:00
Sam Julien 1097a98474 docs: expose canonical onboarding prompt in LLM index 2026-09-09 11:12:29 -07:00
Martha Kelly Schumann 41357e78c9 fix(strands): validate dynamic A2UI generation contract (#6903)
## Summary

- require every generated A2UI component to include the renderer's `id`
and `component` fields
- restrict generated component names to the starter's registered
dashboard catalog
- reject malformed or ambiguous roots before emitting renderer
operations

## Testing

- `npm test`
- `npm run typecheck`

Fixes FAC-196
2026-09-09 10:58:24 -07:00
copilotkit-qa-bot[bot] 5fbd0f892d ci: ignore unused Chrome apt source 2026-09-09 10:46:43 -07:00
copilotkit-qa-bot[bot] 737c2f82d8 Merge remote-tracking branch 'origin/main' into codex/fac-196-a2ui-contract 2026-09-09 10:41:05 -07:00
Martha Kelly Schumann 5c221d9b8b fix: complete CrewAI flows starter prompts (#6961)
## Summary

- upgrade the CrewAI Flows starter to the maintained CrewAI and AG-UI
bridge versions
- constrain the documented Python range to versions covered by the
regenerated lockfile
- add credential-free smoke coverage for assistant text and exactly one
terminal AG-UI event

## Validation

- `uv sync --frozen` and imports on Python 3.10, 3.11, 3.12, and 3.13
- mocked prompt through the actual FastAPI endpoint, including assistant
text and `RUN_FINISHED`
- `npm run build`
- `docker compose config --quiet`

Fixes FAC-156
2026-09-09 10:40:05 -07:00
copilotkit-qa-bot[bot] c8abc62578 test: allow A2UI starter smoke responses 2026-09-09 10:37:21 -07:00
Maxim e69a7c08e2 docs(react-native): cite the shim's removal issue in its deprecation notes
@BenTaylorDev noted that "removal in the next minor" appeared 11 times across
5 files with no issue behind it, which is how a shim becomes permanent. Filed
as CopilotKit/CopilotKit#6976 and Linear OSS-1148, and cited in the shim's
JSDoc, the entry-point comment, and the reference page.

The runtime warning text is deliberately unchanged — a test asserts on it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 19:32:46 +02:00
Maxim ab9376ac49 docs(react-native): correct the wildcard mechanism and stop denying the silent path
Three corrections to the `useRenderTool` reference, matching the code changes
in the preceding commit.

The wildcard section said `name: "*"` on the old hook "advertised it to the
model on every run". It never did — core filters the name `*` out of the tool
list it hands the agent. Replaced with what actually happened: `*` is core's
catch-all handler name, and because the tool-result insertion and the
follow-up-turn request sit outside the check for whether the wildcard tool has
a `handler`, a display-only `*` tool answered every otherwise-unanswered tool
call with an empty tool result and asked for another turn. Includes the
one-turn measurement (2 turns and an empty result before, 1 turn and no result
after) and states the bound: a call whose result has already arrived was never
affected.

"What the warning does not cover" claimed a call with neither `description` nor
`handler` "says nothing, because there is nothing to say". There was. That
shape was unreachable for TypeScript callers (`description` was required) but
perfectly reachable from plain JS, where it registered and advertised a real
tool — so it lost registration and advertisement silently, through the one
shape routing cannot discriminate. That bullet is gone, the section is down to
three gaps, and a new "The shape routing cannot discriminate" section explains
why the warning now fires on that route and what to do about it.

Also: the route table gains a "Registers in" column recording that the
renderer-only routes land in `useEffect` where `useFrontendTool` uses
`useLayoutEffect`, so a call that used to register in the layout phase now
registers one phase later — untyped-JS callers only, and it matches core's web
behaviour. The migration table's renderer-only row no longer says "silent".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015XyonXjic9ZEuPTpzgN2uS
2026-09-09 19:32:46 +02:00
Maxim af0c5628e0 fix(react-native): correct what a tool named * did, and warn on the last silent route
Two things, both in the render-tool shim and its tests.

1. The PR's headline claim was false on current main.

The shim, its tests and `headless.ts` all said the old RN hook "advertised
`*` to the model". Core never has. `buildFrontendTools` filters the name out
of the list it hands the agent (`core/src/core/run-handler.ts`, the
`tool.name !== WILDCARD_TOOL_NAME` clause), with a comment saying that
advertising it would offer the agent a tool named `*`. That filter arrived in
31aa1e2162, hours before this PR was opened; the rebase moved the code forward
and left the prose behind.

Measured by vendoring origin/main's hook verbatim and driving it through this
branch's own recording agent:

  A  old hook, wildcard        in core.tools true   advertised [[]]
  B  old hook, no description  in core.tools true   advertised [["legacyJsShape"]]

Row A is the correction. Isolated further: two `useFrontendTool` registrations
with identical description/parameters/render and only the name differing give
registry `["*","notAWildcard"]` and advertised `[["notAWildcard"]]`, so the
NAME is the sole cause.

The real mechanism is stronger. `*` is core's catch-all HANDLER name: when a
tool call has no matching frontend tool and no result yet, core reaches for
`getWildcardTool()` and runs `executeWildcardTool`, whose tool-result splice
and follow-up return sit OUTSIDE the `if (wildcardTool?.handler)` guard (opens
at :1000, closes at :1089; the splice block is :1091-1120, with
`toolCallResult` initialised to `""`). Driving one turn through it confirms it
rather than only tracing it — an assistant message calling an unregistered
tool yields 2 turns and a spliced `{ toolCallId, content: "" }` through the old
hook, versus 1 turn and no tool result through core's `useRenderTool`. So a
display-only wildcard was auto-answering every otherwise-unanswered tool call
and paying for a follow-up turn. Bounded: a server-side call whose result has
already arrived never reaches that branch.

Two `expect(agent.advertised).toEqual([[]])` assertions were introduced under
comments calling them the ones that catch this "where it would actually hurt".
They cannot: they return the same value on origin/main. Both are kept as
forward guards against someone making `*` advertisable later, with comments
that now say so and point at the registry assertions as the discriminating
ones. Confirmed by mutation: removing rule 1 from `routeFor` kills
`getTool({ toolName: "*" })` and leaves both `advertised` assertions green.

2. The renderer-only route is no longer silent.

`{ name, parameters, render }` is the one shape routing cannot discriminate:
it is both the correct new renderer-only spelling and an old plain-JS call
that registered AND advertised a real tool (`description` was required by the
old types, so only untyped JS could reach it; `FrontendTool.handler` is
optional and `buildFrontendTools` does not filter on it). Rows B and C are
that comparison — `[["legacyJsShape"]]` before, `[[]]` after, zero warnings.
So the one shape the shim could not route was also the one it said nothing
about, which is the exact failure the shim exists to prevent.

`warnRouted` now warns on that route too, deduped per tool name and dev-only
like the others, saying the call registers a renderer only and pointing at
`useFrontendTool` for anyone who relied on the advertisement. Guarded on the
ROUTE rather than merely on the absence of legacy fields, so a frozen
`useFrontendTool` route whose fields were later dropped is not mislabelled.
The pre-existing wildcard-branch message is likewise no longer worded as if
every renderer-only route were the wildcard.

The test that asserted silence on this route is updated rather than left
asserting the old behaviour, and two new tests cover the warning and its
dedup/production gating. All three fail if the unconditional warn is reverted.

Also: dropped the duplicated per-status-spread explanation (it appeared in
both the JSDoc and the body of `toFrontendTool`), and documented that the
renderer-only routes register in `useEffect` where `useFrontendTool` uses
`useLayoutEffect`, so a call that used to land in the layout phase now lands
one phase later. Both hooks' effect types verified in the current source.

react-native vitest 300/300 across 23 files at default settings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015XyonXjic9ZEuPTpzgN2uS
2026-09-09 19:32:46 +02:00
Maxim 1cc28f3243 fix(react-native): replace a literal NUL byte in the render-tool shim's warn key
The route-drift `warnOnce` key in the `useRenderTool` shim separated
`${config.name}` from `route-drift` with an actual 0x00 byte, not the escape
sequence `\x00`. It sat at byte offset 14224.

Two consequences, both verified:

  - ripgrep classified the whole file as binary ("binary file matches (found
    "\0" byte around offset 14224)") and printed NO lines, so an `rg` sweep
    over `packages/` returned nothing from this file — including for the very
    identifiers `headless-entry-surface.test.ts` polices by text. `file(1)`
    reported `data`.
  - `git diff` still rendered it as text, because git's binary heuristic only
    reads the first 8000 bytes and this byte is past that. It looked like an
    ordinary space in every diff view, which is how it survived review.

The byte also reached the emitted bundle as part of the dedup key.

Replaced with `:`. Verified at the byte level rather than visually: a scan for
bytes below 0x20 other than tab/newline/carriage-return now returns none,
`file(1)` reports "UTF-8 text", and `rg` prints 28 matching lines where it
previously printed zero. Every other file in the PR's diff was scanned the same
way; none contained a control byte.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015XyonXjic9ZEuPTpzgN2uS
2026-09-09 19:32:45 +02:00
Maxim 95c9c0a5a0 docs(react-native): make the render-tool docs true of the shim, not of the deletion
The deprecated `useRenderTool` shim landed after these pages were written, so
they described a harder break than the code delivers — "React Native has no
render-tool API of its own", and a migration table presenting the old hook as
simply gone.

reference/react-native/hooks/useRenderTool:

- The overview and the migration narrative now say what actually happens: the
  name still exists, still works this release, routes by shape, warns, and goes
  away in the next minor. The routing table is the shim's three rules verbatim
  (src/hooks/useRenderTool.ts `routeFor`), including the unconditional wildcard
  and why it is unconditional.
- The warning is quoted from the source string rather than paraphrased, and gets
  its own "What the warning does not cover" section: dev-only gate, per-name
  dedup for the module's lifetime, silent when no old field is present, and
  emitted from an effect so an unmounted screen never warns.
- "Three shapes the compiler does not catch" is kept, not deleted. It is now
  framed as what the shim exists to route — and as what to audit by hand anyway,
  because the notice is development-only and because all three go back to
  degrading silently once the shim is removed.
- Two new migration rows for losses the table omitted: bare `RenderToolProps`
  is now `TS2314` (core's `S` has no default where RN's `T` did), and the
  `args` -> `parameters` render-prop rename is still `TS2339` even through the
  shim — verified with tsc, the shim restores the old CONFIG fields only. The
  `RenderToolFunction<T>` row now points somewhere instead of saying "gone".
- `parameters` was documented as `Partial<T> | T`, using a generic this page
  renamed to `S` and explicitly warns is the schema rather than the arguments.
  It is `Partial<InferSchemaOutput<S>> | InferSchemaOutput<S>`
  (react-core/src/v2/hooks/use-render-tool.tsx:9-31).

reference/react-native/hooks/useFrontendTool had zero mentions of `ReactElement`,
`ReactNode` or `FlatList` despite being where the migration sends people. It now
documents that its `render` is a `React.ComponentType` and therefore accepts a
bare string that throws on a device, and points at the opt-in
`FrontendToolRenderFunction<T>`.

docs/frontends/react-native and packages/react-native/USAGE.md (which ships in
the npm tarball — no `files` array, no `.npmignore`) get the same corrections at
their own length.

Every code sample added here was typechecked by pasting it into the package and
running `tsc --noEmit`, including the ones asserted to FAIL. Internal links and
heading anchors were checked mechanically against the content tree.
2026-09-09 19:32:45 +02:00
Maxim 2386b5ab65 feat(react-native): add an opt-in element-only render type for useFrontendTool
The convergence deleted RN's `RenderToolFunction`, which was the only thing
narrowing an RN render function to `ReactElement | null`. The migration points at
`useFrontendTool`, whose `render` is `ReactToolCallRenderer<T>["render"]` — a
`React.ComponentType`, so it returns `ReactNode`. Verified with tsc: a `render`
returning a bare string compiles inline in a `useFrontendTool` call today, and
then throws "Text strings must be rendered within a <Text> component" on a
device. Before the rename that call site was a compile error, so the migration
traded away a real crash-prevention property.

`FrontendToolRenderFunction<T>` gives it back. Named for the hook it annotates
rather than reusing `RenderToolFunction`: that name was just deleted, and
reviving it against a DIFFERENT hook's props would be the same defect this PR
exists to fix — a name whose meaning silently moved.

DERIVED from core's contract, not re-declared: props come from
`React.ComponentProps<ReactToolCallRenderer<T>["render"]>` unchanged and only the
return type is React Native's. That is the precedent the deleted file set, and
the reason is on record — the last time this package declared its own render-prop
shape it drifted to `{ args: T; status: "executing" | "complete"; result?: string }`
(commit ebf0f94fb8^), with no `name`, no `toolCallId`, no in-progress arm and
`args` unconditionally complete. `RenderToolProps` / `UseRenderToolOptions` are
NOT reintroduced.

It is opt-in and the JSDoc says so: it changes no hook signature, so an
unannotated inline renderer is still checked against core's `ReactNode` contract.

The type test is a `.test-d.tsx`, which `tsc --noEmit` compiles (the package
tsconfig has `include: ["src"]`) but vitest's
`src/**/__tests__/**/*.{test,spec}.{ts,tsx}` glob does not collect. Both
directions bite, proven by mutation: widening the return to `ReactNode` fails
with 4x TS2578 (unused directive), and replacing the derived props with `any`
or `unknown` fails too — so "derived, not re-declared" is asserted, not just
asserted about.

Also trims the 21-line comment over the render-tool re-exports down to what a
reader needs at that line: the shim is temporary, and do not reintroduce a local
hook under either name. The rest now lives on the reference page and in the
guard's own comment.
2026-09-09 19:32:44 +02:00
Maxim 29d16e4db3 test(react-native): make the shim's warn-once dedup and frozen route bite
Two gaps found by mutating the shim.

The "fires once per distinct tool name" test passed with the module-level dedup
set DELETED, because it only re-rendered: the warning lives in an effect whose
deps are the tool name and the routed hook, so a re-render never re-runs it and
the effect's dependency array was doing all the work. It now unmounts and mounts
again — a fresh mount runs a fresh effect, which is navigating back to a screen
on a real device — so the dedup set is what the assertion rests on. Deleting it
now fails with 2 calls instead of 1.

The frozen route had no test at all. A config whose shape changes between
renders (`handler: enabled ? fn : undefined`) keeps the route it was first
registered under, because a hook cannot be called conditionally unless the
condition is stable for the component's lifetime. That limitation is now
asserted from both ends: the drift warning fires, and the tool is still not
registered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015XyonXjic9ZEuPTpzgN2uS
2026-09-09 19:32:44 +02:00
Maxim 2b34d975e1 feat(react-native): reintroduce useRenderTool as a loud, deprecated shim
The convergence removed RN's `useRenderTool` outright. That break ships in a
MINOR — @copilotkit/react-native is in the 16-package lockstep `monorepo`
release scope, so a major is not on the table — which leaves the compiler as
the only signal reaching consumers, and there are three call shapes it cannot
see: a hoisted config object whose `render` ignores its props (excess-property
checking needs a fresh literal), the old fields arriving via a spread into an
otherwise fresh literal, and an untyped or `@ts-nocheck` call site (plain-JS RN
screens are common).

In all three, core's `useRenderTool` silently ignores `description` and
`handler`, and because core's bridge spreads `{ ...props, parameters: props.args }`
the old `render: ({ args }) => …` keeps painting exactly as before. The screen
looks unchanged while the tool stops being registered and advertised and the
handler never runs again. This shim is the softer landing @BenTaylorDev asked
for: it routes those calls the way the old hook did, and says so out loud. It
is deprecated, `@deprecated`-marked on every overload, and scheduled for
removal in the next minor; react-core is untouched.

Routing, exactly:

1. `name === "*"` wins UNCONDITIONALLY -> core's `useRenderTool` (renderer-only,
   schema-less wildcard path), then warns about the old fields it ignored. This
   is deliberately not the obvious reading. The old hook made `description`
   REQUIRED, so every wildcard renderer anyone ever wrote carries the old tool
   fields; routing "has old fields" to `useFrontendTool` would recreate the
   original `*`-named-tool bug for precisely the people who had tried hardest to
   use the wildcard.
2. otherwise `handler` or `description` present -> core's `useFrontendTool`
   (tool AND renderer, which is what the old hook actually did), warning that
   the call should be renamed.
3. otherwise -> core's `useRenderTool`, unchanged.

The route is frozen at first render, because a hook cannot be called
conditionally unless the condition is stable for the component's lifetime; a
config that changes shape mid-life keeps its original route and gets its own
warning rather than being silently re-registered elsewhere. Warnings are
dev-only (`process.env.NODE_ENV`), `[CopilotKit]`-prefixed and deduped through a
module-level Set — once per distinct tool name, never once per render, matching
react-core's `warnedUnknownStatuses` idiom.

Registration is DELEGATED on every path: this package still owns no registry.

The entry-surface suite asserted that RN's `useRenderTool` IS core's binding,
which the shim makes false. Rather than weaken that guard — it closed a blind
spot in which every presence check stayed green through the whole convergence
while verifying nothing — its two halves are replaced at equal strength:
`useFrontendTool`'s identity assertion stays as-is, and `useRenderTool` is now
policed by (a) a delegation test that reads which module the entry exports the
name from, asserting runtime identity when that is core's entry and otherwise
requiring the local module to import AND CALL both core hooks, and (b) a
package-wide deny-list that fails if any module in the headless graph touches
`addTool` / `removeTool` / `addHookRenderToolCall` / `renderToolCalls` or
`createContext` — i.e. if RN ever rebuilds a local registry.

Proven by mutation, with the routing suite reading core's own observable state
(`getTool`, `core.tools`, the tool list core hands an agent on a real run)
rather than a spy's arguments.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015XyonXjic9ZEuPTpzgN2uS
2026-09-09 19:32:43 +02:00
Maxim 62cf3878d7 docs(react-native): document core's useRenderTool as React Native's own
Rewrites the RN reference page for core's hook, points tool-plus-renderer users
at `useFrontendTool`, and carries the migration table. That table is the actual
consumer-facing channel for this break: the release-note collector
(scripts/release/lib/changes.ts) reads `git log --format=%H %s` into a
`{ hash, subject }` type, so a `BREAKING CHANGE:` footer has nowhere to land
(#6479).

The migration's loud-failure guarantee is stated precisely rather than
absolutely, because the absolute form is false. TypeScript's excess-property
check rejects an old call site (`TS2769` on `description`) only when the config
is a fresh object literal with those fields written inline; the renamed render
prop gives `TS2339`. Three shapes escape it, each named on the page: a hoisted
config whose `render` ignores its props, the same fields arriving via a spread,
and an untyped or `@ts-nocheck` call site. The last is the one to worry about —
core's bridge spreads `{ ...props, parameters: props.args }`, so `args` still
arrives at runtime and an old renderer keeps painting correctly while the tool
has silently stopped being registered.

Retires three § Known limitations entries this convergence closes (the wildcard,
`followUp`/`available` forwarding, and `handler`'s missing context argument) and
leaves the unrelated ones intact.

Deletes a false claim the previous docs shipped: that comparing `status` against
a bare string literal does not typecheck. A string-enum member is assignable to
its own literal type, so the comparison compiles and narrows in every direction
— verified with tsc in all four combinations. RN's `status` moves from the
`ToolCallStatus` enum to core's string-literal union, and that is explicitly not
a break; no migration work follows from it.

Also updates packages/react-native/USAGE.md, which still taught the deleted API.
That file ships in the published tarball — package.json declares no `files` array
and there is no .npmignore — so the package was documenting an API the package no
longer has. Its remaining samples were compiled against the shipped overloads.

Corrects the primary guide's claim that a `ReactElement | null` render return is
React-Native-specific: it is identical on the web. What is RN-specific is the
consequence of `useFrontendTool`'s looser render type, where a bare string
typechecks and then throws inside a `FlatList`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 19:32:43 +02:00
Maxim 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>
2026-09-09 19:32:42 +02:00
Maxim 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>
2026-09-09 19:32:42 +02:00
copilotkit-qa-bot[bot] 9d70137b04 fix: pin starter React smoke dependencies 2026-09-09 10:22:54 -07:00
copilotkit-qa-bot[bot] c058c0d31a fix: pin starter React smoke dependencies 2026-09-09 10:22:54 -07:00
copilotkit-qa-bot[bot] b9c1d61809 Merge remote-tracking branch 'origin/main' into codex/fac-156-crewai-flows 2026-09-09 10:15:20 -07:00
copilotkit-qa-bot[bot] 2d1eb104fd Merge remote-tracking branch 'origin/main' into codex/fac-196-a2ui-contract 2026-09-09 10:15:20 -07:00
Martha Kelly Schumann 0ad17c3061 Document Strands TypeScript sub-agent state helper (#6946)
## Summary

- include the Strands TypeScript shared-state helper in the Sub-Agents
demo source tabs
- verify the generated documentation bundle exposes the delegation state
implementation

## Testing

- `nx run @copilotkit/showcase-scripts:test --
__tests__/bundle-demo-content.test.ts`
- `nx run @copilotkit/showcase-scripts:validate-manifests`

Fixes FAC-197
2026-09-09 10:14:48 -07:00