Commit Graph

16173 Commits

Author SHA1 Message Date
copilotkit-qa-bot[bot] eff676e9bb Merge remote-tracking branch 'origin/codex/fac-202-shared-state-rendering' into codex/fac-202-shared-state-rendering
# Conflicts:
#	showcase/shell-docs/src/lib/__tests__/setup-concept-rendering.test.tsx
2026-09-09 10:53:43 -07:00
copilotkit-qa-bot[bot] 29cf68640a fix(docs): preserve complete production code blocks 2026-09-09 10:52:49 -07:00
copilotkit-qa-bot[bot] 0030cee0d2 fix(docs): preserve complete production code blocks 2026-09-09 10:29:44 -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
copilotkit-qa-bot[bot] f712369889 Merge remote-tracking branch 'origin/main' into codex/fac-197-state-snapshot-docs 2026-09-09 10:01:14 -07:00
Martha Kelly Schumann 9f580c7f69 docs(crewai): verify AG-UI endpoint before cloud setup (#6975)
## Summary

- explain that CrewAI remote endpoints must expose the AG-UI protocol
through the official adapter
- add a direct SSE preflight before Copilot Cloud registration
- guard the documented bridge and lifecycle contract with a focused test

## Testing

- `npx vitest run src/lib/__tests__/crewai-flows-quickstart.test.ts`
- `npm run typecheck`
- `npm run build`
- CrewAI starter Docker smoke suite (4 tests)

Fixes FAC-162
2026-09-09 09:57:52 -07:00
copilotkit-qa-bot[bot] be5315556c docs(crewai): verify AG-UI endpoint before cloud setup 2026-09-09 09:24:28 -07:00
renovate[bot] 8991dbdc45 chore(deps): update github actions (#6971)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-java](https://redirect.github.com/actions/setup-java) |
action | patch | `v6.0.0` → `v6.0.1` |
|
[zizmorcore/zizmor-action](https://redirect.github.com/zizmorcore/zizmor-action)
| action | patch | `v0.6.3` → `v0.6.4` |

---

### Release Notes

<details>
<summary>actions/setup-java (actions/setup-java)</summary>

###
[`v6.0.1`](https://redirect.github.com/actions/setup-java/compare/v6.0.0...v6.0.1)

[Compare
Source](https://redirect.github.com/actions/setup-java/compare/v6.0.0...v6.0.1)

</details>

<details>
<summary>zizmorcore/zizmor-action (zizmorcore/zizmor-action)</summary>

###
[`v0.6.4`](https://redirect.github.com/zizmorcore/zizmor-action/releases/tag/v0.6.4)

[Compare
Source](https://redirect.github.com/zizmorcore/zizmor-action/compare/v0.6.3...v0.6.4)

[Sponsorship is
appreciated!](https://redirect.github.com/sponsors/woodruffw/)

zizmor 1.30.1 is now the default version.

Release notes: <https://docs.zizmor.sh/release-notes/#&#8203;1301>

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/Los_Angeles)

- Branch creation
  - "before 9am every weekday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/CopilotKit/CopilotKit).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC42OS4xIiwidXBkYXRlZEluVmVyIjoiNDQuNzEuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-09-09 15:52:26 +00:00
renovate[bot] 9596b0e897 chore(deps): update github actions 2026-09-09 15:22:05 +00:00
Ben Taylor 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 -->
2026-09-09 10:20:46 -05:00
Ben Taylor b104f894c2 test: stop two unit tests failing at their timeout ceiling (#6973)
## What is broken

Two unit tests pass with almost no margin, so either can redden a PR
that did not touch it. Both were measured on `test / unit`, comparing
shards of the same commit.

| test | limit | measured | outcome |
|---|---|---|---|
| `react-core` `A2UIMessageRenderer.test.tsx` › "should render A2UI
surface content via React renderer" | 5000ms (vitest default) | 4798ms
on `Node 24.x, React 18` | timed out on `Node 24.x, React 19`, same
commit ([run
34357870288](https://github.com/CopilotKit/CopilotKit/actions/runs/34357870288))
|
| `web-inspector` `threads-state-lab.spec.ts` › "drives ... all 34
Thread routes" | 60s | 29.2s (N24/R19), 56.1s (N22/R18), 57.1s (N20/R19)
| 41.4s then a 60s timeout on two runs of one commit on N20/R18 |

The A2UI failure was found on PR #6830, whose diff is release scripts
and markdown. It reached `react-core` only because deleting
`packages/react-core/CHANGELOG.md` pulls that package into the `nx
affected` set. The identical tree had passed all six shards a week
earlier.

## What this changes

**1. `react-core`: the A2UI test file imports its module once,
statically.** Raising the number was not the right fix here, because the
test's own work is about 10ms. The cost was structural:

`await import("../a2ui/A2UIMessageRenderer.js")` inside a test body
loads the whole `@copilotkit/a2ui-renderer` graph, which
`vitest.config.mjs` inlines (`server.deps.inline`), and vitest charges
that one-time transform to whichever test runs first. Measured locally
with `--reporter=verbose`:

```
✓ should render A2UI surface content via React renderer   502ms   <- pays the import
✓ should update surface when operations change             14ms
✓ should return null when no operations are provided        6ms
... the remaining 15 tests: 0-15ms each
```

All 11 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.

This closes the class for the whole file, not just for today's first
test: any test in it can be the one that pays, and reordering or adding
a test moves the target.

**2. `web-inspector`: the 34-route lab test's ceiling goes from 60s to
180s.** This one is genuinely 34 routes of real work against a real lab
server, so its cost is the sum of 34 bounded waits. There is no
structural fix available at this size without splitting the test, which
changes its shared setup and teardown. 180s is about 3x the slowest
passing run rather than just above it. The number guards against a hang;
nothing in the test asserts elapsed time.

## Testing

**The A2UI fix is measured on both sides, not asserted.** Same file,
same command, before and after:

```
before:  first test 502ms  |  import 27ms   |  tests 709ms
after:   first test  53ms  |  import 468ms  |  tests 159ms
```

The 449ms leaves the first test's budget and reappears in the import
phase, which is exactly the claim. Pass/fail composition is unchanged:
15 passed / 3 failed before, 15 passed / 3 failed after, the same three
names.

**Those three local failures are pre-existing and environmental,
verified rather than assumed.** They exercise `warnAboutUnresolvedRoot`,
added by `b0f349fcfb fix(a2ui): report a surface whose root component
never resolves`. That commit is in `origin/main`, which this branch is
built on, but the worktree resolves `@copilotkit/a2ui-renderer` through
a symlink into the main checkout, which is on an older branch. So
`react-core` at `origin/main` runs against a pre-`b0f349fcfb`
`a2ui-renderer` dist and the reports come back naming `"undefined"`
instead of `"root"`. CI does a real install, where these three pass.

**Typecheck is byte-identical before and after.** `tsc --noEmit` in
`packages/react-core`, run against the base file and then against mine:

```
BEFORE errors: 64
AFTER  errors: 64
diff of (file, line, col, code) tuples: empty
```

Zero of the 64 name the changed file. All 64 are the same worktree dep
skew described above (for example `Module '"@copilotkit/core"' has no
exported member 'isInspectorThreadBridgeEnabled'`), and CI is green on
`main`.

**Also run:** `oxlint` on both changed files (1 warning, pre-existing —
the identical warning appears on the base file, verified by linting
`origin/main`'s copy), and `oxfmt --check` on both (clean).

**Not verified locally:** the `web-inspector` spec does not collect in a
worktree. It imports `ws`, which is not declared in
`packages/web-inspector/package.json` and so is not resolvable through
the symlinked store. The change there is one numeric literal plus a
comment, and CI runs the real lane.

Both commits are `--no-verify`. The pre-commit nx lane cannot run in
this worktree: it pulls in
`@copilotkit/runtime:generate-graphql-schema`, which fails with `Package
subpath './telemetry' is not defined by "exports" in
.../node_modules/.pnpm/node_modules/@copilotkit/shared/package.json`
because it resolves `@copilotkit/shared` from the main checkout's older
store. That failure is independent of this diff, and CI on this PR runs
the real lane.

## Not in this PR

- `react-core` `CopilotChatPerf.e2e.test.tsx` › "renders 100 messages
without error" was the third test on my list. It needs nothing:
`6870c4a926` already gave it a 20s test timeout and a 15s inner
`waitFor`.
-
`packages/vue/src/v2/components/chat/__tests__/CopilotThreadsDrawer.ssr.test.ts`
has the same shape as the A2UI file, a dynamic import in its first test.
It has not flaked yet, and the Vue drawer has work in flight, so it is
left alone.
- `packages/web-inspector` imports `ws` without declaring it as a
dependency. It works in CI through hoisting. Worth fixing separately.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


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

## Summary by CodeRabbit

* **Tests**
* Improved test reliability by replacing repeated dynamic module loading
with a shared static import.
* Increased the timeout for a large integration test to accommodate
slower CI environments.
  * Added documentation explaining the extended test timeout.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 09:39:08 -05:00
Benjamin Taylor d85fa7277b test(web-inspector): give the 34-route Threads lab a real timeout budget
One test drives 34 routes against a real lab server, so its cost is the
sum of 34 bounded waits and it lands wherever the runner's load puts it.
Measured across `test / unit` shards of the same commit: 29.2s on
Node 24 / React 19, 56.1s on Node 22 / React 18, 57.1s on
Node 20 / React 19, and, on two runs of one commit on Node 20 / React 18,
41.4s and then a timeout at the old 60s ceiling.

A five percent margin on the slowest shard is not a budget, so the new
ceiling is 180s, about three times the slowest passing run. The number
guards against a hang. It asserts nothing about elapsed time, because
this test measures no durations.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 09:19:59 -05:00
Benjamin Taylor 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>
2026-09-09 09:19:50 -05:00
Benjamin Taylor fd5c5dfcaa docs(release): correct the publish lane's stale nx release claim
The header said "publish via nx release". The lane builds with
`nx run-many -t build`, but publishing is publish-release.ts driving
`npm publish <tarball>` under the pinned npm from lib/npm-cli.ts, which the
OIDC trusted-publisher binding requires. Versioning and notes happen earlier,
in the release PR.
2026-09-09 08:31:01 -05:00
Benjamin Taylor 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.
2026-09-09 08:31:01 -05:00
Benjamin Taylor afc34d9242 fix(release): raise the notes generator's max_tokens off 2048
A monorepo release carries dozens of PRs; 2048 output tokens truncates the
notes mid-section, and the truncated text is what ships as the release body.

Leaves the model pin alone — main already moved it to a current, undated id.
2026-09-09 08:31:00 -05:00
Benjamin Taylor c4be50cc7e style(release): drop the imports and blank line the Notion removal orphaned 2026-09-09 08:31:00 -05:00
Benjamin Taylor 461bb17913 fix(release): scope the AI notes prompt to the lane being released
The generator fed the model a repo-wide `git log -50` as context and told it
it was writing notes for "CopilotKit vX.Y.Z, an open-source AI agent framework
for React applications" — wrong on three counts for an angular or channels
release: the wrong commits, the wrong framing, and the wrong release title
(only the monorepo lane is titled `vX.Y.Z`).

Pass the scope through, build context from that lane's own commits, name the
packages actually being published, and tell the model to write about nothing
else.

Also fix the API call itself: the pinned model string was a dated snapshot,
max_tokens 2048 could truncate a large release, and the response reader took
content[0].text — which is not the text block on models that return thinking
blocks first.
2026-09-09 08:31:00 -05:00
Benjamin Taylor d3edfa089c fix(release): keep breaking-change footers written on branch commits
Selecting mainline commits alone drops a BREAKING CHANGE footer that lives on
a branch commit rather than in the PR description: the merge inherits neither.
Measured against real history, v1.60.0..HEAD lost 2 of 2 notes.

Fold each merge's branch messages into its body before extraction, so the
entry list stays one-per-PR while the footer scan sees the whole PR. Both
ranges checked now report zero loss versus the previous selection.
2026-09-09 08:31:00 -05:00
Benjamin Taylor 476b48a7d6 fix(release): commit release-notes.md to the release branch, drop the Notion round-trip
release-notes.md and release-notes-notion.json were both gitignored, so
create-pull-request silently skipped them. The notes never reached the release
branch, the publish job's readFileSync missed, and every release since this
lane was built shipped its "Release <tag>" fallback body — v1.70.0,
channels/v0.6.0 and angular/v0.4.0 all have bodyless GitHub Releases.

The same ignore rule severed the Notion lane: without the json ref in the
checkout, publish-release could never read an edited draft back, so that path
had never run either. Remove it rather than repair it — the release PR is
already the review surface, and editing release-notes.md on the branch is a
plainer gate than a Notion page.

Guard the ignore rule with a test, since re-adding it would break the lane
again without breaking anything else.
2026-09-09 08:30:59 -05:00
Benjamin Taylor 8ddb6158a4 fix(release): select release-note commits by PR, scoped to the release lane
Release notes were assembled from every commit since the scope's tag with
--no-merges. Two things were wrong with that:

- No path filter, so a scope inherited every other lane's work. The angular
  v0.5.0 notes drew from 159 commits, 4 of which were angular.
- --no-merges is backwards for this repo. PRs land as merge commits, so the
  merge is the unit of change and the only commit carrying the (#1234)
  reference; --no-merges dropped every PR boundary and kept the intermediate
  branch commits instead.

Walk --first-parent over the scope's package directories, drop commits no
consumer would read about (test/ci/style, chore except chore(deps), and the
release commit itself), and parse the PR number off the subject.

For angular v0.5.0 this turns 159 entries into the 4 real PRs.
2026-09-09 08:30:59 -05:00
Ben Taylor ad6d42a74a fix(react-core): register v1 readables before sibling effects run (#6968)
## What

`useCopilotReadable` (v1) 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.

This registers 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.

## Why now

This completes the half of #4259 that `f9b306aa4e` did not cover. That
commit fixed the v2 `useFrontendTool` the same way; the v1 readable had
since moved to `packages/react-core/src/v1-deprecated/hooks/` and was
left on `useEffect`. #4259 is now closed as superseded, with this as the
named follow-up.

The v2 siblings `useAgentContext` and `useFrontendTool` already register
in the layout phase, so this aligns the last one.

## Scope

React's layout-vs-passive split is what makes this bug possible, so
`packages/vue` and `packages/angular` are not the same class and are
untouched. `use-render-tool.tsx` is still on `useEffect` but registers
only a renderer, so it never reaches the connect payload.

## Testing

**1. The race reproduces on unmodified `main`.** Hook reverted to its
pre-fix body, new test kept:

```
 FAIL  src/v1-deprecated/hooks/__tests__/use-copilot-readable.test.tsx > useCopilotReadable > registers the context before an earlier-mounted sibling's useEffect runs
AssertionError: expected [] to include 'employees'
 ❯ src/v1-deprecated/hooks/__tests__/use-copilot-readable.test.tsx:305:25
```

This is also the mutation check: the test fails when the mechanism is
broken, so it is not self-fulfilling. The consumer is mounted **first**
on purpose — mounting it second passes with either hook and proves
nothing.

**2. Suite passes with the fix.**

```
 ✓ src/v1-deprecated/hooks/__tests__/use-copilot-readable.test.tsx (13 tests) 17ms
 Test Files  1 passed (1)
      Tests  13 passed (13)
```

**3. No regression across the v1 tree.** `vitest run src/v1-deprecated`,
compared against a clean-`main` baseline in the same worktree:

| | Tests passed | Collection failures |
|---|---|---|
| clean `main` baseline | 106 | 9 |
| this branch | 107 | 9 |

The 9 collection failures are identical in both runs
(`@modelcontextprotocol/ext-apps/app-bridge` resolution in a symlinked
worktree) and are not caused by this change.

**4. `tsc --noEmit`** — 64 pre-existing errors in the worktree, **0** on
either touched file (`grep -c use-copilot-readable` on the output → 0).
Same cross-package dist resolution drift.

**5. `oxfmt`** on both files — no changes.

### Committed with `--no-verify`

The pre-commit hook cannot complete in this worktree:
`@copilotkit/runtime:generate-graphql-schema` dies on
`packages/runtime/node_modules/@copilotkit/shared` missing a
`./telemetry` export, which is the symlinked-worktree dist drift above
and cannot be caused by two files in `react-core/src/v1-deprecated`. The
`lint-fix` hook step did pass. Items 1-5 are what I ran in its place.
Worth a second look from CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


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

## Summary by CodeRabbit

- **Bug Fixes**
- Improved the timing of Copilot context registration so context is
available earlier during page transitions and component initialization.
- Resolved an issue where earlier-mounted components could observe
missing readable context.

- **Tests**
- Added coverage validating that readable context is published before
sibling effects run.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 23:45:22 -05:00
Ben Taylor 8a16d251da fix(vue): align human-in-the-loop lifecycle with React (#5965)
## Problem

Vue v2 `useHumanInTheLoop` had drifted from the React v2 lifecycle
contract in a few connected areas:

- The handler ignored its `AbortSignal`. A stopped run could leave the
interaction promise pending instead of producing the explicit abort
error expected by the tool execution flow.
- Render props did not consistently expose `toolCallId` and the static
registration `agentId`, making it harder for renderers to identify the
exact invocation and registration scope.
- Status routing used permissive string checks. That made `respond`
semantics less explicit and allowed a future status to fall through
without a compile-time failure.
- Scoped renderer disposal was not protected by a test capable of
detecting name-only cleanup.

Together, these differences meant Vue could hang on abort, expose a
weaker renderer contract than React, or silently drift again as
tool-call statuses evolve.

## Fix

Align the Vue v2 hook with the current React v2 behavior:

- Reject already-aborted and in-flight interactions with
`Error("Human-in-the-loop interaction aborted")`.
- Register a one-shot abort listener, clear pending resolver references
when settled, and remove the listener before `respond` resolves. A late
abort therefore cannot settle the interaction twice.
- Supply the complete render contract in every status: registration name
and description, `toolCallId`, static `agentId`, args, and result.
`respond` is available only while executing.
- Route statuses through `ToolCallStatus` with a `never` exhaustiveness
check so new statuses require an intentional implementation.
- Preserve exact `{ name, agentId }` renderer cleanup on Vue scope
disposal while intentionally leaving pending interactions unsettled on
unmount, matching React reconnect/remount behavior.

The framework-specific adaptation is limited to Vue refs, rendering, and
scope-disposal mechanics; the lifecycle and response semantics match
React.

## Verification

Added regression coverage that exercises the behavior rather than
restating the implementation:

- Already-aborted and live-abort paths assert the exact error, one-shot
listener behavior, reference cleanup, cleanup-before-resolve, and no
double settlement.
- A full status matrix asserts the complete React render-prop contract
and executing-only `respond`.
- Scoped disposal registers two same-name renderers under different
agents, disposes one scope, and proves only the exact scoped renderer is
removed through the real core registration path.
- Unmount coverage proves a pending interaction remains unsettled for
reconnect/remount.
- End-to-end chat coverage proves run abort produces an error tool
result and scoped/unscoped attribution reaches the renderer.
2026-09-08 23:42:55 -05:00
Benjamin Taylor 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 f9b306aa4e.

Adds a regression test that mounts the consumer FIRST -- mounting it
second passes with either hook and proves nothing. The test fails on the
unmodified hook with "expected [] to include 'employees'".

Completes the half of mxmzb's #4259 that f9b306aa4e did not cover.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 22:54:51 -05:00
Mike Ryan b17e238aa3 docs: show the component tool imports for the built-in agent (#6959)
The Components as Tools page shows a `useComponent` call without its
import. An onboarding run using the built-in agent had to inspect
installed type declarations to find it (friction report R16).

Show the `@copilotkit/react-core/v2` hook import and Zod import directly
on the shared page, including the built-in agent route.

Validation:
- `npm run typecheck` and `npm run build` passed in
`showcase/shell-docs`.
- `npm run test -- src/lib/__tests__/docs-render.test.ts
src/lib/__tests__/setup-concept.test.ts --maxWorkers=1` — 39 tests
passed.
- Browser smoke against the production build verified the visible
imports at `/built-in-agent/generative-ui/tool-based` and the same
import in its `.md` response.
- `pnpm exec oxlint showcase/shell-docs` — zero errors, existing
warnings remain; commit hooks and `git diff --check` passed.
- The broad docs test suite hit unrelated navigation/search UI failures
and excessive worker memory use; it was stopped. No application behavior
changed in this PR.


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

## Summary by CodeRabbit

* **Documentation**
* Added guidance for importing `useComponent` and Zod when registering
tools for tool-based Generative UI.
* Clarified that the same approach applies to the built-in agent, which
does not require backend tool registration.
  * Included a TypeScript example showing the required imports.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 20:22:15 -07:00
Mike Ryan 547329fe09 fix(runtime): accept nullable frontend tool schemas (#6958)
A nullable frontend tool field can reach the built-in agent as `anyOf:
[{type: "string"}, {type: "null"}]`. The converter handles the union but
throws `Invalid JSON schema` for its null branch before the model is
called. This matches R14 in the September 3–8 onboarding friction audit.

Accept explicit null branches when converting frontend tools. Required
nullable fields still require a value; optional fields can be omitted.
Invalid non-null values still fail validation.

Validation:
- RED: both the explicit anyOf input and a real Zod v4 nullable schema
failed with `Invalid JSON schema` before the fix.
- `pnpm nx test @copilotkit/runtime` — 2,293 tests passed, including
HTTP runtime integration tests.
- `pnpm nx test @copilotkit/runtime --
src/agent/__tests__/nullable-tools.test.ts` — 3 focused tests passed
after the final test typing change.
- `pnpm nx run-many -t test,check-types,build -p @copilotkit/runtime`
passed on the revised head (2,293 tests).
- `pnpm exec oxlint packages/runtime/src/agent/index.ts
packages/runtime/src/agent/__tests__/nullable-tools.test.ts` — no
errors; three existing shadowing warnings.
- `pnpm exec oxfmt --check packages/runtime/src/agent/index.ts
packages/runtime/src/agent/__tests__/nullable-tools.test.ts` and `git
diff --check` passed.

No live model request was needed: the regression exercises the actual
AG-UI-to-model-tool conversion and validates accepted and rejected
arguments.


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

## Summary by CodeRabbit

- **Bug Fixes**
- Improved handling of nullable tool fields, including nullable unions,
arrays, and fields generated by Zod.
- Invalid values and missing required fields continue to be rejected
during tool schema validation.
- **Compatibility**
- JSON Schema type declarations now use a single type value; arrays of
schema types are no longer converted automatically.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 20:21:50 -07:00
Ben Taylor fb69a3d403 fix(docs): stop the internal v1 deprecation banner leaking into reference pages (#6966)
Fixes #6939

## Problem

The v1 deprecation notice added in #6582 is a source-file banner aimed
at IDEs and coding agents:

```
V1 SDK DEPRECATED. USE V2 INSTEAD
...
AI CODING AGENTS: Never copy, suggest, or generate these v1 APIs.
```

It sits in the leading trivia of the first statement of every public v1
source file, which is exactly where `Comments.getFirstCommentBlock`
reads real JSDoc from. So running the generator embedded it as visible
body text on 20 published reference pages, and regeneration became
unpublishable: no JSDoc correction to a v1 source could land without
also shipping the banner to the docs site.

## Fix

**1. Strip the notice (`scripts/docs/lib/comments.ts`).** Skip it
wherever the generator enumerates comment ranges —
`getCleanedCommentsForNode`, `getDefaultValueForNode`, and
`getTsDocCommentsForFunction` — keyed off its stable opening delimiter.
Filtering at the range level, rather than in one call site, means the
notice cannot reach a page through the property or function-doc paths
either.

**2. Repoint the six SDK entries (`scripts/docs/lib/files.ts`).** The
issue asked whether `reference/sdk/` should be committed or gitignored.
It is neither: those pages moved to `reference/v1/sdk/` in ec239b15f7,
the old copies were deleted in a8d43a9c2e, and `files.ts` was never
updated. The generator has been refreshing a directory the docs site
never served while the live, nav-linked pages went stale.

Regenerating them recovers real content that was wrong in production:

- `RemoteEndpoints` code samples now import `LangGraphAGUIAgent`, not
the Python `LangGraphAgent` that was renamed upstream and no longer
exists.
- `LangGraph` documents the `tool_call_id` parameter and the corrected
`str` return of `copilotkit_emit_tool_call` (the page still claimed
`Awaitable[bool]`, "Always return True").

The renamed page replaces the orphaned `LangGraphAgent.mdx`, whose
source `sdk-python/copilotkit/langgraph_agent.py` no longer exists, with
a permanent redirect for the old URL. This only touches the **Python**
class; the JavaScript `LangGraphAgent` from
`@copilotkit/runtime/langgraph` is unrelated and untouched.

**3. Regenerate.** 11 v1 pages pick up genuine JSDoc drift that had
accumulated behind the block.

## Testing

**The fix changes only banner text — nothing else.** Generated the full
tree with and without the `comments.ts` change and diffed the two
outputs:

```
added lines introduced by the fix: 0
files differing:       20
```

Every difference is a removal, and every removed line belongs to the
notice. Zero added lines means no page content was altered, reworded, or
dropped as a side effect.

**The banner is gone from the published output:**

```
$ grep -rn "V1 SDK DEPRECATED" showcase/shell-docs/src/content/reference/
(no matches)
```

Diff shrank from the 20 files / 621 insertions the issue reported to 11
files / 157 insertions, all of it real JSDoc drift.

**Regeneration is now idempotent** — the acceptance criterion for
"regeneration is unblocked":

```
$ git add -A && ./node_modules/.bin/tsx scripts/docs/gen.ts
All reference docs processed (26/26 succeeded)
$ git status --porcelain   # nothing unstaged
$
```

The stray untracked `reference/sdk/` tree from the issue's second
finding no longer appears.

**New test — `scripts/__tests__/docs-reference-generator.test.ts`:**

```
$ pnpm exec vitest run scripts/__tests__/docs-reference-generator.test.ts
 Test Files  1 passed (1)
      Tests  31 passed (31)
```

Wired into `static_quality.yml` next to the existing declaration-file
validator step.

**Both guards were mutation-checked** — a test that cannot fail proves
nothing:

| Mutation | Result |
|---|---|
| Revert the `comments.ts` strip | `Tests 2 failed \| 29 passed` — both
notice-stripping assertions red |
| Revert the `files.ts` destinations | `Tests 6 failed \| 25 passed` —
all six SDK destinations red |
| Revert `files.ts` **and** recreate `reference/sdk/` with generated
files | `Tests 6 failed \| 25 passed` — still red |

The third row is why the destination test asserts the parent directory
holds a **git-tracked** file rather than merely existing: a stray
generation recreates the directory, and an existence check would have
passed while the misroute was live.

**No broken links from the removed page:**

```
$ grep -rn "sdk/python/LangGraphAgent" showcase/ --include=*.mdx --include=*.json --include=*.ts
showcase/shell-docs/next.config.ts:832:        source: "/reference/v1/sdk/python/LangGraphAgent",
```

Only the redirect itself.

**Lint and format:** `oxfmt --check` clean. `oxlint scripts/docs`
reports 4 warnings both before and after the change — pre-existing, none
added. Pre-commit hooks (lint-fix, check-binaries, commitlint,
intelligence-env-names) all passed.

## Not in this PR

The generated pages can still drift silently, because nothing fails CI
when a v1 JSDoc edit lands without a regeneration. A "generated docs are
up to date" job would close that, but it reddens any PR that touches a
v1 doc comment, so it is a policy call worth making on its own rather
than smuggling into a bug fix. Happy to follow up if you want it.

## Follow-up commit (self-review)

A review pass over the first commit found three things it got wrong or
newly exposed. All are fixed in `274983d`:

**My redirect was incomplete.** It covered only the bare path.
`next.config.ts` notes that a raw Markdown request reaches redirects
*before* the `.md`/`.mdx` rewrite, and the rest of the table uses
`permanentRedirectsWithSuffixes` for exactly that reason. Switched to
the helper, verified by evaluating the real config:

```
/reference/v1/sdk/python/LangGraphAgent     -> .../LangGraphAGUIAgent
/reference/v1/sdk/python/LangGraphAgent.md  -> .../LangGraphAGUIAgent.md
/reference/v1/sdk/python/LangGraphAgent.mdx -> .../LangGraphAGUIAgent.mdx
```

First-match resolution confirms nothing earlier in the 1008-entry table
shadows them, and the new canonical URL is not itself redirected (no
loop).

**Regenerating republishes JSDoc links, and three were dead.** Frozen
pages hid them; refreshing makes them live 404s. Audited every internal
link the regeneration newly publishes and fixed each at the source:

| Link | Status | Fix |
|---|---|---|
| `/coagents/videos/perplexity-clone` | no content, no redirect, no
rewrite | `/generative-ui/state-rendering` — the guide the published
page already named |
| `/coagents/shared/guides/langgraph-platform-authentication` | does not
exist | `/auth` — how 8+ other pages link to it |
| `/reference/v2/hooks/useCopilotChatHeadless_c` | no such v2 page;
`reference/hooks` has no headless entry | restored the v1 page, which
exists |

The third came from the URL canonicalization in 33f669ba7b, which
flipped `v1` to `v2` in the path without a v2 page existing. I restored
the working link rather than resolve the intent — **if v1 readers should
be pushed to a v2 headless page, that page needs to be written first.**
Worth a second opinion from its author.

Final audit — every newly published internal link resolves:

```
OK   /auth                          -> content/docs/auth.mdx
OK   /generative-ui/state-rendering -> content/docs/generative-ui/state-rendering.mdx
OK   /intelligence/overview         -> content/docs/intelligence/overview.mdx
OK   /migrate/v2                    -> content/docs/migrate/v2.mdx
```

The three package edits are JSDoc comment lines only — no code changed,
and nothing in the repo asserts on those strings. The pre-commit package
matrix was skipped for that commit (it exits 130 in a worktree with
symlinked `node_modules`); CI runs the full unit matrix instead.

🤖 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 v1 SDK reference paths and renamed LangGraphAgent
documentation to LangGraphAGUIAgent, including redirects and navigation
updates.
- Clarified CopilotRuntime options, adapter client types, chat feedback
callbacks, attachment handling, and tool-call identifiers.
- Removed outdated public API key requirements and documented current
setup guidance.
- Added guidance for custom serialization, conditional usage, and
rerunning readable state.
- Marked legacy options and removed obsolete hierarchical-state and
LangGraphAgent reference content.
- **Bug Fixes**
- Improved generated reference docs by excluding deprecation notices and
correcting documentation destinations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 22:18:51 -05:00
Benjamin Taylor 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
  33f669ba7b, but there is no v2 page of that name — reference/hooks has no
  headless entry. Restore the v1 page, which exists and is what the published
  page links to today. If v1 readers should be pushed to a v2 page instead,
  that page has to be written first.

Every internal link the regeneration newly publishes now resolves to a content
file.

The three package edits are JSDoc comment lines only; no code changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 21:58:48 -05:00
Benjamin Taylor c11329c5dd fix(docs): stop the internal v1 deprecation banner leaking into reference pages
The v1 deprecation notice added in #6582 is a source-file banner for IDEs and
coding agents, including the line "AI CODING AGENTS: Never copy, suggest, or
generate these v1 APIs." It sits in the leading trivia of the first statement
of every public v1 source file, which is the same place the reference-docs
generator reads real JSDoc from, so regenerating embedded it as visible body
text on 20 published pages. That made regeneration unpublishable: no JSDoc
correction to a v1 source could land without also shipping the banner.

Skip the notice wherever the generator enumerates comment ranges, keyed off its
stable opening delimiter.

Also repoint the six SDK reference entries. Their pages moved to
reference/v1/sdk/ in ec239b15f7 and the old copies were deleted in a8d43a9c2e,
but files.ts still wrote to reference/sdk/, so the generator refreshed a
directory the docs site never served while the live pages went stale. Those
pages regain the upstream LangGraphAgent -> LangGraphAGUIAgent rename and the
copilotkit_emit_tool_call tool_call_id parameter. The renamed page replaces the
orphaned LangGraphAgent page, whose source file no longer exists, with a
permanent redirect for the old URL.

Regenerating is now idempotent: a second run leaves the tree clean.

Fixes #6939

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 21:44:18 -05:00
Ben Taylor f7192ee40c docs: persist the reader's code-tab choice, and link out to provider key pages (#6925)
Two independent docs-frontend improvements in `showcase/shell-docs`.
Rebased onto current `main` (the branch was 158 commits behind).

## 1. `<Tabs persist>` now actually persists

101 tab groups in the content tree are authored as `<Tabs groupId="..."
persist>`. The wrapper accepted both props and ignored them — the
comment in `docs-tabs.tsx` said so outright:

> `groupId` and `persist` are accepted and currently ignored

So every page reopened on its own default. A reader working through the
LangGraph guide in TypeScript had to reselect TypeScript on each page.

Fumadocs holds tab selection in local component state and exposes no
persistence hook, so the wrapper takes over the controlled
`value`/`onValueChange` pair and mirrors the pick into `localStorage`
under `shell-docs.tab.<groupId>`.

**Selection precedence**, strongest first:

| # | Source | Where it comes from |
|---|--------|---------------------|
| 1 | `urlDefault` | The framework-route override the page shell derives
from the URL (`TAB_DEFAULTS_BY_SLUG`) |
| 2 | Stored pick | `localStorage`, same `groupId` |
| 3 | `default=` | The author's value in the MDX |
| 4 | First item | Fallback |

The page shell passes its URL-derived value as a **separate `urlDefault`
prop** rather than overwriting `default`. This matters: 45 of the 101
`persist` groups also carry an author `default=`. Collapsing the two
sources into one prop ranks the author's default above storage, which
would leave the stored pick unreachable on almost half of the pages the
feature exists for. `language_langgraph_agent` appears both ways (30
sites with `default="Python"`, 8 without), so the same group would have
behaved inconsistently within one guide.

Two details worth noting for review:

- The stored value is read in an **effect**, not in the initial state,
so server and client render identical markup and hydration stays clean.
- Every `localStorage` read and write is wrapped in `try`/`catch`.
Private mode and quota errors leave the tabs fully working, just without
persistence.

## 2. API-key hints under `.env` snippets

The LangGraph quickstart tells the reader to put `OPENAI_API_KEY` in
`.env` and leaves them to go find the key page. `<ApiKeyHint
provider="openai" />` renders a muted one-line link under the snippet.

The component maps a provider id to a label and URL — `openai`,
`anthropic`, `google`, `langsmith`, `copilotkit`. An unknown id renders
nothing, so a typo degrades to today's behaviour instead of throwing. It
is navigational only: it neither reads nor writes a key. Both `.env`
steps on the LangGraph quickstart use it.

## Removed from this branch

The earlier revision led the LangGraph quickstart with a `<InlineDemo
demo="agentic-chat" />` block under a "See it working" heading. That is
gone, along with the `inline-demo.test.tsx` file that covered it —
`InlineDemo` is pre-existing `main` code this PR no longer touches.

Two tests in `docs-page-view-toc.test.tsx` were also dropped rather than
kept. They were named for this PR's components but did not exercise
them: `docs-page-view-toc.test.tsx` asserts on `DocsPage` props, and the
page body is never rendered. Verified by mutation — deleting
`ApiKeyHint` from the MDX registry left the test titled `renders the
LangGraph quickstart (InlineDemo + ApiKeyHint) without errors`
**passing**. Real coverage lives in `api-key-hint.test.tsx` instead.

## Testing

No CI job runs the `showcase/shell-docs` vitest suite.
`test_unit-showcase.yml` covers only `harness` and `shell-dashboard`;
`showcase_validate.yml` runs vitest only in `showcase/scripts`.
Everything below was therefore run locally.

**Full suite, branch vs. pristine `origin/main` in the same
environment** — `main` carries 6 pre-existing failures here, so the
failure *set* is the comparison, not zero:

```
base   (origin/main)  843 tests, 6 failed
branch (this PR)      858 tests, 6 failed
NEW failures: none
```

The 6 are identical on both sides: `brand-nav` layout cap, 3 ×
`angular-docs-content`, `llm-text` mastra, `ms-agent-python-stable-api`.

**Mutation checks** — every new test was verified to fail when the
mechanism it claims to cover is broken:

| Mutation | Result |
|----------|--------|
| `canPersist = false` (persistence off) |  `persists a groupId pick
and reapplies it on a fresh mount` fails |
| Author `default` outranks storage (the pre-fix precedence) |  `ranks
a stored pick above the author's MDX default` fails |
| `urlDefault` demoted below author `default` |  `ranks a urlDefault
above the author's MDX default` fails |
| `ApiKeyHint` removed from the MDX registry |  `is registered as an
MDX component` fails |
| `href={meta.url}` → `href={undefined}` |  5 of 7 `ApiKeyHint` tests
fail |

**End-to-end render** — `ApiKeyHint` was rendered through the real
`MDXRemote` pipeline (same `remarkGfm` options, nested in
`<Steps>/<Step>` as the quickstart uses it) to confirm the `provider`
prop survives compilation and the anchor reaches the HTML:

```
✓ ApiKeyHint through the real MDX pipeline > survives compilation with its provider prop
  expect(html).toContain("https://platform.openai.com/api-keys")
```

**Typecheck and lint** (`showcase/shell-docs`):

```
$ npx tsc --noEmit      → exit 0
$ npx oxlint .          → Found 28 warnings and 0 errors
$ npx oxfmt --check <touched files>  → All matched files use the correct format
```

The 28 lint warnings are pre-existing. The only two in a file this PR
touches (`mdx-registry.tsx`) are `iframe-missing-sandbox` on
pre-existing `InlineDemo` iframes, untouched here.

🤖 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 inline API key guidance below relevant documentation code
blocks, with links to provider credential pages.
* Added tab selection persistence across documentation pages, with
support for URL and author-defined defaults.
  * Added API key guidance to the LangGraph quickstart.
* **Tests**
* Added coverage for tab persistence, selection precedence, invalid
values, disabled persistence, and API key hint behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 21:12:51 -05:00
Ben Taylor b947d507a4 fix(shell-docs): render the CTACards a page actually authors (#6954)
## Problem

`CTACards` in `mdx-registry.tsx` accepted only `children`:

```tsx
CTACards: ({ children }: { children?: React.ReactNode }) => (
  <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", ... }}>
    {children}
  </div>
),
```

Every call site in content authors it self-closing with props instead:

```mdx
<CTACards
  columns={2}
  cards={[
    { iconKey: "circlePause", title: "Interrupt-based", description: "...", href: "..." },
    { iconKey: "share2", title: "Tool-based", description: "...", href: "..." },
  ]}
/>
```

The grid rendered empty and the pages lost their links. Nothing errored.

Four files author a `<CTACards>` block. Three of them are live pages:

- `docs/integrations/crewai-flows/human-in-the-loop/index.mdx` →
`/crewai-crews/human-in-the-loop`
- `docs/integrations/mastra/human-in-the-loop/index.mdx` →
`/mastra/human-in-the-loop`
-
`docs/integrations/microsoft-agent-framework/human-in-the-loop/index.mdx`
→ `/ms-agent-python/human-in-the-loop` and
`/ms-agent-dotnet/human-in-the-loop`

The fourth, `docs/integrations/pydantic-ai/human-in-the-loop/index.mdx`,
renders nowhere: the sibling leaf file
`integrations/pydantic-ai/human-in-the-loop.mdx` shadows the directory
index, and the leaf is what `/pydantic-ai/human-in-the-loop` serves. It
is edited here for consistency, but which of the two bodies should win
is a separate content decision. pydantic-ai is the only one of the four
with that duplicate.

This was pre-existing on the first three. It surfaced while reviewing
#6922, which added the fourth.

## The part the first pass missed

Rendering from `cards` is necessary but not sufficient. Measured on the
docs dev server, the component received **zero props** on the real page:

```
SENTINEL-KEYS[]TYPES[]                      # <CTACards columns={2} cards={[...]} />
SENTINEL-KEYS[foo]TYPES[foo:string]         # <CTACards foo="bar" num={7} />
```

String attributes survive. Expression attributes do not.
`next-mdx-remote@6.0.0` defaults `blockJS` to `true`, which adds
`removeJavaScriptExpressions` (`dist/serialize.js:19`) — a remark plugin
that deletes every JSX attribute whose value is an expression and every
`{expression}` node. `shell-docs` never set `blockJS`.

The original unit tests passed because they call the component directly
and skip the MDX pipeline entirely.

## Fix

**Render from `cards`.** Through the shared `Card`, with `description`
passed as the `description` prop so it lands in the styled `<p>` that
every other docs card grid uses. `iconKey` resolves against a `ctaIcons`
lookup of lucide names, matched on own properties only, and an
unregistered key renders the card without an icon. When `cards` is
absent, fall back to wrapping `children`, the same prop-or-children
contract `EcosystemTable` uses.

**Turn `blockJS` off for the docs route.** Every MDX source there is
first-party content from `src/content`, so the sandbox only dropped
authored props. `blockDangerousJS` keeps its default. Scoped to this one
call site on purpose: the other five keep the default, and
`ag-ui/introduction.mdx` in particular depends on the stripping, because
it authors 26 inline `onMouseEnter`/`onMouseLeave` handlers that would
otherwise reach a server component. The remaining call sites are worth a
follow-up, not a drive-by flip.

**Resolve each card href against the framework being read.** The cards
render through the `Card` imported by the registry, so they never
reached the href-resolving `Card` override in the page's component map.
On `/ms-agent-python/human-in-the-loop` the authored
`/microsoft-agent-framework/...` href 301-redirected to
`/ms-agent-dotnet/...`, moving a Python reader to the .NET page. Content
now authors these hrefs root-relative, and a new `resolveCtaCardHrefs`
helper prefixes them per page.

**Stack to one column below `sm`.** An inline `grid-template-columns`
cannot be overridden by a class, so once the cards actually rendered,
two of them stayed 157px wide side by side on a 390px viewport. The
wrapper now uses the same grid classes and `@container` as the shared
`<Cards>`.

## Testing

### Unit

`vitest run src/lib/__tests__/cta-cards.test.tsx`

```
 Test Files  1 passed (1)
      Tests  16 passed (16)
```

**Mutation-checked.** Each mutation was applied alone and reverted:

| Mutation | Result |
| --- | --- |
| Component back to the `({ children })` stub | 11 failed, 4 passed |
| `blockJS: false` removed from the pipeline test | 5 failed, 10 passed
|
| `blockJS: false` removed from the page | 1 failed, 15 passed |
| `CTACards` override removed from the page | 1 failed, 15 passed |
| `columns` ignored by the grid | 2 failed, 14 passed |
| `description` passed as children again | 1 failed, 14 passed |
| Inline `gridTemplateColumns` restored | 3 failed, 12 passed |
| `resolveCtaCardHrefs` made a no-op | 1 failed, 14 passed |
| `Object.hasOwn` back to `in` | 1 failed, 14 passed |

Two kinds of test carry the weight the first pass was missing. The
pipeline tests compile the four authored `<CTACards>` blocks through
`compileMDX` with the page's options, and fail the moment `blockJS`
returns to its default. The wiring test walks the rendered
`DocsPageView` tree, checks `blockJS` in the page's own MDXRemote
options, and renders the registered `CTACards` override to confirm it
prefixes hrefs; removing either the option or the override fails it.

### Live

Docs dev server on the branch head. Card destinations and grid classes
read out of the served HTML:

```
/ms-agent-python/human-in-the-loop   200  grid-cols-1 sm:grid-cols-2
   /ms-agent-python/human-in-the-loop/interrupt-flow  /ms-agent-python/human-in-the-loop/tool-based
/ms-agent-dotnet/human-in-the-loop   200  grid-cols-1 sm:grid-cols-2
   /ms-agent-dotnet/human-in-the-loop/interrupt-flow  /ms-agent-dotnet/human-in-the-loop/tool-based
/mastra/human-in-the-loop            200  grid-cols-1 sm:grid-cols-2
   /mastra/human-in-the-loop/tool-based  /mastra/human-in-the-loop/interrupt-flow
/crewai-crews/human-in-the-loop      200  grid-cols-1
   /crewai-crews/human-in-the-loop/flow
```

All seven card destinations return 200 directly, with no redirect hop.

Layout, measured in the browser:

```
1440px viewport:  gridTemplateColumns "360px 360px", cards at x=360 and x=736   (identical to a stock <Cards> grid on /mastra/quickstart)
 390px viewport:  gridTemplateColumns "326px", cards stacked at y=1167 and y=1374
 columns={1}:     gridTemplateColumns "736px", one full-width card
```

Before this change, the same 390px measurement was two 157px cards side
by side.

### Regression sweep for `blockJS: false`

Every docs route, one per content file under `src/content/docs`, crawled
against the running dev server following redirects:

```
675 routes: 673 x 200, 2 x 404
```

The two 404s are `/frontends/react-spa` and `/frontends/docs-status`.
Both are 404 with this change and 404 without it.

The full crawl replaced a narrower first pass over the 424 routes whose
own file carries an expression. That sample missed pages that inline a
snippet through `inlineSnippets`, where the snippet body carries the
expression and the page does not. Those pages are covered here.

### Full suite

`vitest run`, same worktree, with and without this diff:

```
before:  Test Files  16 failed | 92 passed (108)    Tests  53 failed | 795 passed (848)
after:   Test Files  16 failed | 92 passed (108)    Tests  53 failed | 805 passed (858)
```

The failing set is byte-identical between the two runs (50 named
failures, `diff` clean). Those failures come from stale generated
bundles in `src/data` in this sandbox, not from the diff. The delta is
the tests added here.

### Typecheck, format, lint

`tsc --noEmit`: **0 errors**.

This corrects the earlier revision of this description, which reported a
21-error baseline. Those 21 were missing-dependency noise from a stale
local install (17 x `Cannot find module '@testing-library/react'`, 2 x
`@clerk/nextjs`, 2 unrelated implicit-any). With dependencies installed
the tree is clean before and after.

`oxfmt --check`: all four touched files correctly formatted.
`oxlint`: 2 warnings in `mdx-registry.tsx`, both on pre-existing `iframe
sandbox` attributes at lines 427 and 696. The same two appear on the
file at `HEAD` in the same run.

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

* **New Features**
  * Added configurable call-to-action card grids to documentation pages.
* Cards support titles, descriptions, links, icons, and adjustable
column layouts.
* Existing child-based card content remains supported when no card data
is provided.
* Unrecognized or invalid icon names are handled safely without
disrupting page rendering.

* **Bug Fixes**
* Fixed CTA card links so they resolve correctly on framework-specific
documentation pages.
* Fixed card data and layout settings not rendering in certain authored
documentation pages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 21:01:58 -05:00
Benjamin Taylor 0868314acd test(shell-docs): cover the CTACards page wiring
A self-review of the previous commit found three gaps.

- Nothing tested that the page wires the fixes up. A new test walks the
  rendered `DocsPageView` tree, asserts `blockJS` is off in the page's
  own MDXRemote options, and renders the registered `CTACards` override
  to confirm it prefixes card hrefs with the framework being read.
  Removing either the option or the override fails it.
- The compile test asserted `grid-cols-1`, which the two-column class
  also contains, so the `columns` half of that test proved nothing. It
  now asserts the absence of the `sm:` variant.
- The per-page test guarded the extracted href count but not the title
  count, so a regex that matched no titles would have passed silently.

Also correct the component comment: four content files author the
block, but the pydantic-ai one is shadowed by a sibling leaf file and
renders nowhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 20:41:35 -05:00
Benjamin Taylor d083d8a7a4 fix(shell-docs): let the authored CTACards props reach the component
The component fix alone was inert on the real page. next-mdx-remote 6
defaults `blockJS` to true, which runs a remark plugin that deletes
every JSX attribute whose value is an expression. On the docs route
`<CTACards columns={2} cards={[...]} />` reached the component with no
props at all, so it still rendered an empty grid. The unit tests passed
because they call the component directly and skip the MDX pipeline.

- Turn `blockJS` off for the docs route. Every source there is
  first-party content from `src/content`. `blockDangerousJS` keeps its
  default. The other MDXRemote call sites keep the default too, because
  `ag-ui/introduction.mdx` authors inline `onMouseEnter` handlers that
  the stripping currently keeps out of a server component.
- Resolve each card href against the framework being read. The cards
  render through the registry `Card`, so they never reached the
  href-resolving `Card` override, and a reader on
  `/ms-agent-python/human-in-the-loop` was redirected to the .NET page.
  Content now authors the hrefs root-relative.
- Stack the grid to one column below the `sm` breakpoint. An inline
  `grid-template-columns` cannot be overridden by a class, so the two
  cards stayed 157px wide side by side on a 390px viewport.
- Pass each description through the `Card` `description` prop, the same
  as every other card grid in the docs.
- Match `iconKey` against own properties only.
- Add a test that compiles the four authored blocks through the MDX
  pipeline, which is the check the earlier tests were missing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 20:41:35 -05:00
Benjamin Taylor 22cc2bf303 fix(shell-docs): render the CTACards a page actually authors
`CTACards` accepted only `children`, but every call site in content
authors `<CTACards columns={n} cards={[...]} />` self-closing. Both
props were dropped, so the grid rendered empty and the pages lost
their links with no error anywhere.

Four human-in-the-loop landing pages are affected: crewai-flows,
mastra, pydantic-ai, and microsoft-agent-framework.

The component now renders each entry through the shared `Card`, honors
`columns` in the grid template, and falls back to wrapping `children`
so legacy `<CTACards>...</CTACards>` authoring keeps working — the same
prop-or-children contract `EcosystemTable` uses in this file.

`iconKey` values on these cards are lucide names, not the framework
keys in `customIcons`, so they get their own lookup. An unregistered
key renders the card without an icon rather than throwing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 20:41:34 -05:00
Benjamin Taylor a04f0f7640 feat(docs): link out to the provider key page from .env snippets
The LangGraph quickstart tells the reader to put `OPENAI_API_KEY` in
`.env` and leaves them to find the key page themselves. `<ApiKeyHint
provider="openai" />` renders a muted one-line link under the snippet.

The component maps a provider id to a label and a URL, covering openai,
anthropic, google, langsmith and copilotkit. An unknown id renders
nothing, so a typo degrades to the current behaviour instead of
throwing. It is navigational only: it neither reads nor writes a key.

Both `.env` steps on the LangGraph quickstart use it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 20:41:24 -05:00
Benjamin Taylor 9157f27f9b feat(docs): persist the reader's code-tab choice across pages
`<Tabs groupId="..." persist>` is written on 101 tab groups in the docs
content tree, but the wrapper accepted both props and ignored them: the
comment in docs-tabs.tsx said so outright. Every page therefore reopened
on its own default, so a reader working through the LangGraph guide in
TypeScript had to reselect TypeScript on each page.

Fumadocs holds tab selection in local component state and exposes no
persistence hook, so the wrapper takes over the controlled
`value`/`onValueChange` pair and mirrors the pick into localStorage
under `shell-docs.tab.<groupId>`.

Selection precedence, strongest first:

  1. `urlDefault` — the framework-route override the docs page shell
     derives from the URL via TAB_DEFAULTS_BY_SLUG.
  2. A stored pick for the same `groupId`.
  3. The author's `default=` written in the MDX.
  4. The first item.

The page shell now passes its URL-derived value as a separate
`urlDefault` prop instead of overwriting `default`. Collapsing the two
into one prop would rank the author's default above storage, and 45 of
the 101 `persist` groups carry an author `default=` — the stored pick
would have been unreachable on almost half the pages the feature exists
for.

The stored value is read in an effect rather than in the initial state,
so server and client render the same markup. Reads and writes are
wrapped in try/catch: private-mode and quota errors leave the tabs
working without persistence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 20:41:24 -05:00
Ben Taylor 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` (31d0cda168) | 146 passed | 1639 | 0 |
| This branch | 146 passed | **1646** | 0 |

Exactly +7. Comparing the two runs' JSON reports file by file,
`CopilotKitProvider.test.tsx` (39 → 46) is the only file whose count
moved.

### The 7 new tests

```
✓ CopilotKitProvider > agentId > becomes the default agent for a chat that does not name one
✓ CopilotKitProvider > agentId > lets a nested chat configuration override it
✓ CopilotKitProvider > agentId > leaves the global default in place when the prop is omitted
✓ CopilotKitProvider > agentId > publishes no chat configuration of its own
✓ CopilotKitProvider > agentId > follows a changed agentId
✓ CopilotKitProvider > agentId > thread isolation > gives sibling chats their own thread when agentId is set
✓ CopilotKitProvider > agentId > thread isolation > matches the no-agentId tree
```

### Mutation checks

| Mutation | Result |
| -- | -- |
| drop the `providerAgentId` fallback in
`CopilotChatConfigurationProvider` | `becomes the default agent for a
chat that does not name one` and `follows a changed agentId` fail (2
failed / 44 passed) |
| publish the default through a root `CopilotChatConfigurationProvider`
instead of the bare context | `publishes no chat configuration of its
own` and both `thread isolation` tests fail (3 failed / 43 passed) |

The second mutation is the original branch's implementation, so the
thread-isolation tests fail against the code they were written to catch.

### Build, typecheck, lint, format, MDX

- `nx build @copilotkit/react-core --skip-nx-cache`: succeeds.
`context-singleton-preflight: OK — src/v2/context.ts bundled only into 4
allowed target(s)` — the new `CopilotChatConfigurationProvider` →
`../context` import does not add a bundle target.
- `tsc --noEmit -p packages/react-core/tsconfig.json`: 0 errors.
- `oxlint` on the 7 changed source files: 0 errors, and the same 9
warnings before and after (measured by checking `main`'s copies into the
same tree).
- Formatted with `oxfmt`.
- Both changed `.mdx` files compile through `@mdx-js/mdx` with
`remark-gfm`, and all 9 string assertions in `docs-render.test.ts`
against `runtime-endpoints.mdx` still hold.
- `nx test @copilotkit/react-native --skip-nx-cache`: 26 passed, 0
failed (it consumes `react-core`).

## Unrelated CI fix carried here

`doc-tests` went red on this branch for a reason that has nothing to do
with it. `@ag-ui/mastra@1.1.3` was published on 2026-09-08 at 23:19:48Z
and raised its peer range for `@ag-ui/client`/`@ag-ui/core` to
`>=0.0.58`. The Mastra doctest pins both at `0.0.57` and left the
adapter floating, so npm takes 1.1.3 and the install fails with
`ERESOLVE`. Every other open PR's `doc-tests` run that passed completed
before that timestamp — the latest at 23:13:15Z — and this branch's run
at 23:45:20Z is the first one after it, so the break lands on every PR
from here.

The second commit pins the adapter at `1.1.2`, whose peer range
(`>=0.0.44`) the existing pins already satisfy. Bumping the client and
core instead does not work: `@copilotkit/runtime@1.68.3` hard-depends on
`@ag-ui/client@0.0.57`, so raising the snippet to `0.0.59` puts two
copies of `AbstractAgent` in the tree and the snippet fails `tsc
--noEmit` with `TS2769`. Measured locally both ways — version bump
21/22, adapter pin **22/22**.

🤖 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 provider-level default agent selection through the `agentId`
prop on `<CopilotKitProvider>`.
- Descendant chats, threads, suggestions, and agent hooks now inherit
the provider’s agent unless overridden locally.
  - Preserved separate thread identities for sibling chats.

- **Documentation**
- Updated provider migration guidance, agent naming, and automatic
endpoint detection behavior.
- Added examples for provider, subtree, and per-chat agent
configuration.

- **Chores**
  - Pinned the Mastra integration example dependency to version 1.1.2.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 20:40:39 -05:00
Tyler Slaton 902d0faea5 Point the Channels docs at an SDK pair that can render components (#6952)
## The problem

Every Channels page that tells a reader what to install named a pair
that is four minors stale:

```sh
npm install --save-exact @copilotkit/channels@0.6.1 @copilotkit/runtime@1.65.0
```

That pin is not merely old, it is load-bearing in the wrong direction.
`@copilotkit/channels@0.6.1`
does not export `defineChannelComponent`. Compiling an agent-rendered
component against the version
our own quickstart installs fails outright:

```
error TS2724: '"@copilotkit/channels"' has no exported member named
'defineChannelComponent'. Did you mean 'ChannelComponent'?
```

The export listing confirms where the boundary sits.
`@copilotkit/channels-core@0.6.1` ships
`defineChannelCommand` and `defineChannelTool` and nothing else in that
family; `defineChannelComponent`
first appears in the 0.6.2 canary line and first ships stable in 0.7.0.

So the two halves of our own documentation disagree. The Channels setup
guide gates success on "at
least one `defineChannelComponent` must render," while the reference
page and both provider
quickstarts hand the reader a version in which that success criterion
cannot be satisfied. A builder
following the docs faithfully reaches a compile error, and the error
blames their code rather than
our pin. The bad trade they make next is guessing — dropping
`--save-exact`, reaching for `@latest`,
or abandoning agent-rendered components entirely, each of which discards
the tested-pair guarantee
the pin existed to provide.

The timing is what makes this worth a same-day fix rather than a queued
one. The "Agents, Everywhere"
global hackathon runs Saturday 12 September 2026 across 51 cities with
CopilotKit as a global
sponsor, and these are precisely the pages participants will open first.

## The approach

Every pinned pair in the Channels docs moves to
`@copilotkit/channels@0.9.2` +
`@copilotkit/runtime@1.70.2`, the current published pair as of
2026-09-08. Five install lines across
the reference index, the direct-adapters reference, the
deploy-and-operate how-to, and the Slack and
Teams quickstarts.

**The pin is what changes, not the prose.** Where a page's guidance
implies agent-rendered components
are available, that guidance was already correct — it was the version
underneath it that was wrong.
Nothing about the described behaviour is edited.

**The direct-adapters availability note moves too.** It read "their
direct adapters already ship in
`@copilotkit/channels@0.6.1`," an availability claim rather than a
first-shipped-in claim, sitting
directly above an install block that now names 0.9.2. Leaving it would
make the page contradict
itself within fifteen lines.

**The SDK reference index gains the tested-pair framing it was
missing.** The quickstarts and the
deploy-and-operate page already explain that the two packages ship as a
tested pair and must be
upgraded together; the reference index pinned exact versions while
explaining nothing, which is how a
pin decays into a number nobody knows they may not touch.

**One file outside the docs content changes, and the cost is worth
naming.**
`src/lib/__tests__/channels-docs.test.ts` asserts the quickstarts
contain the exact tested install
string, so it hard-codes the pair. Updating the docs without it produces
a red PR. Only the two
version constants move; no assertion is added, removed, or loosened.

## What is not covered

- **No recording.** The change is text in six files with no runtime
surface to demonstrate. The
durable evidence is the export listing above, which is reproducible from
the registry rather than
  from this branch.
-
`showcase/shell-docs/src/content/reference/channels/functions/createChannel.mdx:114`
still reads
"Channels 0.6.1 warns when enumerable fields are dropped." This is a
behaviour-provenance note, not
an install pin, and rewriting the number would change a factual claim
about when the behaviour
changed. It needs a maintainer to say whether it means "as of 0.6.1" or
"in 0.6.1."
- `skills/setup-slack-channel/SKILL.md:81` and
`skills/setup-slack-channel/references/troubleshooting.md:8`
reference `@copilotkit/channels@0.6.0` and `@copilotkit/runtime@1.65.0`
— an even older pin, and one
the guard test explicitly calls "the broken Channels 0.6.0 release."
Same class of bug, outside docs
  content, left for a separate decision.
- The three `doctest.json` files pin `@copilotkit/runtime@1.68.3`, also
stale, also a different
  purpose.
- `CopilotKit/channels-sdk` carries the same stale install line in
  `.agents/skills/build-channels-agent/SKILL.md`. Different repository.
- The docs do not document `defineChannelComponent` anywhere, despite
the setup guide gating success
  on it. That gap is not addressed here.
- The guard test could ratchet against 0.6.1 and 1.65.0 the way it
already ratchets against 0.5.0,
0.6.0, and 1.64.2, which would stop this recurring. Deliberately not
added, to keep the diff to the
  bug.

## Verification

Registry state re-checked at edit time: `npm view @copilotkit/channels
version` → `0.9.2`,
`npm view @copilotkit/runtime version` → `1.70.2`.

Export boundary confirmed by unpacking the published tarballs:
`channels-core@0.6.1` has no
`defineChannelComponent`; `0.6.2-canary.1785779327`, `0.7.0`, `0.7.1`,
and `0.9.2` all have it.

All 10 assertions in the `channels-docs` guard test's install and
Node-version blocks were replayed
against the edited files for both provider quickstart slugs — 20 checks,
all passing. The `--save-exact`
negative lookaheads still hold: the docs contain no unpinned
`@copilotkit/channels` or
`@copilotkit/runtime` install, no `@latest` or `@next` tag, and none of
the previously blocked 0.5.0,
0.6.0, or 1.64.2 versions.

`grep` across `showcase/shell-docs/src` returns no remaining
`channels@0.6.1` or `runtime@1.65.0`.

No new test files. No source, config, or lockfile changes.


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

## Summary by CodeRabbit

* **Documentation**
* Updated channel, Slack, Teams, and direct adapter setup guides with
the latest pinned SDK versions.
* Updated the channel reference documentation to reflect the current
package versions.

* **Tests**
* Updated documentation checks to validate the newer SDK versions in
provider quickstarts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-09 03:04:42 +02:00
Benjamin Taylor edfc7c3e17 ci(doc-tests): pin @ag-ui/mastra so the quickstart snippet resolves
`@ag-ui/mastra@1.1.3` was published on 2026-09-08 and raised its peer range
for `@ag-ui/client` and `@ag-ui/core` to `>=0.0.58`. The Mastra doctest pins
both at `0.0.57` and left the adapter floating, so npm took 1.1.3 and every
doc-tests run after the publish failed with ERESOLVE. Runs that completed
before it passed; the break is not specific to any branch.

Pin the adapter at `1.1.2`, whose peer range (`>=0.0.44`) the existing pins
satisfy, rather than bumping the client and core. `@copilotkit/runtime@1.68.3`
hard-depends on `@ag-ui/client@0.0.57`, so raising the snippet to 0.0.59 puts
two copies of `AbstractAgent` in the tree and the snippet then fails
`tsc --noEmit` with TS2769 instead. Verified both ways locally: with the
version bump 21/22 pass, with this pin 22/22 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 18:54:04 -05:00
Benjamin Taylor 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>
2026-09-08 18:40:40 -05:00
Tyler Slaton 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.
v1.70.3
2026-09-09 01:37:03 +02:00
Mike Ryan 3c78b1ad52 fix(runtime): keep nullable support scoped to null branches 2026-09-08 16:31:48 -07:00
Ben Taylor 31d0cda168 fix(a2ui): report a generative-UI result that renders nothing (closes OSS-1048) (#6802)
A generative-UI result that does not paint reports nothing today. The
turn finishes, the input returns to idle, the network calls are all 200,
and the console is byte-identical to what it held before the request.
The only signal is a human noticing a blank space in a screenshot.

Three commits, each breaking one of those silences. Rendering behavior
is unchanged throughout — every report is a development-only
`console.warn`.

## A tool call with no renderer

`use-render-tool-call.tsx` resolves a renderer by name, then by agentId,
then by wildcard, then returns `null`. The existing comment defends that
choice well: auto-painting a default card would leak internal tool names
plus raw args and result JSON into every app's production chat. That
argument is about *painting*, and it does not cover *warning*.

The warning names the tool the agent called and lists the renderer names
that are registered. When the cause is a name that does not match, that
is the whole diagnosis.

It 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.

## An A2UI surface that gets operations and paints nothing

Two reports, both in `A2UIMessageRenderer.tsx`.

**Operations arrived and no paint followed.** The renderer already waits
8s for a surface to report its first paint before dropping the loader,
so reaching that fallback is itself the signal. No new threshold was
invented. `surfaceHasRenderableContent` already knows which half is
missing, so the message says which: no `updateComponents` at all, or
`"path"`-bound components whose `updateDataModel` never carried a value.

**Operations named a surface that was never created.** `A2UIRenderer`
renders its `fallback` for an unknown surface id and that defaults to
`null` — the card is absent and the log is empty. `processMessages` is
synchronous, so a surface still missing after it was never created. This
report is also deferred and re-checked, because operations stream and a
snapshot can reach the processor before the `createSurface` that gives
it somewhere to go.

## The two surface-id resolvers 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. 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 files the operations against a surface `createSurface`
never made, and an unknown surface id renders the `null` fallback above.
So the old React order could *produce* the silence the second commit
teaches the renderer to report — which is what the new test asserts, by
requiring that the missing-surface warning stay quiet.

## What this does not cover

A surface that exists, holds complete components, and still draws
nothing. `onReady` fires exactly when `surfaceHasRenderableContent` is
true, so that case is invisible to the paint-fallback path by
construction. Filed as OSS-1057 with a concrete mechanism: both
renderers hard-code a root component id of `root`, and a components list
without one shimmers forever.

The other item on OSS-1048 — a turn whose only output is generative UI
recording a `tool` message with no assistant parent — is a different
repo and a real design decision. Filed as OSS-1056.

## Verification

`react-core` 1565/1565, `a2ui-renderer` 24/24, both builds clean,
lefthook green on all three commits.

Every new negative assertion was mutation-tested against the pre-change
code. Two of them are guarded twice over, and removing either guard
alone left the test green — so both had to be removed before the test
would fail, which is what confirms it is not vacuous.

Closes OSS-1048.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

* **Bug Fixes**
* Operations now consistently target the correct surface, prioritizing
nested surface identifiers and falling back to top-level identifiers
when needed.
  * Improved handling of operations for surfaces that are created later.

* **Diagnostics**
* Added development-time warnings when surfaces receive operations but
render nothing.
* Added warnings for operations targeting missing surfaces or unresolved
root components, including likely causes.
* Added warnings when tool calls have no matching renderer, with
registered renderer details where available.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-08 18:31:15 -05:00
tylerslaton 69a940c70e chore: release monorepo v1.70.3 2026-09-08 23:22:32 +00:00
Tyler Slaton 30f67b18f1 fix(inspector): enable Learning without flags and correct status (#6957)
## Problem

Inspector Learning required both runtime debug mode and a separate
handler opt-in, while Threads did not. The Home and launcher Learning
indicators also read Memory availability, so configured Learning could
remain off. Enabled launcher toggles were purple rather than green
2026-09-09 01:17:31 +02:00
Tyler Slaton fdb6ce0714 fix(inspector): require Learning container configuration for status 2026-09-08 16:05:53 -07:00
Tyler Slaton 1b2d8eca85 fix(runtime): sync public API manifest after removing Learning flag 2026-09-08 16:03:32 -07:00
Tyler Slaton 78e498bf33 fix(inspector): derive green Learning status from its own endpoint 2026-09-08 15:52:32 -07:00
Mike Ryan 591b3bcd53 docs: show the component tool imports for the built-in agent 2026-09-08 15:52:18 -07:00
Tyler Slaton 290a8323ae fix(runtime): expose Inspector Learning without extra flags 2026-09-08 15:51:58 -07:00