15289 Commits

Author SHA1 Message Date
Lukas Moschitz 10d7820170 docs(inspector): only promise the highlights the code can deliver
Three claims the code refuses, and one source missing.

- "The failed tool call is highlighted" covered both tool cases, but
  `TOOL_NOT_FOUND` is emitted with `toolName` and `agentId` and no
  `toolCallId` -- a tool that was never found has no call to point at.
- "The failed `RUN_ERROR` event is highlighted" holds only when such an
  event is in the buffer; several codes reach the run source without one.
- "A red launcher pill names the failure. Click it" reads as unconditional,
  but the pill is suppressed where there is no room beside the launcher,
  leaving a red dot and nothing to click.
- Learning is the fifth source and appeared in neither page. Documented
  with the constraint that makes it different: the memory store is created
  on first visit, so nothing is reported before that.
2026-08-25 11:27:05 +02:00
Lukas Moschitz aeac35d168 fix(web-inspector): land an error only on arrival, not on every visit
`handleMenuSelect` applied the event error's landing on every click on Agent
or AG-UI Events. A landing selects the failed agent, clears the event
filters and re-expands the failed row -- help for a reader who came
*because* of that error, vandalism for one who did not.

`lastEventError` outlives being read on purpose, so the how-to-fix card
survives while it is being read. Together that meant: one run error, and
for the rest of the session every return to AG-UI Events silently wiped the
reader's own search filter and re-expanded a row from hours ago. It also
undid the `all-agents` restore eight lines above it, whose comment says it
exists "so the Events tab isn't silently filtered to one agent".

The three arrivals that really are landings keep it -- pressing the
launcher, pressing the card, and an error arriving while its view is
already open. The new test fails with the old call in place, on the value
the reader can see: expected '' to be 'bookFlight'.
2026-08-25 11:26:46 +02:00
Tyler Slaton 72fcd1dabd docs(langgraph): fix inaccurate "Works the same for create_react_agent" comments (7 sites) (#6614)
Closes #6607.

Verified against the pinned `langgraph_prebuilt` (1.1.0):
`create_react_agent` has neither `middleware` nor `system_prompt`
parameters, rejects `state_schema=CopilotKitState`, and is deprecated in
favour of `langchain.agents.create_agent`. Readers following the comment
hit an immediate `TypeError`.

All 7 occurrences on `main` are updated (4 sites noted in the issue plus
the 3 that landed with #5469):

-
`showcase/shell-docs/src/content/docs/integrations/langgraph/agent-app-context.mdx`
(Python + TypeScript)
-
`showcase/shell-docs/src/content/docs/integrations/langgraph/frontend-tools.mdx`
(Python + TypeScript)
-
`showcase/shell-docs/src/content/snippets/integrations/langgraph/frontend-tools.mdx`
(Python + TypeScript)
- `showcase/shell-docs/src/content/reference/hooks/useFrontendTool.mdx`
(Python)

Each comment is replaced with an accurate note (”`create_agent`
supersedes the deprecated `create_react_agent`, which accepts neither
`middleware=` nor `system_prompt=`”), and the TypeScript snippets now
reference `createReactAgent` (camelCase) instead of the Python
`create_react_agent` name — the second, smaller problem noted in the
issue.
2026-08-25 01:55:52 -07:00
Alem Tuzlak ba4260ad66 feat(web-inspector): add Event Snippets and save-as-snippet (#6649)
Open Inspector Event Snippets on localhost. You can compile, save, and
replay AG-UI events in chat. Chat shows a bookmark icon next to a tool
call, an A2UI block, or generative UI. Click the icon to save that turn
as a snippet.

## What does this PR do?

This PR adds the Inspector Event Snippets pane.

You can:

- Compile a snippet from a recipe (tool-call, reasoning, text, activity,
raw)
- Save snippets in origin-scoped localStorage
(`cpk:inspector:event-snippets`)
- Import and export snippets from the pane header
- Replay a snippet into live chat through Inspector-only Core inject

Each Run remints `messageId`, `parentMessageId`, `toolCallId`, and
`runId`. The second Run of the same snippet is a new turn.

On localhost, chat shows a bookmark icon beside a tool call, A2UI block,
or generative UI. The icon is absolutely positioned. It hangs to the
right when there is room. Otherwise it hangs to the left. The card stays
full chat width.

The React demo adds `sayHello`, `getTime`, `addNumbers`, and a **Call 3
tools** suggestion.

## Related PRs and Issues

- Linear
[OSS-874](https://linear.app/copilotkit/issue/OSS-874/new-features-also-allow-users-to-emit-specific-events-from-the)

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] 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)

## Testing

### Commands run

1. Lefthook pre-commit ran `nx` targets `test`, `publint`, and `attw`
for 27 affected projects. All passed.
2. I did not run `pnpm test:pr` (full repo). Lefthook ran the affected
package matrix only.

### Manual test

1. Run `pnpm demo:react` from the repo root.
2. Open http://localhost:3000
3. Open Inspector and select Event Snippets.
4. In chat, click **Call 3 tools**. Confirm three tool cards at full
chat width, with the bookmark hanging outside the card.
5. Click a bookmark, then click Run twice. Chat shows a second turn with
new IDs.

### How this PR makes testing easy

- `packages/web-inspector/src/lib/__tests__/event-snippets.test.ts`
- `packages/core/src/__tests__/inspect-inject.test.ts` (covers two
injects)
- React demo: `examples/v2/react/demo/src/app/page.tsx`

## Linked issues

Linear
[OSS-874](https://linear.app/copilotkit/issue/OSS-874/new-features-also-allow-users-to-emit-specific-events-from-the)

## Risk / rollback

- If ID remint is wrong, a second Run can no-op or duplicate a turn.
- The save icon shows on localhost Inspector (or when `showDevConsole`
is `true`).
- Rollback: revert this PR.

## Public API change

**Before**

Angular has no Inspector service.

```ts
// no CopilotInspector export from @copilotkit/angular
```

**After**

```ts
import { CopilotInspector } from "@copilotkit/angular";

const inspector = inject(CopilotInspector);
inspector.openInspector({
  messageId: "msg-1",
  menu: "event-snippets",
});
```

React and Vue apps that already mount Inspector on localhost need no new
caller code. Chat wires the bookmark through Inspector context.

`@copilotkit/core` exports `ɵinjectInspectorEvents` for Inspector only.
App code must not call it. There is no public Core emit API.
2026-08-25 09:39:37 +02:00
deepshekhardas bc0cea234a docs: fix missing space in CONTRIBUTING heading 2026-08-25 12:09:23 +05:30
deepshekhardas 78a751a887 docs: use em dash in AG-UI protocol line in README 2026-08-25 12:08:18 +05:30
Mark 198b226d94 docs(showcase): narrow worker pin policy note 2026-08-24 20:59:28 -07:00
Mark e90a3dd938 fix(showcase): preserve worker policy on railway pin 2026-08-24 19:36:04 -07:00
Mark c56f1d8a43 fix(showcase): verify promoted deployment id before success 2026-08-24 19:36:03 -07:00
Mark c5a0f36efc docs(showcase): document durable worker recycling 2026-08-24 18:00:10 -07:00
Mark a2344ece7f fix(showcase): enforce worker policy in production operations 2026-08-24 17:59:55 -07:00
Mark 92d3de79fb fix(showcase): recycle harness workers with clean exits 2026-08-24 17:59:41 -07:00
Mark 1351fa63d2 fix(showcase): apply durable worker policy in staging 2026-08-24 17:59:22 -07:00
renovate[bot] 47c5510b49 chore(deps): update github actions (#6639)
This PR contains the following updates:

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

---

### Release Notes

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

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

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

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

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

</details>

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

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

---

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

---

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

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zNS40IiwidXBkYXRlZEluVmVyIjoiNDQuMzkuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-08-24 17:59:15 +00:00
Alem Tuzlak 328ee3cb58 Merge origin/main into feat/OSS-871-view-thread-in-app 2026-08-24 19:58:19 +02:00
Alem Tuzlak 98968b49c2 fix(web-inspector): address review on event snippets
- Raw recipe: the Events JSON input now calls requestUpdate, so Run and
  Save stop being permanently disabled.
- Tool args recovery: one depth scan replaces the parse-every-prefix loop.
  Truncated args from a streaming tool call now fail at once, not after
  seconds of blocked main thread.
- Chat bookmark: hidden while the tool arguments are incomplete, so a
  partial payload cannot be captured.
- saveEventSnippet: React, Vue, and Angular wrap the body, so a compile or
  storage failure is reported instead of becoming an unhandled rejection.
- Vue and Angular now gate the in-chat affordances on a dev build plus
  localhost, the same as React. showDevConsole: true on a staging URL no
  longer puts a bookmark into a production chat.
2026-08-24 19:10:06 +02:00
Benjamin Taylor 11895f77d1 flag the Next.js-served origin the quickstarts' relative runtimeUrl assumes
Every framework quickstart sets runtimeUrl="/api/copilotkit". That resolves
only because Next.js serves the app and the runtime from one origin, and
nothing on the page said so -- so a reader on a client-only frontend followed
a step that could not work for them.

Annotate the provider step in all 13 quickstarts that carry it, pointing at
the per-frontend guides that show a standalone runtime server and an absolute
runtimeUrl.

The callout deliberately uses no root-relative links. The Angular surface
reuses these same integration quickstarts via resolveAngularDoc's fallback to
frameworkContentSlug, and its link contract keeps readers inside /angular/**;
linking three other frontends' pages both broke that contract and was wrong
content for an Angular reader.

aws-strands is left un-annotated while the Strands rejuvenation is underway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 11:59:45 -05:00
renovate[bot] bf44b367b8 chore(deps): update github actions 2026-08-24 16:59:00 +00:00
Mike Ryan 8ae9e00a5e chore: rename Enterprise Intelligence product copy (#6652)
## Why

Rename the product from **CopilotKit Enterprise Intelligence** to
**CopilotKit Intelligence** across the open-source repo and docs site.
This is a copy and asset change. It does not rename public APIs, routes,
packages, environment variables, or compatibility keys.

**Enterprise Intelligence** remains only where the text explicitly names
the enterprise tier.

## What changed

- Updated 220 tracked files across guides, reference docs, snippets,
examples, skills, nav labels, page metadata, and generated framework
records.
- Updated user-facing package TSDoc and runtime warning text, with
paired test changes.
- Kept self-hosting and pricing copy explicit about Team, custom
Enterprise plans, and the Enterprise Intelligence tier.
- Fixed grammar and possessives created by the rename, including thread
lifecycle, auth, agent runner, and registry copy.

## Image updates

| Asset | Change |
| --- | --- |
| Channels architecture, light | Visible label now says **CopilotKit
Intelligence** |
| Channels architecture, dark | Visible label now says **CopilotKit
Intelligence** |
| Managed pricing | Product heading now says **COPILOTKIT
INTELLIGENCE**; the **ENTERPRISE** tier label remains |

All three PNGs remain Git LFS assets.

## Compatibility boundaries

| Preserved | Reason |
| --- | --- |
| `/premium`, `/concepts/oss-vs-enterprise`, and existing docs routes |
No redirect or SEO migration |
| `/images/enterprise-intelligence/*` | Existing asset paths |
| `enterprise-platform` registry key and `intelligence-platform` module
paths | Internal compatibility identifiers |
| Public exports, package names, environment variables, and API symbols
| No public API change |
| Existing `go.copilotkit.ai/enterprise-intelligence-platform` target |
Link label changed; target stays compatible |

## Validation

| Check | Result |
| --- | --- |
| `pnpm check:public-api-manifest` | Passed; manifest unchanged |
| React core, runtime, and Vue tests, typechecks, builds, publint, and
attw | Passed |
| Showcase scripts | 2,519 tests passed |
| Shell docs lint and typecheck | Passed |
| Shell docs test suite | 437 tests passed |
| Shell docs production build | Passed; 223 pages |
| Hosted CI on the current PR head | All required checks passed |

Residual searches found no old product name or malformed rename
variants. The remaining old slugs and paths are the compatibility
identifiers listed above.

## Related PRs

- Intelligence product and CLI:
https://github.com/CopilotKit/Intelligence/pull/925
- Website: https://github.com/CopilotKit/website/pull/535
2026-08-24 09:55:18 -07:00
Benjamin Taylor 6f3638c446 document Copilot Runtime hosting for React SPAs
A React single-page app is the one frontend the docs could not get to a
working runtime. Every framework quickstart hosts the runtime in a Next.js
route handler and uses a relative runtimeUrl of /api/copilotkit, which only
resolves because Next.js serves the app and the runtime from one origin. A
Vite or CRA app has neither a server nor a shared origin, so that step had
nowhere to land.

Add a React SPA page covering only that difference: a standalone Node runtime
server, the absolute runtimeUrl it requires, and cors: true -- which is off by
default on createCopilotNodeListener, unlike the Express and Hono adapters.
Everything else links back to the root React docs rather than restating them.

Registering react-spa in the frontend registry is what makes /react-spa
resolve; route resolution is gated on isFrontendId, so an unregistered page
404s. Vue and React Native are the precedent: registered, single-page, no
namespaced subtree. The existing registry-driven guard in frontend-options
already covers the new page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 11:52:48 -05:00
Mike Ryan e5c902aeea fix: align docs auth labels with product name 2026-08-24 09:38:15 -07:00
Mike Ryan db88826432 chore: rename Enterprise Intelligence product copy 2026-08-24 09:38:15 -07:00
Alem Tuzlak efd71cf8e1 style(web-inspector): name the failure on the launcher itself (#6656)
## What does this PR do?

Stacked on top of #6646. That change gives the launcher a red dot when
the app's wiring is broken; this one makes the launcher **say what
broke**, once per outage, and then go quiet again.

The beat fires as it does today. Immediately afterwards the launcher
opens sideways into a short pill carrying the failure's name, holds long
enough to be read, and closes back to the plain mark with its dot.
Nothing remains behind: the dot keeps carrying the state, the pill
carries only the moment.

### Why a pill at all

Two reasons, and the second is the ticket's origin. A dot says
*something* is wrong without saying *what*, so a connection failure and
a refused thread list — two problems you would check in completely
different places — look identical from outside. And the report this work
started from was a user looking at her own application and asking
whether the button used to look different, because she could not tell
what it was. A 36-pixel mark with a 6-pixel dot on it is not
self-explaining. Once per outage, the launcher now names both the
problem and itself.

### The gesture

Four phases in series, each a named constant in one place: beat 1500ms,
open 250ms, hold 2500ms, close 250ms.

The whole gesture occupies the **existing single pending-beat slot** for
its full duration, so this introduces no new scheduling concept — the
third deferral reason already read "another beat is running", and a
gesture is a longer beat. An announcement beat falling inside that
window is deferred exactly as any of the other three reasons would defer
it. If the failure resolves mid-gesture the pill closes early; the beat
is left to finish, because a beat asserts nothing.

### The reveal is a clip, not a resize

The pill is laid out at full width and revealed by animating a
**rounded** rectangular clip. Nothing is scaled and nothing is resized.

That matters because of a contract this component already carried: **no
property that forces a layout on every frame**. Animating width fails it
— the browser recomputes layout sixty times a second on someone else's
page. A clip does not: the geometry is constant and only the visible
region changes. The contract is therefore **restated rather than
broken** — it was never "only opacity and transform", that was the
accurate list at the time; the rule is the layout guarantee, and a
rectangular clip satisfies it. The test that guards it now resolves to
`["clip-path", "opacity", "transform"]`.

The `round` component is not cosmetic. Without it the revealing edge is
a straight vertical line sweeping sideways, which reads as a wipe across
a stationary pill. With it the edge is the capsule's own cap travelling
outward, which reads as the launcher opening.

Two alternatives were built side by side and rejected. Animating width
produces an end state indistinguishable from the clip and gives up the
guarantee for nothing. Scaling horizontally squashes the mark itself —
not merely the rounded ends, as first assumed — so the logo would need
counter-scaling too, at which point the dot and the halo become
ellipses.

### Where it opens, and where it does not

The launcher is always anchored to the top-right corner, so the natural
direction is leftwards. But it is draggable and its position persists,
and it can be dragged to within a margin of the left edge — where a
leftward pill needs far more room than exists, permanently, for that
reader. So the direction is chosen from available room at the start of
the gesture: left if it fits, otherwise right, and if neither side has
room **no pill opens at all**. The dot and the beat still fire, so the
signal is intact and only the label is lost. Constraining where the
reader may drag the control to protect our animation was considered and
rejected: the page is theirs.

### Padding is derived, not literal

The text side of the padding is `size / 2` — the capsule's radius.
Padding is measured from the bounding box, but the first half-height of
that side is the rounded cap, so a literal `14px` put the words *inside*
the curve. And the launcher size is itself a `clamp()` on the viewport,
so no literal could have been right at every size. Half the size lands
the text exactly where the cap ends and the straight edge begins. A test
asserts the expression rather than the value, so a later
"simplification" back to a literal fails.

### One surface, one edge

The launcher's surface and edge are now two custom properties declared
once on the wrapper and resolved by both the button and the pill. Before
this, the button carried literals in a brand-override block and the pill
carried its own — exactly the arrangement where someone changes one side
and forgets the other.

The pill's red-tinted border is gone. It read as a second object sitting
behind the mark rather than as the launcher opening. The surface moves
from near-black to a dark grey, which softens the edge against a white
page from 20.5:1 to 16.5:1 — still far above any legibility threshold,
and the dot's separating ring follows the same token automatically.

The launcher's own ring and shadow are deliberately **not** suppressed
while the pill is open. The alternative was looked at and rejected: an
outline that vanishes reads as something being hidden, not as something
growing. The consequence — the mark's outline stays visible inside the
open pill — is accepted.

### Two lines, and a click that means something

The label is a heading and a subline: the failure's name over `Open
Inspector for details`. It stays exactly as tall as the launcher; the
type is 12px over 10.5px and there is still room above and below.

The subline is an instruction, so the pill **takes pointer events while
it is on screen** and a click opens the Inspector exactly as pressing
the launcher does — the same open source, so the telemetry catalogue
does not change. It is deliberately **not focusable**: the launcher is
already a focusable control for the same action, and a second tab stop
for one action would be a regression.

The subline is the one string in this feature that exists nowhere else
in the product; the heading is word-identical to what the panel says.
That exception is deliberate and recorded at the constant.

### Accessibility

A polite live region speaks the failure once per outage, on the same
rising edge as the beat and the pill, so "once per outage" needs no
separate rule. It speaks the **failure class only** — an instruction
delivered through an announcement is not something a screen-reader user
can act on, and it would double the spoken length.

Reduced motion shows the pill with the same words and the same reading
time, without animating it. The halo is held statically, as it already
was.

### Known limitation, carried over

A runtime that dies **mid-session** is out of reach. The handshake runs
once on connect and nothing re-probes, and a transport-level run failure
is reported through the run channel without touching the connection
state. Measured two ways; filed as **OSS-904**, to be fixed in core so
that every reader benefits and this feature needs no change of its own.

## Tests

`packages/web-inspector` goes to **564** tests across 26 files. New
coverage: the two-line content per source, the shared subline, the
height and typography contract, the derived padding on both directions,
the rounded reveal on both stops of both keyframes,
click-opens-Inspector, click landing on the right view per source, click
ending the gesture, phase-gated clickability, the absence of a second
tab stop, the shared surface and edge tokens, and the spoken sentence
excluding the instruction.

Two guards are worth calling out because they exist to fail a plausible
future edit: the padding test asserts the **expression**, not the value;
and the token test asserts the **sharing**, not the colour.

## Notes for the reviewer

Every visual decision here came from building the variants side by side
in a browser and choosing by eye, not from reasoning about CSS. Where an
earlier assumption turned out wrong — the wipe was a missing `round`,
not a technique problem; the padding was measured from the box, not
flush — the correction is in the commit body.

To see it: run the v2 React demo, leave the panel closed, and from the
console point the core at a dead route with
`document.querySelector('cpk-web-inspector').core.setRuntimeUrl('/api/nope')`.
Two seconds later the dot appears, then the beat, then the pill. Click
it and the panel opens on Home. Heal with the original URL. Watch it in
a **focused** window — an unfocused tab defers the gesture by design.

## Related PRs and Issues

- Stacked on #6646
- Refs OSS-903
- OSS-904 — the core-side follow-up for the mid-session limitation

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked
2026-08-24 18:22:58 +02:00
Ben Taylor 0943c5196e fix(runtime): make published declarations resolvable for consumers (#6674)
Closes the remaining half of OSS-899.

PR #6644 removed the `require("reflect-metadata")` banner from the
published
`.d.cts` files, taking a bare strict-mode `import { CopilotRuntime }`
from 81
errors to 10. This takes it to **1**, and that one is not ours.

## Result

Measured by overlaying the built `dist` onto a real consumer install of
1.69.0
and typechecking a bare import under `strict` + `skipLibCheck: false`:

| entry point | before | after |
| -- | --: | --: |
| `@copilotkit/runtime` | 10 | **1** |
| `/v2` | 1 | **1** |
| `/v2/express` | 1 | **1** |
| `/v2/hono` | 1 | **1** |
| `/v2/node` | 1 | **1** |
| `/langgraph` | 7 | **6** |

The single remaining error on every entry point is the same one, and it
is in a
third-party package — see "Not fixed here" below.

## The gate

`scripts/validate-dts-imports.ts` is the companion to
`validate-dts-ambient.ts`.
That one checks the *shape* of published declarations; this one checks
what they
*reach for*, against a single invariant:

> Every module a published `.d.ts` imports must be resolvable by someone
who
> installed this package and nothing else.

It flags devDependencies, optional peers, dependencies whose types live
in a
devDependency `@types/*`, relative imports of JS-only bundler chunks,
and an
explicit ban on `graphql-yoga`. It started red on 18 violations and is
now clean
on all 175 declaration files. Wired into the existing `check-dts`
target, so it
runs in `check:packages` and `static_quality.yml`.

It caught two problems the typecheck probe missed, because they happened
to
hoist in that particular install: `@whatwg-node/server` and
`@langchain/langgraph-sdk`.

## The fixes

- **`graphql-yoga` (5× TS2416)** — `GraphQLContext` was
`YogaInitialContext & {...}`.
That one reference pulled the Yoga barrel into every consumer program,
and with
it `lru-cache@10`, whose `implements Map` clause is what actually
errors.
Declared locally instead; structurally identical, so a real Yoga context
still
satisfies it. Nothing in this package serves GraphQL any more — every v1
  integration entry point delegates to the v2 Hono endpoint.
- **`@copilotkit/channels` (1× TS7016)** — a devDependency and a pure
re-export of
`channels-core`, so tsdown inlined its prebuilt declarations plus a
rolldown
helper chunk that ships JavaScript only. Types now come from
`channels-core`,
  which is a real dependency and stays external.
- **`cors` / `express` (1× TS7016)** — public declarations use their
types, but both
  `@types` packages were devDependencies. Promoted to dependencies.
- **`@anthropic-ai/sdk` / `groq-sdk` (2× TS2307)** — optional peers
named in exported
signatures. Params and getters now use `SdkClientLike`, a structural
type a real
client satisfies; the SDK types stay at the two call sites that need
them, inside
  function bodies where they are never emitted.
- **`@langchain/langgraph-sdk`** — was deep-importing
`dist/types.messages`, an
internal file of an optional peer. Now derived from the base class
signature in
  `@ag-ui/langgraph`, a real dependency.
- **`@whatwg-node/server`** — an `export type {}` workaround for
microsoft/TypeScript#42873 whose own comment said it was waiting on
TypeScript
  5.5. We are on 5.9.2 and it typechecks without it.

## Not fixed here

**1× TS2694** — `@copilotkit/license-verifier@0.5.0` references zod's
`core`
namespace, which only exists in zod v4; this package pins `zod ^3.23.3`.
It is an
external package (0.5.0 is the latest published), so the fix is either a
license-verifier release or migrating this package to zod v4. Both are
larger than
this PR and deserve their own ticket.

Also unchanged: the 6 remaining `/langgraph` errors, all inside
`@ag-ui/langgraph`,
`@langchain/core`, and `@langchain/langgraph-sdk`. The `TS2416` on `run`
is present
identically in pristine 1.69.0 — it is downstream of
`@ag-ui/langgraph`'s own
unresolvable deep imports, not something this branch introduced.

## Reviewer note

The `SdkClientLike` swap narrows `adapter.anthropic` and `adapter.groq`
for anyone
reaching past `baseURL` and `apiKey`. Both adapters are v1-deprecated,
and the
alternative is a type error for every consumer who does not install
those optional
peers — but it is a public-surface change and worth a second opinion.

## Verification

- `nx test runtime` — 2078 tests, 144 files, all passing
- `nx build runtime` + `npm run check-dts` — both validators clean on
175 files
- `tsc --noEmit` on the package — clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-24 11:21:34 -05:00
Ben Taylor 105ac3cfb9 fix(core): keep exactly one tool result per tool call across message snapshots (#6294)
# fix(core): keep exactly one tool result per tool call across message
snapshots

## Summary

When an agent emits a MESSAGES_SNAPSHOT, AG-UI merges it by message id
and can drop a tool message that TOOL_CALL_RESULT created. The next turn
then sends an assistant tool call without its paired result, which
providers reject.

This PR records observed tool results for the current input and repairs
history through AG-UI's returned-messages mutation channel. It keeps one
tool message per toolCallId and composes with current main's first-seen
message provenance.

## Root cause

@ag-ui/client applies events against its own cloned messages array.
TOOL_CALL_RESULT creates a tool message and inserts it after its
assistant owner. A later MESSAGES_SNAPSHOT is a replace-by-id merge, so
a missing tool entry can remove that result. packages/core had no record
of the result event, so there was nothing to restore it from.

## What changed

- StateManager records one ToolCallResultEvent per toolCallId on the
current RunAgentInput. Events keep flowing through AG-UI's normal path.
- At RUN_FINISHED, RUN_ERROR, and onRunFailed, reconciliation returns a
fresh message array only when a repair is needed. AG-UI applies it
through its normal mutation chain.
- Pending results are released at each finished server-run boundary and
at finalization, so a later server run under one input cannot resurrect
a result removed by its snapshot.
- The "Forwarded to client" sentinel classifier now lives in one
internal module used by StateManager and run-handler.ts.

## The reconciliation rule

toolCallId is the decisive identity:

- No assistant owner for the call: do nothing.
- A real tool message already exists for the call under any message id:
keep one result and remove duplicate same-call representations.
- Only placeholders exist: promote one to the canonical result and drop
the rest.
- Nothing exists: insert the result after its assistant owner and its
contiguous tool messages.

## On the LangGraph duplicate

LangGraph can represent one result with different streamed and
checkpoint message ids. The regression fixture keeps the streamed result
before the snapshot and places both representations in the snapshot. The
final history and next-turn input contain one result for that
toolCallId.

Two tool messages for one toolCallId are one malformed history class.
Keying reconciliation by toolCallId makes that duplicate unrepresentable
while preserving normal event delivery.

## What this does not do

- No direct agent.messages mutation, setMessages from a subscriber, or
stopPropagation. AG-UI remains the owner of message application,
ordering, and publication.
- Reconciliation does not infer or rewrite run identity. Current main's
event-derived run identity and first-seen snapshot provenance remain
intact.
- No message-to-run association is performed inside reconciliation.
- No public API change, export, version bump, or changeset.

## Relationship to #3884

Related to #3884, but not marked as closing it. The event sequence in
that issue has no MESSAGES_SNAPSHOT, and it already produces a correct
turn-2 history on current main. Snapshot-dropped results are a real bug
worth fixing independently, while the reporter's case still needs a raw
event trace.

## Test plan

All cases drive CopilotKitCore.runAgent() against real AbstractAgent
subclasses.

- Two-turn reproduction: a snapshot omits the result, and the next turn
receives exactly one result.
- LangGraph shape: differing message ids under one toolCallId produce
one surviving tool message in real event-before-snapshot order.
- Repeated server runs under one input do not resurrect a result removed
by a later snapshot.
- Terminal mutation, normal result propagation, duplicate results,
placeholders, ownerless results, ordering, RUN_ERROR, local failure, and
run ownership remain covered.
- The focused core tests pass 32/32. Core typecheck, build, formatting,
lint, and diff checks pass. CI checkboxes remain for GitHub.
2026-08-24 11:19:31 -05:00
Lukas Moschitz aa91d059ef test(web-inspector): cover the Learning error source, and quiet its lint
The `memory` latch shipped with no test at all, and the one test that
mentions it still explained the old behaviour: "the Memory store is never
even subscribed from here". That is no longer true -- opening the view
does subscribe it -- so the test passed while teaching the wrong thing.
The comment now says what actually holds: without an Intelligence context
the store never fetches, so there is nothing to fail.

Three tests: a refused load names itself and lands on Learning, a refused
load arms nothing while the view has never been opened, and a resolved one
clears itself. The middle one is the important one -- it is the constraint
that makes `memory` an unread event rather than a state the launcher could
mirror from a cold start.

The harness needed two things. `intelligence` is now settable, because the
view is a locked teaser without it and the latch is unreachable. And the
memory store stub replaces its state rather than mutating it: the store's
selectors memoize on the state they were handed, so an in-place edit
reports the stale value forever and looks exactly like a signal that never
fires.

Also hoists the selector-escape pattern so `no-control-regex` can be
suppressed on it with a reason -- the control range is the point, since an
attribute selector escapes those too. Lint returns to its 5359 baseline.
2026-08-24 18:16:29 +02:00
Lukas Moschitz 1d1a319b1f docs(web-inspector): record why the wiring latch arms without a window
Arming used to wait 2000ms so a blip produced neither dot nor beat. The
window is gone and that is deliberate, but nothing said so, and the tests
now assert immediate arming as the contract -- so the next reader would
find no trace of the trade-off, only the behaviour.

What the note holds: `threads` can flap and plays a whole gesture for a
blip, bounded by the single pending-beat slot to one gesture per gesture
length; `connection` cannot flap on its own because nothing retries the
handshake; and the `connecting` branch already covers the retry case if a
fix for the mid-session gap adds polling. Mirrored on OSS-904, which is
where such a fix would land.
2026-08-24 18:05:28 +02:00
Ben Taylor e8cbd61ec9 docs(integrations): wire quickstart runtimes to Intelligence (closes OSS-932) (#6671)
## What does this PR do?

Twelve integration quickstarts opened by telling the reader to create a
free account and get a license key, then — seven steps later — showed a
runtime constructed like this:

```ts
const runtime = new CopilotRuntime({
  agents: { ... },
  runner: new InMemoryAgentRunner(),
});
```

`runner` and `intelligence` are mutually exclusive by construction
(`CopilotSseRuntimeOptions` declares `intelligence?: undefined`), so the
key provisioned in step 1 could not be consumed. Threads read "locked",
and nothing on the page indicated a choice had been made. Worse, **none
of the twelve pages ever named `INTELLIGENCE_API_KEY`** — after step 1
the key was never mentioned again.

Each page now:

1. Constructs the runtime with `intelligence: new
CopilotKitIntelligence({ apiKey: process.env.INTELLIGENCE_API_KEY! })`
and `identifyUser`.
2. Names `INTELLIGENCE_API_KEY` in a `.env.local` fence at the point the
route reads it.
3. Links `/premium/connect-your-runtime` — a good page that had **zero**
inbound links from any quickstart — from a callout that also documents
the in-memory opt-out.

The in-memory runner stays available and is labelled as the opt-out. It
was already the default (`super(options, options.runner ?? new
InMemoryAgentRunner())`), so passing it explicitly only ever added the
steer.

Also adds the required `name` field to the `identifyUser` snippets in
`connect-your-runtime.mdx` and the runtime skill's `agent-runners.md`.
Both omitted it, so copying either was a type error:

```
Property 'name' is missing in type '{ id: string; }' but required in type 'CopilotRuntimeUser'
```

### Scope

Not "every page mentioning `InMemoryAgentRunner`" — that is 37 files,
and most are legitimate (`backend/agent-runner.mdx` is *about* runners;
an AgentCore host is a Lambda and cannot host the Intelligence socket at
all). The scope is **pages that provision a license key and then show a
runtime that cannot consume it**, which partitions those 37 cleanly into
12 in-scope and 25 untouched.

Note that includes `docs/agent-spec/quickstart.mdx`, which is
byte-identical to `docs/integrations/agent-spec/quickstart.mdx`. Both
are fixed here; retiring the duplication is filed separately.

## Verification

Nine of the twelve runtime fences are `doctest="component"` gated, so
they are really compiled in CI against a pinned
`@copilotkit/runtime@1.68.3`. The new constructor was proven there
before being applied to twelve files.

- Extracted the docs tree with `scripts/doc-tests/extract.ts` → 16
snippets. **All 15 `component` snippets typecheck clean**, covering 9 of
the 12 edited pages plus 6 untouched pages (so it also rules out
collateral damage).
- Confirmed the gate can go red: removing `name` from an edited snippet
reproduces exactly the error above, so the pass above is meaningful.
- The 3 ungated fences (mastra ×1, langgraph ×2) were typechecked by
hand, since CI will not.
- `src/lib/__tests__/intelligence-wiring-docs.test.ts` passes 3/3. That
suite guards this defect class, and **this change brings these pages
into its coverage for the first time — 8 → 20 pages**, because a page
only enters the suite once it actually configures `intelligence`.
- `pnpm check:intelligence-env-names` passes. `pnpm check:plugin-skills`
initially failed (`packages/runtime/skills/**` is mirrored to
`skills/**`); synced, both copies included.

## Related PRs and Issues

- Closes OSS-932
- Part of the OSS-923 split, alongside OSS-933 (runtime silently drops
`runner` alongside `intelligence`)
- OSS-935 — the byte-identical agent-spec duplication
- OSS-936 — the Mastra fence's `getLocalAgents({ mastra })` does not
compile against `@ag-ui/mastra@1.1.2`. **Pre-existing on
`origin/main`**, reproduced on the unmodified snippet, not addressed
here

Worth noting for reviewers: the Mastra runtime fence cannot be
doctest-gated as written (it imports the `@/mastra` path alias, which
does not resolve in the extracted directory), and `@ag-ui/mastra` is in
no `doctest.json`. It is the least-verified page in the set on two
independent axes, and it accumulated two unrelated defects. That may
explain why only the Mastra evaluation cell surfaced this.

## 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-24 10:58:34 -05:00
Mike Ryan 0771fb98dc docs: fix Anthropic thinking snippets to adaptive thinking (#6606)
## What & why

Every Anthropic extended-thinking snippet in the docs was stale, and the
AI SDK one was outright broken. Surfaced by @hejtmii in #2191, who
pointed out the deprecated `{ type: "enabled", budgetTokens: 10000 }`
form. Investigating it turned up two further defects he didn't mention.

Three distinct problems:

1. **The AI SDK snippet never enabled thinking at all.** It passed the
options as a second argument to `anthropic(...)`, but
`AnthropicProvider` declares `(modelId: AnthropicMessagesModelId):
LanguageModelV3` — one parameter. Provider options belong in
`providerOptions.anthropic` on `streamText`. This was a hard TypeScript
error, so anyone copying it got a compile failure, and the shape was
silently ignored by the provider.
2. **The TanStack snippet used a model id and a key name the adapter no
longer accepts.** `claude-sonnet-4` is not in `@tanstack/ai-anthropic`'s
model union, and that package spells the field `budget_tokens`, not
`budgetTokens`.
3. **All five sites used the deprecated thinking form.** To be precise
about blast radius: these snippets pinned Sonnet 4 / 4.5, where `{ type:
"enabled", budgetTokens }` is still accepted, so they were not returning
400s as written. The form is deprecated from Claude 4.6 and rejected
outright on 4.7+, so the cost was teaching a pattern that breaks the
moment a reader bumps the model — which is exactly what a docs reader
does.

## Changes

- `providerOptions.anthropic` for the AI SDK snippet, with `thinking: {
type: "adaptive" }` + `effort`.
- `thinking: { type: "adaptive", display: "summarized" }` for the
TanStack snippet.
- `advanced-configuration.mdx` modernized (it was already structurally
correct — just the deprecated shape).
- Models pinned to `claude-sonnet-4-6` across all three files.
- New callout on the reasoning-visibility trap (see below).
- Cross-link from the custom-agent reasoning section to the BuiltInAgent
`providerOptions` page — #2191's reporter went looking for a
BuiltInAgent thinking example and didn't find the one that exists.

### Why `claude-sonnet-4-6` and not a newer model

This is deliberate, not staleness. From Opus 4.7 onward (including
Sonnet 5 and Opus 5) Anthropic defaults thinking `display` to
`"omitted"`, and `@ai-sdk/anthropic@3.0.49` exposes no `display` option
(its only `display*` fields are computer-use tool params). On a newer
model these snippets would emit `REASONING_*` events carrying **no
text** — silently defeating the purpose of a page about reasoning.
Sonnet 4.6 defaults to `"summarized"`. The added callout documents the
trade-off so a future model bump is a conscious choice.

## Testing

No runtime code changed, so verification is that the new snippets
compile against the real installed providers and the old ones do not.
Typechecked with the repo's own `@ai-sdk/anthropic@3.0.49`, and
`@tanstack/ai-anthropic@0.16.9` installed fresh.

**AI SDK — new form passes:**
```
$ npx tsc --noEmit --skipLibCheck --strict ... __snippet_check.ts
EXIT=0
```

**AI SDK — old form fails (mutation check, confirms the defect was
real):**
```
$ npx tsc --noEmit --skipLibCheck --strict ... __snippet_check_old.ts
__snippet_check_old.ts(7,41): error TS2554: Expected 1 arguments, but got 2.
```

**TanStack — new form passes, old form fails:**
```
===== NEW =====
(no output)
===== OLD =====
old.ts(6,28): error TS2345: Argument of type '"claude-sonnet-4"' is not assignable to parameter of type
  '"claude-fable-5" | "claude-haiku-4-5" | "claude-opus-4-1" | "claude-opus-4-5" | "claude-opus-4-6" |
   "claude-opus-4-7" | "claude-opus-4-8" | "claude-opus-5" | "claude-opus-5-fast" | "claude-sonnet-4-5" |
   "claude-sonnet-4-6" | "claude-sonnet-5"'.
old.ts(8,33): error TS2322: Type '"enabled"' is not assignable to type '"adaptive" | "disabled"'.
```

Wire-shape confirmed by reading the provider's compiled request builder:
`thinking: { type: "adaptive" }` is forwarded as-is, and `effort` is
emitted as `output_config: { effort }` — the correct modern location.
`effort` values were checked against each provider's own union
(`@ai-sdk/anthropic` has no `xhigh`; `claude-sonnet-4-6` in TanStack
resolves to `AnthropicAdaptiveThinkingOptions`, which has no `effort`,
so none is shown there).

MDX integrity checked: `Tabs`/`Tab`/`Callout` tags and code fences all
balance in every edited file. Link target verified —
`integrations/built-in-agent/meta.json` sets `"root": true`, which
strips that prefix, so `/advanced-configuration` is the correct absolute
URL (matching how `server-tools.mdx` and the section index already link
to it).

Also confirmed no `budgetTokens`/`budget_tokens` occurrences remain
anywhere under `showcase/shell-docs/src/content/` except the explanatory
comment.

## Scope

Docs only. Three things deliberately **not** included:

1. **`BuiltInAgent`'s `ModelString` union still lists only pre-4.6
Anthropic models.** It is an open union (`| (string & {})`), so the new
snippets typecheck without touching it, but refreshing that list is a
separate code change.
2.
**`showcase/integrations/claude-sdk-typescript/src/agent_server.ts:1125`
still sends `thinking: { type: "enabled", budget_tokens: 2048 }`.** That
cell defaults to `claude-sonnet-4-6`, where the form is
deprecated-but-functional, so it works today and would only break if
`CLAUDE_MODEL` were pointed at 4.7+. It is app code under `showcase/`
with its own rules, so it belongs in its own PR.
3. **The rest of #2191.** Replaying `thinking`/`signature` blocks on
follow-up turns is a genuine runtime gap —
`convertMessagesToVercelAISDKMessages` has no `reasoning`-role branch,
and the signature never leaves the runtime — and needs its own PR.
Leaving #2191 open.

Refs #2191

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-24 08:44:21 -07:00
Benjamin Taylor cbe6a81ff4 fix(runtime): keep optional peer SDK types off the published surface
@anthropic-ai/sdk and groq-sdk are optional peers, so a consumer who never uses
those adapters does not install them -- yet both were named in exported
signatures, which is a TS2307 on a bare `import { CopilotRuntime }`. The
LangGraph agent had a third case, and a worse one: it deep-imported
@langchain/langgraph-sdk/dist/types.messages, an internal file of an optional
peer.

The adapter params and getters now use SdkClientLike, a structural type a real
Anthropic or Groq instance satisfies. The SDK types stay for the two call sites
that need them, inside function bodies where they are never emitted. The
LangGraph message type is derived from the base class signature in
@ag-ui/langgraph, which is a real dependency.

Narrows adapter.anthropic and adapter.groq for anyone reaching past baseURL and
apiKey. Both adapters are v1-deprecated and the alternative is a type error for
every consumer.
2026-08-24 10:39:37 -05:00
Benjamin Taylor 1d9f19713a fix(runtime): ship the @types packages our public declarations depend on
dist/v2/runtime/endpoints/express.d.cts imports types from `cors` and `express`.
Neither ships its own declarations, and both @types packages were devDependencies,
so whether a consumer resolved them came down to whether something else in their
tree happened to hoist them.

Moving them to dependencies makes the published types self-contained.
2026-08-24 10:38:17 -05:00
Benjamin Taylor 3755a227d5 fix(runtime): drop the obsolete @whatwg-node/server type workaround
`export type {} from "@whatwg-node/server"` worked around microsoft/TypeScript#42873
and its own comment says it was waiting on the TypeScript 5.5 stable release.
The repo is on 5.9.2 and typechecks without it.

It was reaching a devDependency, so it emitted an import of a module consumers
never install.
2026-08-24 10:37:51 -05:00
Benjamin Taylor d81e7db63e fix(runtime): take channel types from channels-core, not the channels shim
@copilotkit/channels is a devDependency and a pure re-export of
@copilotkit/channels-core, so tsdown treated it as bundleable and inlined its
prebuilt declarations into dist/channels/dist/index.d.cts -- along with a
rolldown helper chunk that ships JavaScript only. Consumers got a TS7016 for a
file they cannot see.

channels-core is a real dependency and stays external, so importing the types
from there drops the inlined copy entirely.
2026-08-24 10:37:48 -05:00
Benjamin Taylor 2fa1bea289 fix(runtime): stop shipping graphql-yoga types to consumers
GraphQLContext was defined as `YogaInitialContext & {...}`. That single type
reference pulled the whole graphql-yoga barrel into every consumer's program,
and with it lru-cache@10, whose `implements Map` clause costs five TS2416
errors under strict + skipLibCheck: false.

Nothing in this package serves GraphQL any more -- every v1 integration entry
point delegates to the v2 Hono endpoint -- so the type is declared locally
instead. It is structurally identical, so a real Yoga context still satisfies it.
2026-08-24 10:36:46 -05:00
Benjamin Taylor 996ac76a24 test(runtime): gate published declarations on consumer-resolvable imports
OSS-899 shipped 81 strict-mode errors to consumers because nothing checked what
the published .d.cts files reach for. validate-dts-ambient.ts checks their shape;
this checks their imports against the one thing that matters -- whether someone
who installed this package and nothing else can resolve them.

Flags devDependencies, optional peers, dependencies whose types live in a
devDependency @types package, relative imports of JS-only bundler chunks, and an
explicit ban on graphql-yoga, whose types drag lru-cache@10 into every consumer
program. Currently red on 18 real violations; the fixes follow.
2026-08-24 10:27:25 -05:00
Lukas Moschitz ebbe70e567 fix(web-inspector): only claim a highlight when there is one to find
The landing card ended in "The failed run event is highlighted below."
unconditionally, but the error carries no guarantee that such an item
exists. A code that reaches `run` through the catch-all without a run of
its own -- a locked thread, an agent that was never registered -- lands on
an empty AG-UI Events with the card still pointing at nothing, and
`applyEventErrorLanding` bails out silently rather than telling it. Same
for a tool error that arrives without a call id.

Split the copy by what it can promise: `advice` is about the reader's next
move and always holds, `highlight` is a claim about this view and renders
only once the item is there. Sending someone to look for something absent
is worse than saying nothing.

The mapping itself is unchanged. Whether a locked thread should be called
a failed run at all is a separate question for its author.
2026-08-24 17:15:41 +02:00
Benjamin Taylor deb8b5c579 Merge branch 'main' into ben1/docs-anthropic-adaptive-thinking
Resolves conflicts in the two custom-agent docs copies. Main's "docs: update
Anthropic model references to Opus 4.8" (518feae6cd) independently bumped the
Anthropic thinking snippets to `claude-sonnet-4-6` -- the same model id this
branch picked -- so the overlap was textual, not a disagreement.

Kept this branch's side in both files: it already carries that model id and
additionally moves the AI SDK options into `providerOptions.anthropic` and
switches to `{ type: "adaptive" }` with `effort`. Main's bumps on the
surrounding property-forwarding examples merge in unchanged.
2026-08-24 09:40:00 -05:00
Lukas Moschitz 4a2b1a3a6e fix(examples): type the react-router lab's error handler and drop an undeclared import
Two type errors the example could not see. It has no check-types target,
and the import is type-only, so esbuild erases it and the build passes.

- `CopilotKitCoreFriendsAccess` came from `@copilotkit/core`, which the
  example does not depend on. `react-core/v2` re-exports it.
- `onError` also accepts React's DOM error handler, so the parameter is a
  union and reading `.error` / `.context` off it is not allowed. Narrow on
  the CopilotKit shape first; a synthetic DOM event has nothing to report.

Example type errors go from 9 to 3. The three left are older: two react
-router codegen paths and one model name.
2026-08-24 16:34:18 +02:00
Lukas Moschitz a8bcfebf02 fix(web-inspector): wait for the thread failure before asserting its landing
The thread-list-error route asserts that the first open lands on Threads
rather than Home. That only holds once the list request has been refused,
and the request is still in flight at that point: the helper waits two
microtask turns and a render, never the fetch. On a loaded machine the
fetch loses the race, the launcher carries no signal yet, and the open
lands on Home.

Green locally, red on all six CI matrix combinations. Reproduced here by
delaying the list fetch, which fails the assertion at 300ms and passes it
with this change at 1200ms.
2026-08-24 16:33:23 +02:00
Alem Tuzlak 7792347674 fix(core): narrow assistant messages in inspector test 2026-08-24 16:10:34 +02:00
Benjamin Taylor 59b0476e67 docs(integrations): wire quickstart runtimes to Intelligence (closes OSS-932)
Twelve quickstarts provisioned a license key in step 1 and then showed a
runtime constructed with `runner: new InMemoryAgentRunner()` — an option
that is mutually exclusive with `intelligence`, so the key was never read.
Threads showed "locked" and nothing indicated a choice had been made.

Each of those pages now constructs the runtime with `intelligence` and
`identifyUser`, names `INTELLIGENCE_API_KEY` where the route reads it, and
links /premium/connect-your-runtime — which had no inbound link from any
quickstart. The in-memory runner stays available as a labelled opt-out;
it was already the default, so passing it explicitly only added the steer.

Also adds the required `name` field to the `identifyUser` snippets in
connect-your-runtime.mdx and the runtime skill's agent-runners reference.
Both omitted it, so copying either was a type error.

Scope is every page that provisions a key and then shows a runtime that
cannot consume it. Pages that legitimately document the in-memory runner
(backend/agent-runner, deploy/agentcore) are unchanged.

Verified: all 15 doctest `component` snippets typecheck against the pinned
@copilotkit/runtime@1.68.3, and the gate goes red when `name` is removed.
2026-08-24 08:48:59 -05:00
Alem Tuzlak 4bd576d4ea Merge branch 'main' into alem/oss-874-inspector-event-snippets 2026-08-24 15:48:38 +02:00
Alem Tuzlak eb9d8d5a84 feat(web-inspector): land run and tool errors on the failed item (#6669)
Click the red Inspector launcher after a run or a tool fails. Inspector
opens the pane that explains that error and highlights the failed item.

## What does this PR do?

The launcher already turns red for CopilotKit errors (see #6656). This
PR takes the click to the explaining view:

- Tool handler / missing tool: Agent pane. The banner names the agent,
the tool, and the error. The failed tool card is highlighted.
- Agent run / `RUN_ERROR`: AG-UI Events. The banner names the agent and
the error. The `RUN_ERROR` event is highlighted and expanded.
- Click the in-panel banner again. Inspector returns to that same item.

The beat before the error pill is 400ms (was 1500ms). Failed tool titles
use dark text on a light rose card so "crash failed" stays readable.

The react-router example can produce these errors in chat:

- `crash the tool` calls a real `crash` frontend tool that throws.
- `crash the run` makes the server emit `RUN_ERROR` after `RUN_STARTED`.

Refresh no longer keeps a fake Threads 503 from the Break threads
cookie.

## Related PRs and Issues

- Stacked on #6656 (`lukas/oss-903-error-pill`)
- Follows #6646
- Linear:
[OSS-903](https://linear.app/copilotkit/issue/OSS-903/presentation-wire-errors-notifications-to-emanate-from-and-open-the)

## Checklist

- [ ] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] 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)

## Testing

**Commands run**

1. `pnpm nx test web-inspector` — 26/26 passed on the inspector commit.
2. Example/docs commit ran lefthook (lint, lockfile sync, env-name
check). Passed.
3. `pnpm install --frozen-lockfile --ignore-scripts` — passed after the
lockfile specifier fix.
4. I did not run `pnpm test:pr`.

**Manual test**

1. Run `pnpm --filter react-router-example dev`. Open
http://localhost:5173/.
2. Send `crash the tool`. Click the red launcher. Agent pane must show
the failed `crash` tool, not "No agent selected".
3. Send `crash the run`. Click the red launcher. AG-UI Events must
highlight `RUN_ERROR`.
4. Click Break threads, then refresh. Threads must load. Do not keep
"Failed to load threads."

**How this PR makes testing easy**

`packages/web-inspector/src/__tests__/launcher-error-signal.spec.ts`
covers landing. `examples/v2/react-router` is a live lab for the same
paths.

## Risk / rollback

Inspector click-to-pane behavior changes for run and tool errors. Revert
this PR to restore the previous landing. The example lab is local-only.
2026-08-24 15:46:22 +02:00
Alem Tuzlak 06f1b23992 fix(examples): match the zod lockfile specifier to the root override
Root pnpm.overrides.zod is >=3.22.3. Frozen CI install requires that specifier.
2026-08-24 15:36:55 +02:00
Benjamin Taylor 4a50c4c97c fix(runtime): reject runner passed alongside intelligence (closes OSS-933)
`CopilotIntelligenceRuntime` hardcodes `IntelligenceAgentRunner` into its
`super()` call, so a caller-supplied `runner` could never be honored. The type
forbids it — `runner` is declared only on `CopilotSseRuntimeOptions` — but that
is an excess-property check: a JS, `as any`, or non-literal caller passing
`{ intelligence, runner }` reached the constructor and had `runner` silently
dropped with no diagnostic.

Add the runtime guard, mirroring the `channels` guard already in
`CopilotSseRuntime` for exactly this reason. Explicit `runner: undefined` still
constructs, matching how the sibling `identifyUser` / `channels` / `memory`
guards treat undefined.

This also resolves a contradiction inside the shipped runtime skill:
`SKILL.md:87` already asserted the rejection happens at construction, while
`references/agent-runners.md` correctly described the silent drop. The guard
makes SKILL.md true; agent-runners.md is updated to describe the throw and to
cite line numbers that match the current file.
2026-08-24 08:31:33 -05:00
Alem Tuzlak f988af0915 chore(examples): add zod to the lockfile
The crash tool uses useFrontendTool with a zod schema.
2026-08-24 15:23:29 +02:00
Alem Tuzlak 0c6f52fc41 feat(examples): add crash-the-tool and crash-the-run inspector lab
Add chat phrases and lab buttons that fail a frontend tool or emit RUN_ERROR.
Clear the Break threads cookie on load so a refresh does not keep a fake
thread-list failure.
2026-08-24 15:22:07 +02:00
Alem Tuzlak c226e50259 feat(web-inspector): land run and tool errors on the failed item
Show agent, tool, and error on Inspector banners. Click the launcher to open the failed tool call or RUN_ERROR. Shorten the error pill wait to 400ms. Use dark gray titles on the rose tool-error card so the name stays readable.
2026-08-24 15:12:35 +02:00
Benjamin Taylor 7f068c6eed docs(mastra): resolve remote agents per request, and correct two wrong claims
Self-review of the previous commit found three defects, two of them factual
errors I asserted without checking.

1. The promise form can kill the process. I documented
   `agents: MastraAgent.getRemoteAgents({ ... })` and described its failure mode
   as "the rejection is cached and every later request fails". That understated
   it: the call starts at module load with nothing awaiting it, so an agent
   server that is not up yet produces an *unhandled* rejection and Node
   terminates. Both files now lead with the factory form, which has no such
   window — nothing runs until a request arrives, a failure is a 500, and the
   next request retries.

2. The tsconfig warning named the wrong key. `next dev` does not overwrite
   `moduleResolution: "NodeNext"` — `nodenext` is in Next's accepted set for
   both `module` and `moduleResolution`. What it actually does at its own root
   is force `esModuleInterop`, `isolatedModules`, `resolveJsonModule` and `jsx`,
   set `noEmit: true`, and replace `include`/`exclude`. `noEmit` is the sharp
   one for an agent project that compiles with `tsc`. The advice stands; the
   mechanism is now the verified one.

3. `MASTRA_BASE_URL` was set in the wrong shell. It was exported in the agent's
   terminal, where the frontend cannot see it. It now appears as `.env.local` in
   the Next app, next to the route that reads it. The port note moved to the
   step that starts the agent, and says what `mastra dev` really does: 4111 when
   free, walking up to 4131 when not.

Verified:

    shipped fence, agent down -> up  -> 500, then 200; process survived, no restart
    promise form, agent down        -> node terminated on unhandled rejection
    next build, NodeNext tsconfig   -> 13 keys written; module/moduleResolution untouched
    mastra CLI                      -> serverPort 4111, getPort over 4111..4131, apiPrefix /api
    extracted fence, runner config  -> tsc exit 0; mutation (drop resourceId) -> TS2741
    MDX compile, both files         -> OK

The behavioural checks drove the extracted fence itself, not a paraphrase of it.

Refs OSS-925.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 07:49:52 -05:00
Mark 4c975dad49 feat(showcase): enable LlamaIndex attachments (#6660)
## Summary

- enable the LlamaIndex multimodal Showcase demo
- update the LlamaIndex AG-UI protocol pin and its compatible
core/OpenAI adapter pins
- register the multimodal agent and refresh the integration parity notes

## Verification

- Showcase manifest and route validation passed
- LlamaIndex integration production build passed
- container dependency check passed with core 0.14.24, llms-openai
0.7.10, and protocols-ag-ui 0.4.1
- live local OpenAI smoke passed for both image and PDF attachments

## Known harness issue

The shared D6 image assertion still expects the exact contiguous phrase
`copilotkit logo`; OpenAI returned the semantically equivalent `a logo
for CopilotKit`. The browser validation confirmed one intact image
attachment and a relevant response.
2026-08-24 00:45:24 -07:00