14810 Commits

Author SHA1 Message Date
Tyler Slaton e764482e46 chore: release monorepo v1.68.0 (#6498)
## Release monorepo v1.68.0

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

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `monorepo` packages to `1.68.0`
   and generated AI-enhanced release notes.

2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
   must pass before merging. This is the review gate.

3. **Review the release notes** in `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.68.0`
   - Creates git tag `monorepo/v1.68.0`
   - Creates a GitHub Release with the final release notes

### Before merging

- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit 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.68.0
2026-08-14 13:39:21 -07:00
tylerslaton e6864b6bdd chore: release monorepo v1.68.0 2026-08-14 20:11:31 +00:00
Tyler Slaton c39ace681b fix(release): keep generated artifacts current in release PRs (#6497)
## What changed

- Regenerate the public API manifest after stable release version bumps.
- Update package skill `library_version` metadata from each package
before mirroring.
- Detect stale package skill versions in check mode.
- Cover write-mode synchronization and check-mode drift detection.

## Why

The v1.68.0 release PR bumped package versions without refreshing
generated release artifacts. That made the public API manifest test
fail, and refreshing the manifest then exposed stale package-skill
versions.

This keeps both derived artifacts synchronized as part of the existing
stable-release workflow.

## Validation

- `pnpm exec vitest run scripts/__tests__/sync-plugin-skills.test.ts
scripts/__tests__/public-skill-drift.test.ts` — 19 passed
- `pnpm check:plugin-skills`
- `pnpm check:public-api-manifest`
- Targeted `oxfmt` check
- Pre-commit and commitlint hooks
2026-08-14 13:09:31 -07:00
Tyler Slaton dedd07591d fix(release): sync package skill versions during releases 2026-08-14 12:57:55 -07:00
Tyler Slaton d542a29445 fix(release): regenerate API manifest after version bumps 2026-08-14 12:56:29 -07:00
Tyler Slaton be3e23ef68 feat(channels-slack): render table cells as rich_text when they carry markup (#6481)
## Problem

Portable `<Table>` / `<Row>` / `<Cell>` cells were always emitted as
Slack `raw_text`, which is literal. Measured in a real Slack workspace:

- `[**CPK-1234**](https://linear.app/...)` renders as literal text
- Slack's own `<https://…|CPK-1234>` renders as literal text
- a bare URL is not auto-linkified

So there was **no way** to get a clickable link or bold text into a
table cell through the portable vocabulary. This blocks moving an
OpenTag issue list from one-text-section-per-row to a real Slack
`table`, since its identifiers are markdown links to Linear.

## Change

`<Cell>` body content is now emitted as a `rich_text` cell **when, and
only when, it carries markup** — a link, bold, italic, strikethrough or
inline code. Plain content still produces the byte-identical `raw_text`
payload it always did (emoji glyphs and everything else pass through
untouched), so no existing fixture or snapshot changes.

- New `src/markdown-to-rich-text.ts` converts the portable dialect into
`rich_text` runs. It routes through the existing `markdownToMrkdwn` —
the package's single source of truth for what the portable dialect means
— and tokenizes its `mrkdwn` output. The package keeps one markdown
parser; what is added is an `mrkdwn` tokenizer, not a second dialect.
- Header cells always stay `raw_text`: Slack already renders them bold
(verified — no visual difference), and `rich_text` is not allowed in a
`data_table` header cell, so promoting one risks a refusal.
- Truncation: the 2000-char cell budget now applies to the **visible
text** of a rich cell, mirroring `truncateText` (ellipsis only when
something was actually dropped). Link URLs are not counted, and a link
whose label is cut stays a link — this keeps a rich cell and the
equivalent plain cell truncating at the same place.
- Only the portable `table` path is touched. A `data_table` reaches
Slack solely through the native passthrough (`Slack.Block.DataTable` →
`native-codec.ts`), which is untouched; `rich_text` body cells were
verified to render in both block types.

## Verification

Both target payload shapes were **verified live in a real Slack
workspace** (delivered through the managed transport, clickable/bold
confirmed by eye) and are asserted exactly in the tests:

```json
{"type":"link","url":"https://linear.app/copilotkit/issue/CPK-1234","text":"CPK-1234","style":{"bold":true}}
```

```json
[{"type":"text","text":"bold","style":{"bold":true}},
 {"type":"text","text":" plain "},
 {"type":"text","text":"code","style":{"code":true}},
 {"type":"text","text":" — "},
 {"type":"link","url":"https://linear.app/copilotkit/issue/CPK-1234","text":"unstyled link"}]
```

New tests cover plain text (unchanged `raw_text`), the bold link, mixed
runs in one cell, a header cell with markdown in it, both truncation
paths, and that word-internal markers (`provider_file_id`, `2 * 3 * 4`)
are left alone.

`nx run @copilotkit/channels-slack:{check-types,test,build}` all pass
(405 tests), `oxfmt --check` clean, `oxlint` warning count unchanged. No
existing test needed changing.

Linear: OSS-794
2026-08-14 11:55:12 -07:00
Lukas Moschitz f248a7eb30 feat(channels-slack): render table cells as rich_text when they carry markup
Portable <Cell> content was always emitted as a Slack `raw_text` cell, which
is literal: markdown links, Slack link syntax and bare URLs all rendered as
plain characters, so there was no way to get a clickable link or bold text
into a table cell through the portable vocabulary.

Body cells whose content contains a link, bold, italic, strikethrough or
inline code are now emitted as a `rich_text` cell. Plain content still
produces the byte-identical `raw_text` payload, and header cells always stay
`raw_text` (Slack renders them bold already, and `rich_text` is not allowed
in a `data_table` header cell).

The conversion reuses `markdownToMrkdwn` — the package's single source of
truth for the portable dialect — and tokenizes its `mrkdwn` output into
rich-text runs, so the package keeps one markdown parser. The 2000-char cell
budget now applies to the visible text of a rich cell.
2026-08-14 11:52:53 -07:00
Tyler Slaton 929ad01edb feat(runtime): assign threads to Learning Containers (#6428)
## What changed

- add one `learning.containerId` hook for Intelligence web and Channel
runs
- send the stable ID through existing Thread create and lock calls
- reject invalid IDs, cross-container reassignment, and SSE-only use
before an agent run starts
- keep the shipped plural React hooks as deprecated compatibility APIs
- forward Intelligence options through the package-root `CopilotRuntime`
- document persisted IntelligenceAgentRunner AG-UI events as the
Learning source

## Why

Learning Containers belong to a Project, and each Thread has at most one
immutable Container assignment. The Runtime selects that Container; it
does not upload transcripts.

## Cross-repo boundary

This PR owns Runtime Thread assignment only. Intelligence PR #787 owns
Project authorization, persisted Learning data, queue and runner work,
product UI, CLI downloads, Helm wiring, and the shared rollout flag.

## Related

- Intelligence platform:
https://github.com/CopilotKit/Intelligence/pull/787
- Architecture and merge-readiness walkthrough:
https://ent-1149-learning-v1.mikeryandev.chatgpt.site
- [ENT-1149](https://linear.app/copilotkit/issue/ENT-1149)

## Validation

- pre-commit Nx test, build, publint, and attw checks passed for
affected public packages
- React package suite: 123 files, 1,480 tests passed
- focused runtime review suite: 109 tests passed
- focused React compatibility review suite: 23 tests passed
- `pnpm nx run @copilotkit/runtime:check-types`
- `pnpm nx run @copilotkit/react-core:check-types`
- runtime and React package builds passed
- scoped oxlint passed with 0 errors and 12 existing warnings
- exact-head GitHub checks passed; required review is still pending
2026-08-14 11:29:42 -07:00
Maximiliano Korp eb3f430ae1 feat(runtime): mark Learning config experimental 2026-08-14 10:43:51 -07:00
Mike Ryan 99da13de53 fix(runtime): preserve Learning compatibility contracts 2026-08-14 10:34:44 -07:00
Mike Ryan a9f283ab55 feat(runtime): assign threads to Learning Containers 2026-08-14 10:34:27 -07:00
renovate[bot] 532b895df7 chore(deps): update reviewdog/action-actionlint action to v1.73.2 (#6492)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[reviewdog/action-actionlint](https://redirect.github.com/reviewdog/action-actionlint)
| action | patch | `v1.73.1` → `v1.73.2` |

---

### Release Notes

<details>
<summary>reviewdog/action-actionlint
(reviewdog/action-actionlint)</summary>

###
[`v1.73.2`](https://redirect.github.com/reviewdog/action-actionlint/compare/v1.73.1...v1.73.2)

[Compare
Source](https://redirect.github.com/reviewdog/action-actionlint/compare/v1.73.1...v1.73.2)

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

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4yOS41IiwidXBkYXRlZEluVmVyIjoiNDQuMjkuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-08-14 14:51:25 +00:00
renovate[bot] 6c43d9c699 chore(deps): update reviewdog/action-actionlint action to v1.73.2 2026-08-14 14:40:18 +00:00
renovate[bot] 66162b6ca1 chore(deps): update astral-sh/setup-uv action to v10.0.1 (#6487)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [astral-sh/setup-uv](https://redirect.github.com/astral-sh/setup-uv) |
action | patch | `v10.0.0` → `v10.0.1` |

---

### Release Notes

<details>
<summary>astral-sh/setup-uv (astral-sh/setup-uv)</summary>

###
[`v10.0.1`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v10.0.1):
🌈 Tolerate transient manifest timeouts

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v10.0.0...v10.0.1)

##### Changes

Thank you [@&#8203;arguile-](https://redirect.github.com/arguile-) for
making this action more resilient.

##### 🐛 Bug fixes

- Tolerate transient manifest timeouts
[@&#8203;arguile-](https://redirect.github.com/arguile-)
([#&#8203;1016](https://redirect.github.com/astral-sh/setup-uv/issues/1016))

##### 🧰 Maintenance

- chore: update known checksums for 0.12.4
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1017](https://redirect.github.com/astral-sh/setup-uv/issues/1017))

##### 📚 Documentation

- docs: update version references to v10.0.0
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1014](https://redirect.github.com/astral-sh/setup-uv/issues/1014))

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

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4yOS41IiwidXBkYXRlZEluVmVyIjoiNDQuMjkuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-08-14 14:17:17 +00:00
renovate[bot] 6023c008be chore(deps): update astral-sh/setup-uv action to v10.0.1 2026-08-14 13:51:49 +00:00
Ben Taylor ba98b6390a fix(runtime): only treat a request stream as consumed once it is drained (#6489)
Re-applies a community fix from closed PR #3489 (diagnosed by @AlexNti)
on the current tree. That PR patched `packages/v1/runtime/…`, a path
retired by the repo restructure, so it could not be merged as-authored —
but the bug is real and still live on `main`. Credit for the diagnosis,
repro, and original test is theirs.

Linear: OSS-610

## Problem

`isStreamConsumed` in
`packages/runtime/src/lib/integrations/node-http/request-handler.ts`
over-reported that the request stream had been consumed:

```ts
return Boolean(
  req.readableEnded ||
  req.complete ||
  readableState?.ended ||
  readableState?.endEmitted,
);
```

`req.complete` and the private `_readableState.ended` are set by the
Node HTTP parser once all network bytes reach the socket — before the
route handler reads anything. Any framework that awaits between socket
read and dispatch (notably the **Next.js pages router**) therefore sees
them already `true` while the body sits unread in
`_readableState.buffer`.

The call site in `node-http/index.ts`:

```ts
const streamConsumed = isStreamConsumed(req) || parsedBody !== undefined;
const canStream = hasBody && !streamConsumed;
```

With `bodyParser: false` there is no `req.body` to rebuild from either,
so the handler logged `"Request stream consumed with no available body;
sending empty payload."`, forwarded an **empty body** upstream, and the
client got `400 Invalid JSON payload` — making agents unreachable on the
pages router.

## Fix

Rely only on `req.readableEnded`, which flips true after the `end` event
fires from genuinely draining the stream. `readableEnded` is exactly
`_readableState.endEmitted`; the two dropped flags conflate *"the
message arrived"* with *"the application read it"*.

The body-parser case is unaffected: parsers drain to `end` (so
`readableEnded` is true), and the `parsedBody !== undefined` half of the
call-site check covers it independently. The `isDisturbedOrLockedError`
fallback remains as a backstop.

`copilotRuntimeNodeHttpEndpoint` backs the `nextjs/pages-router`,
`node-express`, and `nest` integrations, so all three are covered by
this change.

Live-checked the body-parser assumption on express 5.2.1 with
`express.json()` + `express.urlencoded()` mounted:

| request | `readableEnded` | `req.body` | path taken |
|---|---|---|---|
| `application/json` | `true` | parsed | synthesis (unchanged) |
| `application/x-www-form-urlencoded` | `true` | parsed | synthesis
(unchanged) |
| `text/plain` (parser skips) | `false` | `undefined` | streaming
(correct) |
| `multipart/form-data` (parser skips) | `false` | `undefined` |
streaming (correct) |


## Testing

New
`packages/runtime/src/lib/integrations/node-http/__tests__/request-handler.test.ts`
— @AlexNti's four unit cases plus a live `http.IncomingMessage` case
that starts a real server, awaits past the parser, and asserts both the
verdict and that the body was still readable.

Verified the tests actually pin the bug by running them against the
pre-fix implementation:

```
$ git stash -- .../request-handler.ts && vitest run .../request-handler.test.ts
 FAIL  > isStreamConsumed > returns false when EOF was pushed and `complete` is set but nothing was read (async framework)
 AssertionError: expected true to be false
 FAIL  > isStreamConsumed over a real http.IncomingMessage > reports an unread body as not consumed after async routing...
 AssertionError: expected true to be false
 Test Files  1 failed (1)
      Tests  2 failed | 3 passed (5)
```

With the fix applied:

```
$ vitest run src/lib/integrations/node-http/
 ✓ src/lib/integrations/node-http/__tests__/request-duck-type.test.ts (4 tests)
 ✓ src/lib/integrations/node-http/__tests__/request-handler.test.ts (5 tests)
 Test Files  2 passed (2)
      Tests  9 passed (9)
```

Full `packages/runtime` suite: `133 passed | 6 failed` — the 6 failing
files (`google-genai-adapter`, `fetch-handler`,
`inspector-metadata-passthrough`, `channel-manager-recovery`,
`handle-inspector-metadata`, `intelligence-platform/client`) fail
identically on a clean checkout of the same base commit in this worktree
(`parseInspectorMetadataV1 is not a function`, a stale cross-package
`dist`), so they are unrelated to this change. `oxfmt` + `oxlint` clean
on both touched files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-14 08:50:30 -05:00
Benjamin Taylor e7e8f7dc37 fix(runtime): only treat a request stream as consumed once it is drained
`isStreamConsumed` checked `req.complete` and the private
`_readableState.ended`/`endEmitted` alongside `req.readableEnded`. The first
two are set by the Node HTTP parser once all network bytes reach the socket,
which happens before the route handler reads anything. Any framework that
awaits between socket read and dispatch — notably the Next.js pages router —
therefore reported an unread body as already consumed.

With `bodyParser: false` there is no `req.body` to rebuild the request from
either, so `copilotRuntimeNodeHttpEndpoint` logged "Request stream consumed
with no available body" and forwarded an empty payload upstream, and the
request failed with `400 Invalid JSON payload`.

Rely only on `readableEnded`, which flips true after the `end` event fires from
genuinely draining the stream. The `parsedBody !== undefined` check at the call
site still covers the body-parser case.

Diagnosed by @AlexNti in #3489, which patched the since-retired
`packages/v1/runtime` path; re-applied here on `packages/runtime` with the
tests ported and a live `http.IncomingMessage` regression test added.
2026-08-14 08:37:25 -05:00
Alem Tuzlak 97addd4cc5 CrewAI Flows and Conversational Flows full D6 (#6392)
## Summary

Bring CrewAI Flows and CrewAI Conversational Flows to the complete
showcase D6 surface on the official integration stack:

- `ag-ui-crewai==0.3.0`
- `ag-ui-protocol==0.1.19`
- `crewai==1.15.11`
- GPT-5.4 across CrewAI showcase agents

The implementation follows the established D6 behavior while keeping
execution native to CrewAI. It covers reasoning and chained tools,
interrupts, shared and streaming state, generative UI, A2UI and
recovery, multimodal image/PDF input, and the remaining showcase
surfaces.

## Conversational Flows and docs

- Adds a Conversational Flows implementation with full feature parity
with regular CrewAI Flows.
- Keeps a single CrewAI documentation integration and adds a focused
guide for promoting a Flow to a Conversational Flow.
- Documents CrewAI `1.15.11` as the minimum supported version for
Conversational Flows.
- All manifest capabilities have connected documentation; no separate
feature-parity page or alpha guidance remains.

## Review hardening

- Offloads synchronous beautiful-chat backend tool work from the event
loop and verifies heartbeat/cancellation behavior.
- Probes `reasoning-custom` and `reasoning-default` as independent D6
cells.
- Removes the legacy multimodal fixture collision and requires
D6-specific response evidence.
- Forces the scheduling tool contract, preserves fallback slot
selection, and treats protocol cancellation as cancellation.
- Restores shared `data/` staging parity between the shell and
TypeScript showcase lifecycle, with an erosion guard.
- Materializes shared `data/` symlinks in both showcase build workflows
and returns the frontend matrix test to the CI gate with catalog-derived
counts.

## Surgical cancellation follow-up

This follow-up is deliberately limited to the two cancellation edge
cases identified in review; it does not add or change demo inventory,
UI, dependencies, or workflows.

- Makes Beautiful Chat's secondary `generate_a2ui` request genuinely
cancellable by using `AsyncOpenAI`, while retaining the thread fallback
for synchronous backend tools.
- Adds a narrowly version-scoped compatibility shim for the pinned
`ag-ui-crewai==0.3.0` bridge so a resolved `null` remains distinct from
cancellation. Only resolved-null is encoded as JSON `null`; cancellation
remains blank, both captured bridge bindings are updated, and version
drift fails loudly.
- Reuses the canonical shared `render_a2ui` schema for the secondary
model request.

## Validation

- CrewAI Flows production-equivalent isolated D6 matrix: **green**
- CrewAI Conversational Flows production-equivalent isolated D6 matrix:
**green**
- CrewAI Flows Python: **157 passing**
- CrewAI Conversational Flows Python: **162 passing**
- Showcase harness: **3,708 passing / 18 skipped**
- Showcase scripts: **2,505 passing**
- Both CrewAI production builds: **62/62 pages generated**
- Production Docker images build successfully with `ag-ui-crewai==0.3.0`
installed
- Full live GPT-5.4 validation exercised both implementations, including
ordinary chat, reasoning chains, interrupts, multimodal PDF/image input,
and A2UI generation
- Final PR CI at `f97f0768ba`: **76 successful / 3 intentionally skipped
/ 0 pending / 0 failing**

The live matrix produced valid alternate model behavior for two
deterministic fixture assertions: custom-catchall narration wording and
A2UI recovery succeeding on the first valid render rather than forcing a
malformed retry. Both underlying features completed successfully; these
are harness-vs-live nondeterminism rather than integration failures.
2026-08-14 13:18:41 +02:00
Ran Shemtov fa13d52502 Merge branch 'main' into codex/crewai-full-d6 2026-08-14 09:37:42 +02:00
Mark f97f0768ba test(showcase): isolate CrewAI resume bridge contracts
Exercise both bridge bindings without leaking monkeypatches, and verify rejected bridge versions cannot mutate either binding.
2026-08-13 16:46:26 -07:00
Mark 2116257e1e test(showcase): harden CrewAI cancellation regressions
Use bounded dispatch and cancellation waits in both CrewAI integrations, and verify any fallback worker finishes during cleanup.
2026-08-13 16:46:14 -07:00
Mark 35aa2a34a0 fix(showcase): close CrewAI cancellation edge cases
Use AsyncOpenAI so cancellation reaches the in-flight GenerateA2UI request while retaining the thread fallback for synchronous backend tools.

Preserve cancelled versus resolved-null interrupts across pinned ag-ui-crewai 0.3.0 by encoding only resolved null as JSON null and failing loudly on version drift.

Reuse the canonical shared render_a2ui schema so the secondary request remains aligned with the shared tool contract.
2026-08-13 16:46:05 -07:00
Sam Julien 6a5bb62b62 docs(README): Channels is live — add per-channel status (#6486)
Channels is live at
[copilotkit.ai/channels](https://www.copilotkit.ai/channels), but the
README still gated it behind an early-access form and claimed eight
channels were supported when only two of them ship today. This cleans
that up and brings the README in line with how we talk about Channels
now.

## What changed

**Channels is no longer early access.** The `🔒 Early access — we're
onboarding teams now` block and the `Request early access →` form link
are gone, replaced with a straight link to the live Channels page.

**"Beyond the Browser" is retired.** That section is now **Channels: One
Agent, Every Chat App**, and the copy leads with the Channels SDK — the
agent you already built, dropped into the chat apps your users live in,
no rewrite.

**Honest status per channel.** The platform table used to claim `✅
Supported` for eight channels in a single row. It now has two: Slack and
Microsoft Teams as Supported, with a quickstart you can follow today,
and Discord, WhatsApp, Telegram, Google Chat, iMessage, and SMS as
Coming soon, pointing at the Channels page.

No per-package or npm links anywhere. Package paths and versions move
too fast to keep accurate in a README, and a channel with no shipped
quickstart shouldn't send anyone to a 404. When one ships, it moves up
to the Supported row with a real link.

**New banner.** The old art above the badges showed only agent-framework
logos — half the story. The new one shows both halves: every agent
framework *and* every channel. It lives at
`assets/bring-your-own-agent-any-channel.png` so it ships with the repo.
The link target is unchanged.

**Messaging matches the site.** copilotkit.ai now leads with "Connect
any agent to any user," so two lines were updated to match:

- the subtitle now names Slack and Microsoft Teams instead of "beyond
the browser"
- "a multi-platform agentic framework" is now "the **horizontal layer
between your agents and your users**"

**One restored sentence.** `Your agent logic stays the same — AG-UI
handles the wire protocol, CopilotKit handles the UI layer…` was dropped
in #6239. It's the only line that explains what the platform table is
showing, so it's back.

## How the Supported / Coming soon line was drawn

Checked against the repo, npm, and the docs site rather than going off
existing prose. Slack, Teams, and WhatsApp have live docs pages; Discord
and Telegram have code but no docs; Google Chat, iMessage, and SMS have
neither. Slack and Teams are also the two the site markets today, so
those are the Supported rows.

WhatsApp is the debatable one — it has a published package *and* a live
docs page, so there's a case for promoting it. Left as Coming soon
deliberately; happy to flip it if that's wrong.

Worth a conscious ack: the banner shows all eight channel logos while
the table calls six of them Coming soon. That's intentional — it's the
launch art already running on the site.

## GTM impact

The `go.copilotkit.ai/beyond-the-web-form` link is removed **from the
Channels section only**. It's still live in the Self-Learning section,
which is genuinely still early access, so the go-link and its
attribution keep working. No campaign loses its destination.

One new outbound destination: `copilotkit.ai/channels`. No tracking,
pixels, or analytics touched — this is a README-only change.

## Verification

Everything below was checked against the live rendered branch, not
assumed:

- Every link in the diff returns 200 — the Channels page, and the Slack,
Teams, and WhatsApp docs pages.
- The new banner renders. `*.png` is LFS-tracked in this repo and no
other README image is LFS-backed, so this was worth confirming:
`raw.githubusercontent.com` serves the 131-byte LFS pointer, but
`github.com/…/raw/…` — the path GitHub's README renderer actually uses —
returns the real `image/png`, 856,322 bytes. Confirmed on the rendered
branch page.
- No empty or dead links in the README.
- Formatting matches the repo's `oxfmt` config.

## Follow-up

AG-UI's README has the mirror-image problem — it lists Discord,
WhatsApp, and Telegram as In Progress and duplicates the 1st-party
Slack/Teams row. Handling that separately in ag-ui-protocol/ag-ui#2279.
2026-08-13 16:19:53 -07:00
Nathan 🔶 Tarbert daaad93fd6 docs(README): drop the per-channel table, it repeated the platform table
The platform table above already says which channels are supported and which
are coming soon, so the second table said it twice. The Channels section
keeps the banner, the Channels SDK copy, and the link out to the Channels
page.

The coming-soon row now links straight to copilotkit.ai/channels instead of
jumping to a section that no longer lists those channels.
2026-08-13 18:31:32 -04:00
Nathan 🔶 Tarbert 27c37552d2 docs(README): rename the section to Channels and list every channel
"Beyond the Browser" is not how we talk about this anymore. The section is
now "Channels: One Agent, Every Chat App" and the copy leads with the
Channels SDK.

Lists all eight channels from the banner individually instead of lumping six
of them into one row. No per-package links: those move too fast to keep
accurate in a README, and channels without a shipped quickstart shouldn't
send anyone to a 404.
2026-08-13 18:20:00 -04:00
github-actions[bot] 9ff8a6c75d style: auto-fix formatting 2026-08-13 22:13:44 +00:00
Nathan 🔶 Tarbert b973ed6f77 docs(README): swap the top banner for the Bring Your Own Agent, Any Channel art
The old banner showed only the agent-framework logos. The new one shows both
halves of the story — every agent framework AND every channel — which matches
how the site now positions CopilotKit.

Committed under assets/ rather than a CDN upload so the image ships with the
repo. Link target is unchanged (go.copilotkit.ai/copilotkit-docs).
2026-08-13 18:11:40 -04:00
Nathan 🔶 Tarbert b678bc1100 docs(README): split Channels into Supported vs Coming soon, drop early access
Slack and Microsoft Teams are the two channels that actually ship today:
both have packages in this repo, published @copilotkit/channels-* builds,
and docs pages. Discord, WhatsApp, Telegram, Google Chat, iMessage, and SMS
had no quickstart to point at, so they move to a "Coming soon" row instead
of claiming Supported.

Channels is live, so the early-access gate is replaced with the public
https://www.copilotkit.ai/channels page.

Also restores the AG-UI explainer sentence under the platform table, which
was dropped in #6239.
2026-08-13 18:07:24 -04:00
Tyler Slaton e6510884a6 chore(README): Revise supported platforms in README (#6239)
Updated supported platforms and added new messaging services.

<!--
Thank you for sending the PR! We appreciate you spending the time to
work on these changes.

Help us understand your motivation by explaining why you decided to make
this change.


**Please PLEASE reach out to us first before starting any significant
work on new or existing features.**

By the time you've gotten here, you're looking at creating a pull
request so hopefully we're not too late.

We love community contributions! That said, we want to make sure we're
all on the same page before you start.
Investing a lot of time and effort just to find out it doesn't align
with the upstream project feels awful, and we don't want that to happen.
It also helps to make sure the work you're planning isn't already in
progress.

As described in our contributing guide, please file an issue first:
https://github.com/ag-ui-protocol/ag-ui/issues
Or, reach out to us on Discord: https://discord.com/invite/6dffbvGU3D


You can learn more about contributing to copilotkit here:
https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md

Happy contributing!

-->

## What does this PR do?

(Describe the changes introduced in this PR)

## Related PRs and Issues

- (Direct link to related PR or issue, if relevant)

## Checklist

- [ ] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
- [ ] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
2026-08-13 14:56:04 -07:00
Maxim 65742fbea1 feat(reskinnable-demo): bring every skin to demo-beat parity, and hoist teach mode, PDFs and attachments into the shell (#6455)
Takes `airline` and `keel` from ~1 demo beat each to full parity, and
hoists three
per-skin mechanisms into the shell on the way. Merges `main`, so
`bookstore` is
included.

**Verified live** by the author: Aeronova's new opening chart and
Rowan's beat-3c
fix both behave correctly against a running app.

## What changed

**1. Three mechanisms hoisted out of the skins and into the shell**

`teach-mode recording`, `PDF generation` and `attachment staging` had
each been
copied into three skins, and the copies had diverged. Every failure mode
of a
diverged copy is silent — `useRecording` returns inert no-ops outside a
provider, `logStep` early-returns while idle — so a broken copy still
compiles
and renders and is discovered on stage. They now live in
`src/shell/teach`,
`src/shell/documents` and `src/shell/attach`, with a "DO NOT IMPLEMENT
THE
CHAIN" guard in `templates.md` so a fourth copy cannot grow.

**2. `logistics`, `airline` and `keel` brought to beat parity**

`logistics` gained beats 2 and 3a–3d, then 4, 5 and 6. `airline` and
`keel` were
converted from in-memory `useData` stores to REST substrates and taken
through
every beat. Airline stays a PASSENGER concierge on purpose: its beat-6
gate is
ENTITLEMENT (a fare's own conditions), not organizational authority — a
rejected
first attempt reframed it as an ops-control desk, and the passenger
framing turned
out to make the gate stronger, since no choice of option can evade a
fare rule.

**3. `main` merged, including `bookstore`**

Seven skins now. The merge conflicted in seven files because both sides
hand-maintained the same roster; resolved by taking the union and
replacing
counts with the commands that derive them.

## Current state, derived rather than asserted

```
ls src/skins/                                   -> 7 skins
ls -d src/app/api/*/v1                          -> 7 REST substrates
ls src/skins/*/intelligence/seed-memories.ts    -> 7/7
grep -rln useAgentContext src/skins/*/layout.tsx -> 7/7 route readables
grep -l offerWorkflowRecording src/skins/*/tools.tsx -> 6/7 teach loops
grep -l 'useData:' src/skins/*/skin.tsx         -> bookstore only
```

`bookstore` is the one skin not demo-complete — it marks beats 3d and 6
`SKIPPED`
with a reason in its own beat map, which is a scope decision rather than
a gap.
It is also the only remaining `useData` implementor, so both substrates
are live.

## Bugs found and fixed that were not in scope

- **`resolvePage` returned `Object.prototype` members.**
`/banking/constructor`
answered 500 where it owed 404, on three shipped skins: an object
literal
inherits the prototype, so `PAGES["constructor"]` is a truthy Function
and
`?? null` never fires. Fixed, plus a shell guard walking every
registered skin,
  mutation-verified.
- **A real `TS2352` in a test file** that three green gates missed,
because
  nothing in this repo type-checks tests. Now `pnpm typecheck`.
- **A genuinely flaky test** in `shell/attach`, quantified at 39ms
against a 40ms
  budget under load. Its old assertion also passed under a mutated
implementation; the replacement drives the encode instead of timing it.
- **Rowan's beat-3c pill described the levers instead of firing the HITL
card** —
the tool said "confirm the levers with them first" without saying the
card IS
  the confirmation, and the prompt never named the tool.

## Verification

`pnpm lint` · `pnpm typecheck` · `pnpm test:unit` (214 files / 2448
tests) ·
`pnpm build` — all clean.

⚠️ **What tests cannot cover.** Beats 2, 4, 5 and 6 are
runtime-conditional and
need a live Intelligence stack. The suites prove the code and the
prompts are
right, not that the model obeys them. Airline's chart and Rowan's fix
were
confirmed by hand; the memory and teach-mode beats on the other skins
have not
been re-walked.

⚠️ **Several commits used `--no-verify`**, each recording why in its
body: the
pre-commit hook fails on a pre-existing `@copilotkit/vue` SSR test that
times out
at 5s on this machine and fails standalone with no merge in progress.
This
branch's diff is entirely inside `examples/showcases/reskinnable-demo`.
Also fixed
along the way: `packages/runtime`'s `better-sqlite3` was compiled
against Node 24
while `.nvmrc` pins Node 22, so every `SqliteAgentRunner` test threw on
load.

## Reskin skill impact

Answered per the standing rule in `CLAUDE.md`. The skill was updated in
the same
PR: the `Skin` contract's `useData` row, the beat matrix, the
demo-completeness
routing table, the memory-scope guidance (`user`, not banking's
`project` —
`forget-memories` skips project rows, so a project-scoped learned
procedure
survives every presenter reset), the beat-3c two-readings failure, and
the
`resolvePage` prototype hazard. Historical narration was stripped
throughout:
the docs now record current state and forward instruction only.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-13 20:37:14 +02:00
Maxim 6e07637083 Merge branch 'main' into feat/reskinnable-demo-beat-parity 2026-08-13 20:32:41 +02:00
Tyler Slaton be40072891 revert: remove public AEO surface contract (#6483)
## Summary

- revert #6458 and remove the public AEO contract, validator, docs page,
capability endpoint, CI enforcement, and related tests
- preserve the later AEO production synthetics from #6459 by giving them
a self-contained host and endpoint configuration
- update the synthetic workflow and runbook so they no longer refer to
the reverted contract

## Why

PR #6458 needs to be rolled back. A literal revert left #6459 importing
the removed validator and reading the removed contract, so this PR also
decouples that follow-on while retaining its production checks.

## Impact

The `/aeo` page and `/.well-known/copilotkit-capabilities/v1.json`
endpoint are removed, along with the contract validation CI. Existing
website/docs crawler synthetics remain available as a manual workflow.

## Validation

- `pnpm nx run @copilotkit/showcase-scripts:test --skip-nx-cache --
__tests__/check-aeo-synthetics.test.ts
__tests__/aeo-synthetics-wiring.test.ts` (6 tests)
- `npm --prefix showcase/shell-docs test -- src/app/sitemap.test.ts
src/lib/__tests__/next-config-redirects.test.ts` (12 tests)
- `git diff --check origin/main...HEAD`

Reverts #6458.
2026-08-13 10:56:06 -07:00
Maxim 335209b39a Merge branch 'main' into feat/reskinnable-demo-beat-parity 2026-08-13 19:55:27 +02:00
Tyler Slaton cf59bc51ba fix(showcase): decouple AEO synthetics from reverted contract 2026-08-13 10:49:40 -07:00
Maxim 3bf6e30e9a feat(reskinnable-demo): open Aeronova's demo on a flight-cadence chart
Beat 1 is the demo's first move, and it was answering "how do my trips look?"
with a trip wall. It now answers "How often do I fly?" with a picture: every
trip on the account laid out on a day scale, a today divider, the disrupted
ones called out, and the average gap between trips.

WHY A STRIP AND NOT BARS. The account holds seven trips across about ten weeks.
Monthly bars collapse that to three columns, hide which trips are disrupted, and
read as a stub on a projector. The strip uses all seven, and the GAPS are the
actual answer to "how often" -- which is why the summary quotes the average gap
rather than a count.

MEASURED against the shipped seed and the app's own clock, pinned in
data/flight-cadence.test.ts:

    7 markers - 0 flown - 7 ahead - 2 disrupted - average gap 11 days

Note the clock. This app runs on a FIXED demo clock (`store.ts` publishes
`now: SEED_NOW`, 2026-07-14), not the wall clock, so every seeded trip is AHEAD
and the strip is forward-looking. "About every 11 days" is therefore the honest
answer, and it is a better one than any count of flights behind us.

Structure:
  - `data/flight-cadence.ts` -- pure, no React, no Date. Takes `now` as an
    argument and reads days out of the ISO string by civil-day arithmetic.
    Both rules are load-bearing here: a `Date.now()` would put the divider in
    one place on the server and another in the browser (the hydration class
    this branch already chased once), and `new Date(iso)` on a string carrying
    an airport's UTC offset re-expresses a 23:00 Lima departure as the next
    day. `components/local-clock.ts` makes the same argument for display; this
    is its data-side counterpart.
  - `components/flight-cadence-chart.tsx` -- paints only. Receives `position`
    already normalised to 0..1, so there is no date maths in a component where
    nothing could unit-test it.
  - `showFlightCadence` registered with `useComponent`, NOT `useFrontendTool`:
    only a component replays out of thread history, which is what beat 2 asks
    the audience to reload and see.

Three details worth keeping:
  - Only flights someone HOLDS a booking on are drawn. The ledger's `flights`
    also carries the rebooking candidates, and counting offers would inflate
    the answer to the question being asked.
  - An unreadable departure is DROPPED and counted, never placed at day 0. A
    marker at the wrong point asserts a cadence that is false while still
    looking like data.
  - The helper takes a structural `{ id, flightId }` rather than `Booking`, so
    it accepts the client's `BookingDto` without a cast -- and therefore cannot
    see `waiverGround`, beat 6's sixth leak channel.

Tests: 12 on the helper (including the offset case, the drop-don't-relocate
case, and the seed figures), 7 on the component (every marker by flight number,
the cancelled trip named in WORDS and not only as a coloured dot, summary and
picture derived from one object), and `beat-1.test.ts` pinning the contract --
pill wording, registration via useComponent rather than useFrontendTool, the
prompt naming the tool and demanding prose alongside the chart, and no `Date`
in either new file.

Also uses airline's existing amber/negative tones from `trip-list.tsx` rather
than inventing a `warn` design token -- there isn't one; the vocabulary is
brand / positive / negative.

Gates: lint clean, tsc 0 errors, 214 files / 2448 tests, build exit 0.
--no-verify for the reason recorded in 6473cdcf9d.
2026-08-13 19:49:33 +02:00
Tyler Slaton 83bd1f9088 Revert "docs: define public AEO surface contract (#6458)"
This reverts commit d21aebc6e2, reversing
changes made to b075704c77.
2026-08-13 10:45:31 -07:00
Maxim e3d9c911a1 chore(reskinnable-demo): add a typecheck script and point the docs at it
`tsc --noEmit` is the only command in this tree that type-checks the 211 test
files -- `next build` visits only what the app's module graph reaches, and
vitest does not type-check at all. The docs already said so and told readers to
run `pnpm exec tsc --noEmit`; this makes it a script, so the command people are
told to run is one word and shows up in `package.json` beside the others.

Note this is a NEW convention here, not a missing piece being restored: no
package in this monorepo defines a typecheck script, so build-time checking is
the house norm and test files fall outside it everywhere, not just in this app.
This closes the DISCOVERABILITY half of that gap for this app only.

It does NOT make the check enforced. Nothing runs it unless a person or an
agent chooses to. Wiring it into CI is a repo-wide decision with real CI cost
across 45 packages and is deliberately not taken here.

Earned: a slot reported three green gates (lint, test:unit, build) and still
shipped a TS2352 in a test file, because none of those three look at test
files.

8 doc references updated from `pnpm exec tsc --noEmit` to `pnpm typecheck`
across README.md, CLAUDE.md, SKILL.md and demo-beats.md. Verified the script
runs clean under the new name.

--no-verify for the reason recorded in 6473cdcf9d: the pre-commit hook fails on
a pre-existing @copilotkit/vue timeout unrelated to this app.
2026-08-13 19:13:32 +02:00
Maxim b7c144d94a fix(reskinnable-demo): make Rowan's queue pill move the user, not describe the move
Reported from the running demo: clicking "Oldest pending requests" often got a
prose reply --

    Confirm the levers and I'll take you there: **pending** only, sorted by
    **oldest first**, top **10**.

-- and nothing else. No tool call, no confirm card, no navigation. Beat 3c
failing while looking like it worked: the answer is correct and well formatted,
and "that was a maneuver, not a link" goes unproven.

ROOT CAUSE, and why the model was not disobeying. It was obeying a sentence
that reads two ways. `showRequestQueue`'s description said "Confirm the levers
with them first" without saying WHERE that happens. The HITL card IS the
confirmation -- it lists the levers and waits -- but nothing said so, so
confirming in chat satisfied the instruction as written. Two other things left
it with no reason to prefer the tool:

  - `people/agent.ts` never mentioned `showRequestQueue`, or navigation at all.
    Nothing connected "show me the oldest requests" to a tool call.
  - `top` was `.optional()`, and an optional lever invites the model to go and
    ask for the missing value first.

`logistics` hit this and was fixed; `people` never was, because nothing pinned
the fix. This applies logistics' shape:

  - the description now says the card confirms, and says not to confirm in prose;
  - the prompt gains MOVE THEM, DON'T DESCRIBE THE MOVE, naming the tool and the
    "in front of ... rather than describe one" framing;
  - every lever is REQUIRED, with 0 as the "no limit" sentinel. That needs no
    page change: the render sets the `top` query param only `if (args?.top)`,
    which is falsy at 0, so the page applies no limit.

`beat-3c.test.ts` pins all three. It is source-level on purpose -- what went
wrong is what the MODEL was told, which lives in `description` and the prompt,
and nothing else in this app checks either. Mutation-verified: reverting `top`
to `.optional()` turns it red.

NOT changed: commerce. Its `top` is `.int().positive().optional()` with a stated
reason -- omitting it is exactly what its `parseTopLever` honours -- so that is a
different, documented design rather than the same defect. Its prompt already
names its nav tool.

Reskin skill impact: YES, fixed here. demo-beats.md ss 3c now records the
two-readings failure, the quoted prose it produces, both halves of the close
(description AND prompt), and the note that commerce's optional `top` is
deliberate so nobody copies the wrong shape.

Gates: lint clean, 211 files / 2420 tests passing. Committed with --no-verify
for the reason recorded in 6473cdcf9d: the repo's pre-commit hook fails on a
pre-existing @copilotkit/vue timeout unrelated to this app.
2026-08-13 19:07:42 +02:00
Mark 8a6d14b29a fix(showcase): port pydantic-ai integration to v2 and restore live system prompts (#6379)
Ports `showcase/integrations/pydantic-ai` — the last pydantic-ai surface
still on v1 — to Pydantic AI v2. Refs #6364.

Three commits plus a bot formatting fix, best reviewed separately.

## 1. `chore(showcase): port pydantic-ai integration to Pydantic AI v2`

- **`requirements.txt`** → `pydantic-ai-slim[ag-ui,openai]==2.22.0`,
`ag-ui-protocol==0.1.19`. Drops the `opentelemetry-api<1.44` ceiling
from #6374; v2 resolves cleanly against otel 1.44.0, so the workaround
is no longer needed. `starlette<1.0.0` is unchanged and satisfies v2's
`>=0.46.2`.
- **9 `StateDeps` imports** move from `pydantic_ai.ag_ui` (removed in
v2) to `pydantic_ai.ui`.
- **`agent_server.py`** — `Agent.to_ag_ui()` was removed in 2.0.0, so a
`mount_agent()` helper builds the equivalent Starlette sub-app and
mounts it. The shape is deliberately identical to what v1's `AGUIApp`
produced — a Starlette app whose only route is `POST /`, named
`run_agent` — so **all 19 mount paths behave exactly as before, trailing
slashes included, and no TypeScript route file changes**.

`deps` is constructed **per request**. v1's `run_ag_ui` did `deps =
replace(deps, state=state)`, handing each run its own object; v2's
adapter does `deps.state = state`, mutating what it is given. A single
shared instance under v2 therefore lets concurrent runs overwrite each
other's state mid-run.

## 2. `fix(showcase): apply the multimodal provider gate to v2 native
content`

v2's `AGUIAdapter.load_messages` converts AG-UI attachments to native
content types *before* the model boundary; v1 delivered the raw AG-UI
part dicts. `_NATIVE_CONTENT` listed `BinaryContent` as a flatten
fixpoint, so under v2 inline attachments were waved straight through and
the entire provider gate was skipped:

- inline PDFs were no longer text-extracted, so raw bytes went to OpenAI
- unsupported image subtypes (HEIC/SVG/TIFF) were no longer degraded and
reached the provider as images, which fails the turn
- missing-mime magic-byte sniffing never ran
- `AudioUrl`/`VideoUrl` were neither fixpoints nor classifiable, so they
hit the fail-loud raise

`BinaryContent` is no longer a fixpoint. `_classify_native_content` maps
native content onto the same `(kind, scheme, mime, value)` tuple the
AG-UI classifier already produces, so **every existing gate applies
unchanged** — no gate logic was rewritten. `audio/*` and `video/*` are
named explicitly because `_kind_for` routes them to `"other"`, and a
missing mime defaults to `"image"` so the sniffer runs.

Net behaviour matches v1: a supported inline image still flattens to an
`ImageUrl` data URI, which is why most of the suite went green without
touching assertions.

Five assertions did change. They checked that state-backing content was
still AG-UI `InputContent`, which encoded v1's bridging. They now assert
the flatten's output (`ImageUrl`) never appears in state — the leak they
were written to guard. The adjacent identity and snapshot checks that
prove non-mutation are untouched.

## 3. `fix(showcase): gate url-source content and correct the v1-parity
claim`

Adversarial review of the first two commits found the gate was only half
fixed. `_NATIVE_CONTENT` still short-circuited `ImageUrl` and
`DocumentUrl`, which v2 builds from unvetted client input, so url-source
attachments bypassed the gate where v1 routed them through it:

- an `image/heic` or `image/svg+xml` url reached the provider as
`input_image`, which the Responses API rejects — failing the turn
- an `audio/mpeg` document url reached it as `input_file`
- a blank-mime inline PDF went to the image sniffer instead of text
extraction, because `load_messages` collapses `ImageInputContent` and
`DocumentInputContent` to the same bare `BinaryContent` and erases the
modality v1 defaulted on

Native content is now gated **before** the fixpoint check rather than
instead of it. `_classify_native_content` returns a tuple only when the
gate must act; `None` means provider-safe and falls through to the
fixpoint, preserving object identity. `ImageUrl` is gated rather than
rerouted so a provider-safe one keeps its identity and any explicit
`_media_type`.

It also corrected a false claim. The `mount_agent` docstring said
routing *and* behaviour were unchanged. Routing is; model input is not.
v2 defaults `manage_system_prompt='server'`, so each agent's
`system_prompt=` now reaches the model. On v1 it never did —
`_agent_graph` emitted system parts only `if not messages` and the AG-UI
bridge always supplied history — so **18 of 19 agents had silently dead
system prompts on main**. A/B on both versions with the same agent and
request: v1 sends 0 system-prompt parts, v2 sends 1. The new behaviour
is correct and kept; the docstring now says so.

## Verification

Against pydantic-ai 2.22.0, in a venv built from this branch's
`requirements.txt`:

- **52/52 Python tests pass**, up from 42/52.
`test_multimodal_content_mapping.py`'s `importorskip` pointed at the
removed `pydantic_ai.ag_ui`, which would have skipped all 43 of its
tests **green** under v2; it now targets `pydantic_ai.ui.ag_ui` and uses
the public `AGUIAdapter.load_messages` in place of the v1 private
helper.
- **16/19 mounts** return `200 text/event-stream` with `RUN_STARTED …
RUN_FINISHED` and no `RUN_ERROR`, driven through the real app with
`TestClient` using trailing-slash URLs as the TS routes do. The other
three (`/a2ui_dynamic`, `/beautiful_chat`, `/`) reach tool execution and
then fail on a raw `OpenAI()` client constructed inside a tool, which
the harness cannot intercept and aimock handles in CI.
- **Per-request deps isolation** confirmed on
`/shared_state_read_write`: state sent by one request does not appear in
the next.

`build-check (pydantic-ai)` is green on this branch, and because
`requirements.txt` changed, the cached pip layer was invalidated — so
that was a **genuine fresh resolve of pydantic-ai 2.22.0 inside the real
Dockerfile**, not a cached pass. It also confirms dropping the
`opentelemetry-api<1.44` ceiling is safe.

### D6 harness probes — run, with a baseline

The behavioural gate is the shared harness D6 probes. No CI job runs
them for showcase paths, so they were run locally on both this branch
and `main`:

| | main (v1) | this branch (v2) |
|---|---|---|
| passed | **33** / 36 | **34** / 36 |
| `reasoning-display` | ✗ `no reasoning-role message rendered within
5000ms` | ✅ **passes** |
| `gen-ui-agent` | ✗ `waitForTurnComplete … runStartCount=2,
done-signal-missing` | ✗ identical error |
| `shared-state-read` | ✗ `Strict mode: 1 candidate fixture(s) skipped
by sequence/turn state` | ✗ identical error |

```bash
cd showcase
AIMOCK_URL_LOCAL=http://localhost:4010 bin/showcase test pydantic-ai --d6 --direct --rebuild --cycle --verbose
```

**The port takes D6 from 33/36 to 34/36.** The two remaining failures
are pre-existing on `main` with byte-identical error strings — this
branch neither causes nor fixes them, and both are tracked in #6381
rather than blocking here.

`gen-ui-agent` is root-caused and is not fixture drift: that demo was
never ported to pydantic-ai. `src/agents/gen_ui_agent.py` exists in
llamaindex with a real `set_steps` tool but has no counterpart here, the
route points at `/gen_ui_tool_based/` (the chart-viz agent), and
`set_steps` is declared nowhere in the package. The fixture fabricates
`set_steps` calls the backend cannot honour, so pydantic-ai rejects the
unknown tool and exhausts its single retry. Confirmed live against real
OpenAI: the cell returns plain text, which is correct for the code as
written.

`reasoning-display` going green is the notable behavioural gain, and it
retires a documented v1 limitation. `PARITY_NOTES.md:91-97` justifies
omitting the reasoning-message branch of `use-rendered-messages.tsx` on
the grounds that "PydanticAI's AG-UI adapter does not emit reasoning
content today" — true on v1, false on v2. (That block is stale on two
further counts: it cites `@ag-ui/core@0.0.43` where `package.json` pins
0.0.57, and claims `ReasoningMessage` is not exported where it is
imported at `reasoning-block.tsx:4`.) Correcting it is tracked on #6364.

To be precise about what that proves: **v2 forwards reasoning content
where v1 dropped it.** The probe supplies the reasoning channel via its
fixture, so what is verified is the forwarding path — adapter → AG-UI
stream → frontend renderer — end to end. Whether a given model actually
emits a reasoning summary live is a separate matter and outside this
port's control: it requires a native reasoning model
(`reasoning_agent.py` defaults to `gpt-5`, overridable via
`REASONING_MODEL`) and, for summary text, a verified OpenAI
organisation. A live run here returned prose with no reasoning block,
consistent with the org-verification gate rather than anything in the
port.

Also verified: the image builds from scratch on v2. Because
`requirements.txt` changed, the cached pip layer was invalidated, so
`build-check (pydantic-ai)` in CI was a genuine fresh resolve of
pydantic-ai 2.22.0 inside the real Dockerfile — which also confirms
dropping the `opentelemetry-api<1.44` ceiling is safe.

### CI gate coverage, for the record

No CI job exercises this package's runtime behaviour on a PR, on this
branch or on `main`:

- `test / e2e / dojo` runs from the upstream `ag-ui` checkout (`ref:
main`) against upstream example agents, and filters on `packages/**` /
`sdk-python/**`
- `test_showcase-frontend-matrix.yml` is dispatch-only and builds the
integration from `base/` — a frozen-backend React baseline
- `showcase_validate.yml` asserts `tests/e2e/` exists with a minimum
spec count; it does not run it
- the package's own `tests/e2e/` (37 files) is invoked by nothing — per
`AGENTS.md` rule 1 the measuring test is the shared harness probe, so
that layer is legacy

## Remaining for #6364

Two acceptance criteria are outstanding, which is why this says Refs
rather than Closes:

- the harness D6 value-test (`bin/showcase test pydantic-ai --d6
--rebuild`), which no CI gate runs for showcase paths
- `PARITY_NOTES.md` has 6 version-dependent blocks, 4 of which were
already inaccurate against the tree before this PR; left alone
deliberately to keep this diff scoped

## Possible follow-up

`multimodal_agent.py` still reaches into three private APIs
(`pydantic_ai._run_context`, `pydantic_ai.models.wrapper`,
`pydantic_ai.models.{ModelRequestParameters,StreamedResponse}`) and
subclasses `WrapperModel`, overriding
`request`/`count_tokens`/`request_stream`. v2 adds a supported
alternative: `AbstractCapability.before_model_request`, which receives a
`ModelRequestContext` carrying `messages` and `streaming`. Migrating
would delete those private imports and ~85 lines. Deliberately not in
this PR — it fixes nothing and would obscure the review.
2026-08-13 09:59:40 -07:00
Alem Tuzlak 4e9eee3094 feat(runtime): add MiniMax built-in models (#6464)
Reason: Add the current MiniMax text models to BuiltInAgent model
resolution.

- Register MiniMax-M3 and MiniMax-M2.7 as built-in model identifiers.
- Resolve MiniMax model strings through the global endpoint with API key
and regional base URL configuration.
- Document both model specifiers and cover global and China endpoint
selection.

Checks:
- `node_modules/.bin/nx run @copilotkit/runtime:test --
src/agent/__tests__/resolve-model-baseurl.test.ts`
- `node_modules/.bin/nx run @copilotkit/runtime:check-types`
- `pnpm validate:model-names`
- `node_modules/.bin/nx format:check
--files=packages/runtime/src/agent/index.ts,packages/runtime/src/agent/__tests__/resolve-model-baseurl.test.ts`
- `git diff --check`
2026-08-13 18:57:51 +02:00
Ben Taylor 7187a0aa19 fix(channels-slack): three defects that silently broke Slack Block Kit (#6462)
Closes OSS-819. Part of OSS-794, which stays open for the OpenTag
demonstration (OSS-820).

*Reopened from #6454 — the branch was renamed so Linear links the right
sub-issue, and GitHub closed the original rather than retargeting it.
Same three commits, unchanged.*

Three defects in the Slack Block Kit catalog, each verified against a
real workspace. **99 lines changed across three files.**

The reason these sat undetected matters more than their size: **a
payload Slack refuses produces no error anywhere.** No log line, no
exception, no failing test — the message simply never arrives, which is
indistinguishable from a bot that had nothing to say. The renderer
compounds it by design, dropping unknown nodes silently so one bad node
cannot fail a whole message.

## 1. `container` was refused on every send

Its children serialized into `blocks`; Slack reads `child_blocks`.

## 2. Every menu, checkbox, radio group, overflow and confirm dialog was
refused

The codec stamped `type` onto every catalog entry, including composition
objects whose schema has none — Slack's option object is `{text,
value}`, and the same holds for `confirm`, `option_group`,
`conversation_filter`, `dispatch_action_config`, `slack_file`, `trigger`
and `workflow`. An unknown field makes Slack reject the entire message,
so the whole interactive surface was unusable through `Slack.Object.*`.

Measured against a live workspace: **1 of 26 block elements delivered
before this fix, 23 after.**

Note the existing `native-catalog.test.ts` asserted the very assumption
that was wrong — that every entry serializes its discriminator. It was
green while the product was broken. It now asserts the corrected rule.

## 3. An image could not use a file already in the workspace

The required-field check demanded `image_url` unconditionally; Slack
accepts `image_url` *or* `slack_file`. An image needs alt text plus
either source now, and passing neither is still an error.

## Two catalog corrections

`file` leaves the authorable manifest. Slack: *"You can't add this block
to app surfaces directly, but it will show up when retrieving messages
that contain remote files."* The same sentence appears verbatim in
`@slack/types`' own doc comment. It is an inbound shape; offering it as
a component meant offering something that can never succeed.

`alert` stays out with its citation — *"Alert blocks are currently only
supported in modals."* Verified rather than assumed: Slack's own example
payload posted verbatim into a message is refused, while a plain section
in the same delivery seconds later arrives.

## How these were found

A fixture per catalog entry — 19 authorable blocks, 26 elements, 15
composition objects — with the expected payload **transcribed from
`docs.slack.dev`, not captured from our serializer**, delivered through
a managed Channel into a real workspace. **55 of 60 deliver.**

That corpus is a working instrument, not a deliverable, so it is
deliberately not part of this PR — ~1700 lines of fixtures to maintain
against a 99-line change is a bad trade for reviewers. It lives with the
team and gets re-run when the catalog moves.

One methodological note, because it changed what we count as proof: the
first live run passed entries that demonstrated nothing. A rich-text
block with one unstyled run renders exactly like a plain section; a
carousel with one card renders like a card. Both were accepted and
worthless as evidence — caught by a human looking at the output, not by
the harness. Fixtures had to *exercise* each entry, and that is what
surfaced defect 2.

## Found in the same pass, tracked separately

- **OSS-817** — the managed path dropped every picker's value (9 of 26
elements). Fixed and confirmed live.
- **OSS-818** — handler ids collide across structurally identical
messages.

## Verification

`test`, `check-types` and `build` green across `channels-slack`,
`channels`, `channels-intelligence` and `runtime`, both with and without
the fixture corpus present. Every block, element and object was
delivered into a live Slack workspace and reviewed by eye.
2026-08-13 11:56:14 -05:00
Alem Tuzlak 14f90410ff docs(examples): fix stale clone paths in v1 example READMEs (#6471)
<!--
Thank you for sending the PR! We appreciate you spending the time to
work on these changes.

Help us understand your motivation by explaining why you decided to make
this change.


**Please PLEASE reach out to us first before starting any significant
work on new or existing features.**

By the time you've gotten here, you're looking at creating a pull
request so hopefully we're not too late.

We love community contributions! That said, we want to make sure we're
all on the same page before you start.
Investing a lot of time and effort just to find out it doesn't align
with the upstream project feels awful, and we don't want that to happen.
It also helps to make sure the work you're planning isn't already in
progress.

As described in our contributing guide, please file an issue first:
https://github.com/ag-ui-protocol/ag-ui/issues
Or, reach out to us on Discord: https://discord.com/invite/6dffbvGU3D


You can learn more about contributing to copilotkit here:
https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md

Happy contributing!

-->

## What does this PR do?

Fixes three `examples/v1/*` README files whose "Clone the repository"
step `cd`s into a directory that no longer exists (leftover from when
examples were reorganized under `examples/v1/`). Following the README as
written fails at the first step with `cd: no such file or directory`.

- `examples/v1/chat-with-your-data/README.md`: `cd
CopilotKit/examples/copilot-chat-with-your-data` → `cd
CopilotKit/examples/v1/chat-with-your-data`
- `examples/v1/form-filling/README.md`: `cd
CopilotKit/examples/copilot-form-filling` → `cd
CopilotKit/examples/v1/form-filling`
- `examples/v1/state-machine/README.md`: `cd
CopilotKit/examples/copilot-state-machine` → `cd
CopilotKit/examples/v1/state-machine`

This matches the already-correct format in
`examples/v1/travel/README.md`.
Docs-only change, no code/behavior affected.

## Related PRs and Issues

- N/A

## Checklist

- [X] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [ ] If the PR changes or adds functionality, I have updated the
relevant documentation
- [X] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
2026-08-13 18:55:16 +02:00
Alem Tuzlak 4880afc846 test(skills): guard public skill API contracts (#6457)
## Summary

- extend the existing public-skill drift suite to validate maintained
setup assets against the generated public API manifest
- fail when a skill imports an unpublished CopilotKit package or
entrypoint, or a manifest-deprecated API
- run the guard in the existing plugin-skills workflow when skills or
the manifest change

## Why this matters

Coding agents copy these skill assets directly into user projects.
Mirror-sync tests prove that our duplicated skill files match, but they
do not prove that the examples still reference packages and APIs we
actually publish. A stale import can make CopilotKit fail at the first
install or build step, which is exactly the kind of failure that
prevents agents from choosing and successfully adopting us.

This PR adds the smallest deterministic guard for that risk. It reuses
our existing Vitest suite and canonical public API manifest; it does not
introduce an eval harness, run agents, score behavior, collect metrics,
add a provider, or add dependencies.

## Scope

This is package-contract validation, not behavioral evaluation. Broader
questions such as whether an agent follows a skill well, how many
attempts it needs, and whether the generated application behaves
correctly remain separate work and should start with a concrete decision
the deterministic checks cannot answer.

## Verification

- `pnpm exec vitest run scripts/__tests__/public-skill-drift.test.ts
scripts/__tests__/sync-plugin-skills.test.ts` (17 tests)
- `pnpm check:plugin-skills`
- `pnpm check:public-api-manifest`
- targeted TypeScript, oxfmt, and oxlint checks
- mutation check: replacing `BuiltInAgent` with deprecated `BasicAgent`
fails with the manifest-provided replacement

Linear: PDX-320
2026-08-13 18:53:26 +02:00
Alem Tuzlak 44d54c65d6 fix(react-core): repair useCopilotReadable effect deps, convert args, and dependencies (#6409)
Fixes #6383. Fixes #6243.

Both issues land in the same 35 lines of `useCopilotReadable`, so they
are fixed together. This PR also covers a third defect neither issue
reports.

All of it traces to a single commit: 80dffec4e7 ("feat: Reimplement
CopilotKit on top of refreshed internals (v1.50.0)", #2638), which
repointed the hook from the v1 context tree onto the v2 flat context
store. The pre-1.50 implementation was correct on every count below.

## Fixes

**`available` was missing from the effect deps** (#6383)
The effect body read `available` but the deps were `[description, value,
convert]`, so toggling between `"enabled"` and `"disabled"` after mount
did nothing. It is back in the deps, along with the `available =
"enabled"` default the port dropped.

**`convert` was called with one argument** (#6243)
`(convert ?? JSON.stringify)(value)` invoked a user's `(description,
value) => string` as `convert(value)`, so it received the value as
`description` and `undefined` as `value`. The branches are now split
rather than passing two arguments to the combined expression —
`JSON.stringify(description, value)` would treat the second argument as
a *replacer*, not a value.

**`dependencies` was accepted and ignored** (#6243)
The second positional argument was destructured but never reached the
deps array. Now spread, matching `useCopilotAdditionalInstructions`.

**The `found` dedup branch was dead code** (unreported)
It compared `JSON.stringify({ description, value })` against a stored
entry whose `value` had already been serialized by `addContext`
(`packages/core/src/core/context-store.ts:36`). That never matches — for
objects or strings — so the branch and its cleanup-skipping early return
were unreachable. Deleted rather than repaired: making the comparison
work would newly let component A's unmount remove a context entry
component B is still relying on. The test `keeps separate entries for
identical readables in two components` locks that in, and it passes
against the pre-fix hook, which is what confirms the branch never fired.

## `parentId` / `categories`

Both are still in `UseCopilotReadableOptions` and were still documented
— the top-of-file JSDoc example was a `parentId` tutorial — but the same
v1.50 commit dropped them from the hook body. They have been no-ops
since.

This PR does not implement them. Real support needs parent/child
modelling in the v2 context store, which is flat by design
(`getContextForAgent` emits `{ description, value }` only). Instead both
are marked `@deprecated` and the JSDoc example is rewritten to document
behavior that exists. Tracked in #6408.

## Not addressed

Two pre-existing behaviors left alone to keep this a bugfix:

- `value` is in the deps raw, so an inline object literal re-registers
the entry on every render. Pre-1.50 depended on the serialized string
instead.
- The hook returns `undefined` on first render, since the ref is
assigned inside the effect.

## Testing

`useCopilotReadable` had no test file. This adds one — 12 tests, using a
fake that mirrors `ContextStore` semantics (`addContext` assigns an id
and stores the already-serialized value).

Full project suite — `nx run @copilotkit/react-core:test`:

```
 Test Files  124 passed (124)
      Tests  1487 passed (1487)
 NX   Successfully ran target test for project @copilotkit/react-core and 17 tasks it depends on
```

Each fix is covered by a test that fails against the pre-fix hook.
Reverting only `use-copilot-readable.ts` and re-running the new file:

```
   ✓ registers the context on mount
   ✓ removes the context on unmount
   ✓ available > registers nothing when mounted as disabled
   × available > removes the context when flipped to disabled after mount
     → expected [ { description: 'employees', …(1) } ] to deeply equal []
   × available > re-adds the context when flipped back to enabled
     → expected [] to deeply equal [ { description: 'employees', …(1) } ]
   × convert > is called with (description, value) in that order
     → expected "spy" to be called with arguments: [ 'employees', …(1) ]
   × convert > is used in place of JSON.stringify
     → Cannot read properties of undefined (reading 'map')
   ✓ convert > serializes the value alone when convert is omitted
   × dependencies > re-runs the effect when a dependency changes
     → expected "spy" to be called 2 times, but got 1 times
   ✓ dependencies > does not re-run the effect when the dependency is unchanged
   ✓ re-registers when the description changes
   ✓ keeps separate entries for identical readables in two components

 Test Files  1 failed (1)
      Tests  5 failed | 7 passed (12)
```

The two that still pass pre-fix are deliberate: `serializes the value
alone when convert is omitted` guards the `JSON.stringify` replacer trap
in the fix itself, and `keeps separate entries…` is the evidence that
the `found` branch was dead.

With the fix applied:

```
 ✓ src/hooks/__tests__/use-copilot-readable.test.tsx (12 tests) 15ms

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

Types — `pnpm --filter @copilotkit/react-core check-types`:

```
> @copilotkit/react-core@1.66.2 check-types
> tsc --noEmit
```

(no diagnostics)

Formatting — `oxfmt --check` on both files:

```
Checking formatting...
All matched files use the correct format.
Finished in 16ms on 2 files using 18 threads.
```

`oxlint` reports one warning, on `...(dependencies || [])` in the deps
array. The same pattern already warns in
`use-copilot-additional-instructions.ts`, `use-frontend-tool.ts` and
`use-coagent-state-render.ts`; CI runs `oxlint .` without
`--deny-warnings`.

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

The `convert` and `dependencies` fixes were independently found and
fixed first by @jwgrsol in #6246, opened a week before this PR. Credited
below.

Co-authored-by: jwgrsol <wefhio1985@gmail.com>
2026-08-13 18:51:54 +02:00
Alem Tuzlak 19fb1329b7 fix(shell-docs): repair 15 reader-visible doc defects (#6425)
Fifteen defects in the shell-docs tree, each verified against the
running site or the source of truth rather than pattern-matched. Found
while root-causing
[PDX-313](https://linear.app/copilotkit/issue/PDX-313).

Scoped deliberately: this is content only. The checker changes that
surfaced these follow separately.

## Snippet components used with props but never imported (7)

The subtlest item here, and invisible to anyone skimming the source.

`<FrontendTools components={…} framework="pydantic-ai" />` without an
import falls through to `stubWithPartial` in the global mdx-registry,
which drops props "on the floor" by design. So `framework` never reached
the partial and the shared snippet rendered **untailored** — the reader
got generic content on a framework-specific page.

The `mastra` and `ag2` siblings were already correct. All seven broken
ones are in authored trees, matching the template-residue pattern from
OSS-777.

## Tutorial cross-links that land on the homepage (4)

`/tutorials/ai-todo-app` and `/tutorials/ai-powered-textarea` have no
`index.mdx`, so they `307 -> /`. A reader clicking "next: the todo app
tutorial" gets the docs homepage. The pages are at `/overview`.

## Dead `YouTubeVideo` imports (2)

The component is provided globally by `mdx-registry.tsx`, and four other
pages render it with no import at all. These two imported a module that
has never existed in the repo.

## Stale `byoc-*` demo ids (2)

Renamed to `declarative-*` in 70e2fb31 (2026-05-10, *"rename byoc-\*
slugs to declarative-\*"*); the docs were never updated, so the ids
resolve against nothing in the registry. Only the three registry ID
references per page change — `snippet_cell`, `InlineDemo`,
`IntegrationGrid`.

## What was cut, and why

An earlier revision of this PR also rewrote nine `/integrations/<fw>/*`
links to their canonical URLs. Checking production, those were never
broken:

```
/integrations/adk/quickstart  ->  301  /google-adk/quickstart
```

`seo-redirects.ts` keeps that retired surface alive for inbound SEO
traffic, so readers always landed correctly. Canonicalizing them is
still worth doing — a 301 costs a round trip and couples internal
navigation to a legacy surface — but it is cosmetic, and it was padding
a diff whose value is the defects above. Dropped; tracked separately.

## Left alone deliberately

The `runtimeUrl` / `agent` code samples on `generative-ui/hashbrown.mdx`
and `generative-ui/json-render.mdx`. The API routes were renamed to
`copilotkit-declarative-*`, but the agent ids were **not** renamed
consistently:

| demo | agent id |
| --- | --- |
| `declarative-hashbrown` | `agent="declarative-hashbrown-demo"`
(renamed) |
| `declarative-json-render` | `AGENT_ID = "byoc_json_render"` (not
renamed) |

A blind find-and-replace over `byoc-` would have shipped a broken
copy-paste sample. Needs an owner's call.

## Review notes

15 files, +17/-12. The seven import additions are the only changes that
affect what renders; the rest are identifier strings and link targets.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-13 18:50:31 +02:00
Alem Tuzlak 47ad5e34a3 refactor(react-native)!: converge tool-call rendering onto CopilotKit's shared registry (#6438)
## What does this PR do?

`@copilotkit/react-native` maintained a **private tool-call render
registry** (`hooks/RenderToolContext.tsx`) alongside the canonical one
that `CopilotKitCoreReact` already provides — and which every React
Native app already ships, unused. This PR deletes the fork and points
React Native at the shared registry.

That fork caused three bugs:

| Bug | Symptom | Cause |
|---|---|---|
| **Tool renders never streamed** | A component registered with
`useRenderTool` / `useComponent` painted nothing until the tool call
completed | `CopilotChat` used `JSON.parse` on the argument buffer.
While a model writes a tool call that buffer is *invalid JSON by design*
— AG-UI delivers `TOOL_CALL_ARGS` deltas that are concatenated
client-side — so the parse threw on every delta, warned, and fell back
to `{}` |
| **`useComponent` rendered nowhere** | Silently, with no error | It
writes to core's registry; React Native's chat read React Native's
private `Map` |
| **Chat history degraded** | Navigating away from the registering
screen turned earlier tool calls into a `Called: <name>` placeholder |
The private `Map` deleted renderers on unmount; core deliberately keeps
them |

`@copilotkit/react-core` has used `partialJSONParse` on this path since
v2 shipped. React Native diverged because `useRenderToolCall` was
excluded from its re-exports on the stated grounds that it "depends on
DOM elements via `DefaultToolCallRenderer`" — a claim that was never
true of the hook itself. It was only ever reachable through the fat
`/v2` entry, whose weight is the real hazard (#4893). #5883 moved it
into `/v2/headless` on 2026-07-23; the exclusion comment was rewritten
the next day without revisiting the reason.

### What changed

- **One registry.** `useRenderTool` registers through `useFrontendTool`
into `CopilotKitCoreReact.renderToolCalls`. `CopilotChat` and any custom
surface consume react-core's `useRenderToolCall`.
- **Types are derived, not declared.** `RenderToolProps` is now
`React.ComponentProps<ReactToolCallRenderer<T>["render"]>`, so React
Native cannot drift from `ReactToolCallRenderer` — the contract every
registered renderer is actually invoked against. Change that contract
and `check-types` names every React Native renderer the change breaks.
React Native narrows only the *return* type to `ReactElement | null`,
which `FlatList`'s `renderItem` genuinely requires.
_Scope of that guarantee (corrected during review):_ it does **not**
extend to the type react-core publicly exports under the same name.
Web's `RenderToolProps<S>`
(`react-core/src/v2/hooks/use-render-tool.tsx`) is a separate
hand-declared union, generic over a schema, carrying arguments under
`parameters` (not `args`) and declaring `status` as string literals
rather than `ToolCallStatus` members. Both divergences are live today
and nothing type-checks them shut — the one place the shapes meet,
react-core's own bridge, compiles because a string-enum member is
assignable to its own literal type but not the reverse. Aligning web's
alias is a breaking web API change, filed separately.
- **`RenderToolContext.tsx` deleted** (−150 lines), along with 15 tests
that described the removed subsystem. One of them — `unregisters the
render function on unmount` — asserted the chat-history bug as a
requirement.
- **Two structural CI guards for #4893**, in opposite directions: a test
failing if any React Native source imports the fat `/v2` entry, and a
script failing if react-core's `/v2/headless` or `/v2/context` chunks
ever link shiki/mermaid/cytoscape/katex/streamdown. Both were verified
able to fail by deliberately introducing the regression. These are
*structural* assertions, not size budgets — `dev-docs/bundle-size.md`
freezes `limit` fields until OSS-122.
- **`react-native` added to the bundle-size glob**, which it had never
been in, plus a `size:headless` measurement.

React Native also gains capabilities it lacked: render props inferred
from your schema, `name`/`toolCallId` on render props, and `result` on
completed calls.

**Corrected during review — two capabilities this originally claimed are
not delivered:**

- **Wildcard (`"*"`) renderers do not work on React Native.** Because
`useRenderTool` routes through `useFrontendTool` (which calls
`addTool`), `name: "*"` registers a frontend tool literally named `*` —
advertised to the model, and colliding with core's separate
wildcard-executable-tool path. react-core's `useRenderTool` is
renderer-only and special-cases the wildcard; React Native's is not. The
guide now advises against it.
- **`followUp` (and `available`) are not forwarded**, and the handler's
`context` argument is dropped, so `stopAgent()`'s abort signal is
unreachable from an RN handler.

Both are tracked in § Known limitations for the follow-up that converges
React Native onto react-core's hooks — deleting RN's `useRenderTool` in
favour of re-exporting `useFrontendTool` (tool + renderer) and
react-core's `useRenderTool` (renderer-only, wildcard-capable). That is
an API change with its own migration note, so it is not in this PR.

### ⚠️ Breaking (in a minor)

`useRenderToolRegistry` and `RenderToolProvider` are **removed**. Both
are documented on the docs site, so this is a real break — see the
`BREAKING CHANGE:` footer on `db67ccf`, which is what the release notes
derive from, plus the rewritten reference pages.

```diff
- const registry = useRenderToolRegistry();
- const renderer = registry.get(toolCall.function.name);
- return renderer ? renderer({ args, status }) : null;
+ const renderToolCall = useRenderToolCall();
+ return renderToolCall({ toolCall });
```

Also note two semantic changes: `args` is `Partial<T>` **only** while
`status` is `"inProgress"`, and a render function is now captured at
registration — if it closes over changing values you must declare them
in `deps` (React Native previously refreshed the closure on every
render).

**Known limitation:** agent-scoped renderer resolution does not take
effect on React Native. `CopilotChatConfigurationProvider` is not in
RN's provider tree, so `agentId` always resolves to the default.
Renderers still resolve by name; two agents registering the same tool
name resolve arbitrarily. Filed separately.

### A data point worth recording

Adding `useRenderToolCall` to the measured headless entry moved the
bundle **92.8 kB → 92.7 kB**. Flat. The hook React Native spent months
not using was already inside the chunk every RN app resolves whole —
Metro doesn't tree-shake, so the fork never saved a byte. It cost them.

### Testing

- `@copilotkit/react-native`: **253 passing / 22 files** ·
`@copilotkit/react-core`: **1480 passing / 123 files** · `check-types`
and `build` green for both.
- Each of the three bugs has a deterministic test driving a real
`CopilotKitCoreReact` — no mocking of the code under test.
- Both #4893 guards carry mutation evidence: introduce the regression,
watch them fail, revert, watch them pass.

### Follow-up

`useRenderTool`'s JSDoc is split across two blocks, which orphans the
primary description from IDE hover (the `@param deps` warning still
surfaces). One-line fix, deliberately left out of the final fix wave.

## Related PRs and Issues

- **Supersedes #6346** (@davidmckayv) — its diagnoses were correct and
its test assertions are ported here, re-driven through the real registry
rather than a mocked local one. Credited via `Co-Authored-By` on
`4104bd1`.
- Addresses the React Native half of **#4893**.
- Builds on **#5883**, which created the lean `/v2/headless` entry this
PR consumes.

## Checklist

- [x] I have read the Contribution Guide
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-13 18:49:32 +02:00
Alem Tuzlak c16476b960 chore: release python sdk 0.1.95 (#6444)
Closes #6231.

## Why

`copilotkit` on PyPI is stuck at **0.1.94 (2026-06-04)**. The newest
upload of any kind is the **0.1.95a4** prerelease from **2026-06-19**,
and four merged sdk-python fixes postdate it — so none of them exist in
any installable artifact. Consuming them today requires a VCS pin.

The version in `sdk-python/pyproject.toml` was never bumped, which is
why nothing published: the Python lane in `publish-release.yml` fires on
a merged PR that changes that version and no-ops otherwise. `sdk-python`
is not one of the `release / create-pr` scopes (`monorepo | angular |
channels`), so it never gets swept along with the JS releases. This PR
is the bump.

## What ships

Eleven commits since 0.1.94, including the two fixes the issue is
blocked on:

| commit | landed on main | |
|---|---|---|
| `44c43e477` | Jul 3 | fold app context into the system prompt — fixes
`langchain-anthropic` rejecting a second, non-consecutive system message
|
| `bb32138e1` | Jul 24 | read copilotkit context from config when state
is empty |
| `fee7ec237` | Jul 24 | bridge copilotkit context into LangGraph
subgraphs |
| `a76d59ae0` | Jul 26 | capture subgraph context from run input (#3886)
|

Plus `ag-ui-langgraph >=0.0.42`, the ag-ui state-channel declaration
with the `a2ui_params` host override, and the A2UI single-arg
`A2UIToolParams` work.

## Testing

- **Confirmed the fixes are genuinely unpublished.** Downloaded the
`0.1.95a4` sdist from PyPI and grepped it: `_get_copilotkit_context` and
the config-fallback docstring introduced by `bb32138e1` are absent. The
reporter's containment analysis is correct.
- **Reconciled the one date that looked wrong.** `bb32138e1` carries an
author date of Jun 10, before the Jun 19 prerelease, which would suggest
it should have been included. Its committer date is Jul 24 — it landed
on main after the prerelease was cut. All four fixes genuinely postdate
every published artifact.
- **Verified all four commits are ancestors of `origin/main`** and touch
`sdk-python/`.
- **Python unit CI green on main** — `test_unit-python-sdk.yml`
succeeded on Jul 27 at `e9148b305`, which is after the last sdk-python
change (`a76d59ae0`, Jul 26).
- **Matched the precedent.** The previous release, `2b5d2e0113` ("chore:
release python sdk 0.1.94"), was a one-line change to the same file.
`sdk-python/uv.lock` has no root `copilotkit` entry and `poetry.lock`
records only dependency versions, so neither needs to move; there is no
`sdk-python/CHANGELOG.md` and no `__version__` in `__init__.py`.
`pyproject.toml` is the single source.
- `0.1.95` sorts above the existing `0.1.95a4` prerelease, so the
publish lane's version-delta detection will fire.

## Follow-up, deliberately not in this PR

Seven files pin the old version and should move once 0.1.95 is actually
on PyPI — pinning ahead of the publish would break them:

-
`examples/integrations/{claude-sdk-python,langgraph-fastapi,langgraph-python,strands-python}/agent/pyproject.toml`
-
`showcase/integrations/{langgraph-fastapi,langgraph-python,strands}/requirements.txt`

Two showcase files (`_header_forwarding_middleware.py` in
langgraph-fastapi and langgraph-python) also carry comments describing a
workaround vendored against "copilotkit 0.1.94's
copilotkit_lg_middleware module" — worth rechecking whether the subgraph
fixes make that vendoring unnecessary.

Keeping the bump minimal so the publish lane cannot be held up by an
unrelated example failure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-13 18:45:52 +02:00
Ben Taylor 3a80c2696e feat(vue): mirror React's useAgent thread scoping, remove thread cloning (#6234)
## Problem

Vue's `useAgent` implemented per-thread agent **cloning** — a mechanism
React never had. Passing a `threadId` silently handed you a copy of the
agent:

```ts
useAgent({ agentId: "assistant", threadId: "thread-1" })  // → a clone, keyed (agent, threadId)
```

The clones lived in a module-level `WeakMap` (`globalThreadCloneMap`),
so:

- Nothing tied a clone's lifetime to the scope that created it — they
were never released.
- Components had to *look up* which copy was live.
`CopilotChatMessageView` called `getThreadClone(registryAgent,
config.threadId) ?? registryAgent` just to find the agent actually being
rendered.
- `getThreadClone` / `globalThreadCloneMap` were exported from the
module purely so components could do that lookup.

Meanwhile React grew an explicit contract for the same use case in
#6141: a private *proxied* agent, registered under a local `agentId` and
routed to a `runtimeAgentId`.

## Change

Deletes cloning entirely and ports React #6141's contract to Vue.

`cloneForThread`, `getOrCreateThreadClone`, `getThreadClone` and
`globalThreadCloneMap` are gone — zero references remain, including in
prose.

`UseAgentProps` becomes a base plus a two-branch union, with the same
all-or-nothing rule React now enforces:

```ts
useAgent()                                       // shared registry agent
useAgent({ agentId })                            // shared registry agent
useAgent({ agentId, runtimeAgentId, threadId })  // private proxied agent
```

Every partial set — `{ agentId, threadId }`, `{ agentId, runtimeAgentId
}`, `{ runtimeAgentId, threadId }` — is a compile error, backed by the
same three runtime guards with the same messages for callers TypeScript
doesn't reach.

### Parity with #6141

| | React (#6141) | Vue (this PR) |
|---|---|---|
| scoped branch | `agentId` / `threadId` / `runtimeAgentId`, all
required `string` | same, as `MaybeRefOrGetter<string>` |
| unscoped branch | `agentId?: string`, `threadId?: undefined`,
`runtimeAgentId?: undefined` | identical |
| runtime guards | 3 | same 3, same messages |
| thread resolution | prop → chat config, gated on `hasExplicitThreadId`
| identical |
| proxy registration | balanced effect on core + both ids | same deps |

## Two Vue-specific details

Both are load-bearing and were found by tests failing, not by
inspection:

**The pin watcher's first source is `() => agent.value`, not `agent`.**
Vue sets `forceTrigger` when any array watch source is a shallow ref, so
passing the ref directly re-ran the pin on *every* `triggerRef(agent)` —
i.e. every streamed message — re-pinning the inherited thread over one
`CopilotChat` had deliberately set for the chat it renders. Two existing
suites cover this (`uses the explicit agentId and threadId over
inherited configuration`). React has no equivalent hazard because effect
deps compare by identity.

**`CopilotChat` assigns `agent.threadId` inside its `/connect`
watcher**, not a separate one. `CopilotKitCore.connectAgent` reads that
field *synchronously* (`run-handler.ts`) to decide whether a restore is
fresh, so a later assignment lets `/connect` address the previous thread
— skipping the messages/state reset and re-stamping its restore key with
the stale id. Same placement as React's `CopilotChat`.

`CopilotChatMessageView` now resolves the registry agent directly
instead of consulting the clone map, and reads `copilotkit.agents` so it
recomputes when the registry changes.

## What callers see

**One agent per `agentId`** — the model React has always had. Thread
isolation is now explicit instead of implicit: ask for it and you get a
real, separately-registered agent rather than a copy that appears out of
nowhere.

```ts
// before — silently produced a copy of the "assistant" agent
useAgent({ agentId: "assistant", threadId: "thread-1" })

// now — an explicit private agent of your own, routed to "assistant"
useAgent({ agentId: "chat-1", runtimeAgentId: "assistant", threadId: "thread-1" })
```

Nothing in this repo needed updating: `CopilotChat`, `use-capabilities`,
`use-interrupt` and all six example apps already used `{ agentId }`.
`<CopilotChat agentId threadId>` is unchanged for consumers.

## Tests

`use-agent-thread-isolation.test.ts` (433 lines) covered clone semantics
that no longer exist; it's replaced by
`use-agent-thread-pinning.test.ts`, which pins the new invariants — one
instance per `agentId` never a copy, config-thread pinning gated on
explicitness, and all three all-or-nothing guards.

Four component suites used `getThreadClone` purely as a lookup to find
the agent under test and now read from the registry.

`MockMCPProxyAgent` recorded `addMessage` **only inside its `clone()`
override**, so those assertions were passing only because cloning
existed. The recording moves onto the class. `clone()` itself is left
intact everywhere — `CopilotKitCore`'s `SuggestionEngine` still clones
agents (`packages/core/src/core/suggestion-engine.ts`), so removing
those overrides would have planted a latent trap.

## Deliberately not included

Found while reviewing this area, real, but out of scope — each wants its
own change:

- `useAgent`'s header watcher **replaces** `agent.headers` instead of
calling `copilotkit.applyHeadersToAgent()`, dropping per-agent
construction-time headers. Regresses #5635 in Vue; React does this
correctly.
- `credentials` never reach a provisional agent.
- No `onAgentsChanged` subscription anywhere in `packages/vue`, so `()
=> copilotkit.value.agents` as a watch source never re-evaluates on
registry change.
- `/connect` is skipped for a plain `HttpAgent` — the `hasCustomConnect`
prototype comparison matches every real agent. Vue-only, no React
equivalent.
- `CopilotThreadsDrawer.ssr.test.ts` is a latent flake (5s timeout on a
dynamic import; passes in isolation).
2026-08-13 11:43:33 -05:00
Tyler Slaton f13fcb09e9 ci: add manual AEO production checks (#6459)
## Summary

- add an on-demand production check for the website and docs discovery
surfaces defined by #6458
- derive the ten in-scope routes and media types from the public
contract instead of maintaining a second monitoring manifest
- exercise those routes as four documented crawler user agents with a
global concurrency cap of four
- validate status, content type, canonical host, robots/sitemaps, one
sampled sitemap link, LLM index links, and soft-404 behavior
- retain failure evidence and provide a deliberate `exercise_alert`
input for proving the `#oss-alerts` path

## Why this matters

AEO is a production property, not a one-time content change. A correct
repository can still deploy a broken canonical, HTML fallback, stale
sitemap, or inaccessible LLM index. Those failures happen at the top of
the agent-led growth funnel: if agents cannot reliably discover and
verify CopilotKit, downstream recommendation and activation work never
gets a chance to perform.

This PR adds the smallest useful operating check for that risk. It is
deliberately limited to PDX-340's website/docs scope. It does not
monitor MCP, the CopilotKit capability document, raw Markdown, Open
Graph, or JSON-LD. Existing deploy-parser utilities are reused where
practical, requests run with bounded concurrency, and failures include
the exact URL, crawler identity, observed status/type, and a bounded
response excerpt.

The workflow is intentionally manual at first. We should not create a
scheduled noisy alarm while the website LLM endpoints are known red, and
we should not claim Slack ownership until a deliberate failure proves
the secret and alert path. A small follow-up can add the schedule after
one normal run is green and one `exercise_alert` run reaches
`#oss-alerts`.

## Stacked dependency

- Depends on #6458; this PR is intentionally based on
`codex/pdx-317-aeo-surface-contract`.

## Validation

- `pnpm nx run @copilotkit/showcase-scripts:validate-aeo-contract
--skip-nx-cache`
- `pnpm nx run @copilotkit/showcase-scripts:test --skip-nx-cache --
__tests__/check-aeo-synthetics.test.ts
__tests__/aeo-synthetics-wiring.test.ts
__tests__/verify-deploy.drivers.test.ts` (102 tests)
- targeted `oxfmt` and `oxlint` checks
- `git diff --check` and commit hooks

## Live baseline (2026-08-12)

The narrowed production command fails with eight records: four crawler
identities × two website gaps.

- `https://www.copilotkit.ai/llms.txt` returns HTTP 200 `text/html` with
a noindex soft-404 instead of plain text
- `https://www.copilotkit.ai/llms-full.txt` returns the same soft-404

The remaining website/docs targets pass: both home canonicals, both
robots files, both sitemaps and sampled links, and both docs LLM
indexes. The current failures are why this PR ships manual-first rather
than enabling a schedule.

## Status

PDX-340 remains In Progress until the website endpoints are fixed, a
normal workflow run is green, the deliberate Slack alert reaches
`#oss-alerts`, and a follow-up enables the agreed schedule.
2026-08-13 08:30:01 -07:00
Tyler Slaton d21aebc6e2 docs: define public AEO surface contract (#6458)
## Summary

- publish a single shared, versioned technical contract for website,
docs, and docs MCP AEO surfaces
- publish the human policy through the existing shell-docs MDX pipeline
at `/aeo`
- expose the machine-readable contract at
`/.well-known/copilotkit-capabilities/v1.json`
- validate the contract with JSON Schema/Ajv plus narrow repository and
CI cross-reference checks
- run the actual shell-doc behavior tests in CI and assign external
website and Pathfinder gaps to named owners

## Why this matters

Answer engines and coding agents decide which source to trust from
machine signals such as canonical hosts, stable URLs, response types,
and consistent capability claims. When those signals disagree,
CopilotKit can be classified incorrectly, cited from the wrong hostname,
or skipped even when it is the right product.

This PR gives those public surfaces a versioned source of truth. It
separates standards, community conventions, and CopilotKit-specific
guarantees; records real endpoint paths and media types; and makes
ownership explicit when behavior lives in another repository or service.
That gives us a reliable base for improving agent discovery without
pretending one repository can enforce every public surface.

The implementation deliberately uses the current docs architecture:
`/aeo` is ordinary shell-docs MDX under
`showcase/shell-docs/src/content/docs/`, not a bespoke page or the
retired docs tree. Schema shape lives in JSON Schema, while the small
TypeScript layer only checks relationships JSON Schema cannot express,
such as whether referenced files and CI commands exist.

## Validation

- `pnpm nx run @copilotkit/showcase-scripts:validate-aeo-contract
--skip-nx-cache`
- `pnpm nx run @copilotkit/showcase-scripts:test --skip-nx-cache --
__tests__/validate-aeo-contract.test.ts` (6 tests)
- `npm --prefix showcase/shell-docs test -- src/app/sitemap.test.ts
src/app/llms.txt/route.test.ts src/app/llms-full.txt/route.test.ts
'src/app/llms-mdx/[[...slug]]/route.test.ts'
src/app/well-known/copilotkit-capabilities/v1.json/route.test.ts
src/lib/runtime-config.test.ts
src/lib/__tests__/next-config-redirects.test.ts` (43 tests)
- `npm run typecheck` in `showcase/shell-docs`
- `npm run build` in `showcase/shell-docs`
- targeted `oxfmt`, `oxlint`, TypeScript, diff, and commit-hook checks

## External follow-ups

- CopilotKit/website must link the same policy and fix `/llms.txt` plus
`/llms-full.txt`, which returned 200 `text/html` soft-404 pages during
the production audit
- Pathfinder/docs MCP owners must define a machine-readable discovery
surface; the current contract records `/sse` as the known transport
without presenting transport availability as discovery

## Related

- PDX-317
2026-08-13 08:29:42 -07:00