15289 Commits

Author SHA1 Message Date
Tyler Slaton 786f4f525c chore: release monorepo v1.69.2 (#6698)
## Release monorepo v1.69.2

**Scope:** `monorepo` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `monorepo` packages to `1.69.2`
   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.69.2`
   - Creates git tag `monorepo/v1.69.2`
   - 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.69.2
2026-08-25 17:40:24 -07:00
tylerslaton 9629e930d1 chore: release monorepo v1.69.2 2026-08-26 00:18:42 +00:00
Tyler Slaton 2ad03320fe fix(inspector): clarify local action availability (#6696)
## Problem

Event Snippets adds bookmark controls across assistant, thinking,
tool-call, A2UI, and generative UI surfaces, but snippet saving is not
ready to ship.

## Why

The Inspector needs one clear local-development entry point without
exposing unfinished save or replay behavior.

## Fix

Revert the complete Event Snippets feature in one mainline rollback,
removing every snippet button, the Inspector pane, storage/replay
support, framework wiring, and related documentation. Retain the direct
🪁 `View in Inspector (local only)` control with its explicit
local-development hover explanation.
2026-08-25 17:16:52 -07:00
Tyler Slaton b3b339f544 Revert "feat(web-inspector): add Event Snippets and save-as-snippet (#6649)"
This reverts commit ba4260ad66, reversing
changes made to 47c5510b49.
2026-08-26 02:11:19 +02:00
Tyler Slaton dc916484ab fix(inspector): clarify local action availability
fix(inspector): group local developer actions

fix(inspector): refine local tools menu

fix(inspector): use direct local action
2026-08-26 02:11:19 +02:00
Mike Ryan b3c3cb0d7b test(web-inspector): stop the gesture tests racing the beat on real timers (#6693)
## Problem

`packages/web-inspector/src/__tests__/launcher-error-signal.spec.ts` is
failing intermittently on `test / unit`, across unrelated branches and
on `main`. It is currently red on the **v1.69.1 release PR**.

```
FAIL  src/__tests__/launcher-error-signal.spec.ts >
      the whole gesture completes on its own and leaves the resting state behind
AssertionError: expected 'opening' to be 'closed'
```

| When | Branch | Shard | Run |
|---|---|---|---|
| 19:29Z | `release/publish/monorepo/v1.69.1` | Node 24 / React 18 |
[32886136479](https://github.com/CopilotKit/CopilotKit/actions/runs/32886136479)
|
| 14:09Z | `ben1/oss-924-agui-core-058` (#6687) | Node 24 / React 19 |
[32857130830](https://github.com/CopilotKit/CopilotKit/actions/runs/32857130830)
|
| 12:41Z | `lukas/oss-903-presentation-…` | Node 20 / React 18 |
[32848616256](https://github.com/CopilotKit/CopilotKit/actions/runs/32848616256)
|

Neither #6687 (an `@ag-ui/core` version bump) nor the presentation
branch touches gesture timing, and no shard fails consistently — it
follows runner load, not code.

## Cause

Both real-timer tests in this suite asserted a *pre-beat* state 200ms
after breaking the connection:

```ts
const context = await setup({ realTimers: true });
await context.breakConnection();
await context.advance(200);
expect(pillPhase(context.inspector)).toBe("closed");   // ← races the beat
```

Under `realTimers`, `advance(ms)` is a literal `setTimeout(resolve, ms)`
(spec L656–664). The pill's `closed → opening` transition fires at
`ERROR_GESTURE_MS.beat = 400` (`index.ts` L330–339, scheduled at
L18998). So the assertion had a **200ms margin against a 400ms boundary
on a wall clock**. On a loaded runner the 200ms sleep overshoots 400ms,
the beat has already fired, and the phase reads `opening`.

The comment directly above the test already says phase boundaries are
asserted on the fake clock "because real timers would make this suite
slow and flaky" — and then this was a phase-boundary assertion on real
timers.

The same 200-vs-400 race sat in the adjacent test (`pulsing` is true
only for the beat's 400ms), so both are fixed here.

## Fix

Remove the two racy preconditions. Both claims are already pinned
deterministically on the fake clock at spec L411–427, which asserts
`pillPhase === "closed"` **and** `pulsing === true` right after arming,
then walks every phase boundary. These real-timer tests exist only to
show the beat and the gesture run to their end on their own — which the
loops and their closing assertions still prove.

This also matches the idiom the sibling `launcher-signal.spec.ts`
already uses (L667–676): assert at t≈0, then poll for the end.

## Testing

**1. Reproduced the CI failure locally.** Injected a 250ms stall before
the assertion on the unmodified test, simulating a loaded runner (200ms
sleep + 250ms ≈ 450ms > the 400ms beat):

```
FAIL  src/__tests__/launcher-error-signal.spec.ts > the whole gesture completes on its own …
AssertionError: expected 'opening' to be 'closed' // Object.is equality
Expected: "closed"
Received: "opening"
 ❯ src/__tests__/launcher-error-signal.spec.ts:2666:40
```

Byte-for-byte the CI assertion.

**2. The fix survives that same simulation.** With a 700ms stall (well
past the beat) injected into both tests:

```
 ✓ the beat ends and leaves the resting dot behind  750ms
 ✓ the whole gesture completes on its own and leaves the resting state behind  3435ms
 Tests  2 passed | 88 skipped (90)
```

**3. Mutation-checked that the remaining assertions still have teeth.**
Three separate breaks to `src/index.ts`, each caught:

| Mutation | Result |
|---|---|
| Gesture opens but never closes (drop the `closing` phase +
`endGesture`) | `FAIL … AssertionError: expected <span …> to be null` |
| Pill never opens at all (`openPill` returns early) | `FAIL …
AssertionError: expected false to be true` (`sawOpenPill`) |
| Beat never ends (`beat: 400` → `999_999`) | `FAIL … AssertionError:
expected true to be false` (`pulsing`) |

Source restored afterwards; `git status` confirms this PR touches only
the spec file.

**4. Full `@copilotkit/web-inspector` suite:**

```
 Test Files  29 passed (29)
      Tests  625 passed (625)
```

**5. Pre-commit gate** (`test-and-check-packages`: test, publint, attw
across 5 projects + 22 dependencies, incl. `@copilotkit/react-core`,
`@copilotkit/angular`, `@copilotkit/runtime`) passed on the committed
tree.

## Note

`main` also has a second, unrelated flake I did not touch here —
`CopilotChatToolRerenders.e2e.test.tsx > should not re-render a
completed tool call when subsequent text is streamed` (`expected 4 to be
3`), which reddened `main` at `0943c519` ([run
32750372113](https://github.com/CopilotKit/CopilotKit/actions/runs/32750372113)),
a runtime `.d.ts` change that touches no react-core chat code. Different
mechanism, worth its own issue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-25 14:22:03 -07:00
Tyler Slaton cc74d650ca fix(docs): stop shipping stale Intelligence config claims, and gate the dead hosts (refs OSS-961) (#6694)
Follow-up to OSS-961. The reported failure — a `both-oss` conversion
cell that turned a working OSS dashboard into a 502 — was caused by the
packaged runtime skill in **v1.62.2** prescribing `api.copilotkit.ai` /
`realtime.copilotkit.ai`:

```
{ status: 404, body: '', path: '/api/threads' }
```

That signature is exact. `api.copilotkit.ai` and
`api.cloud.copilotkit.ai` resolve to the **same three IPs** — the legacy
Copilot Cloud ALB. With `Host: api.cloud.copilotkit.ai` a listener rule
matches and Express answers; with `Host: api.copilotkit.ai` nothing
matches, so the ALB returns its default action, a 404 with
`content-length: 0`. `realtime.copilotkit.ai` has no DNS record at all.

The hosts themselves were already fixed in **v1.64.0** (`3c1c85937b`
corrected the reference, `3f0bbe4a7b` made `apiUrl`/`wsUrl` optional
with managed defaults), so the ticket's "the fix regressed or never
reached that surface" is not what happened — the failing run was on a
runtime released three and a half weeks before the fix. Bumping the
fixture pins is the actual unblocker and lives in the Intelligence repo,
not here.

What this PR fixes is the residue that survived on `main`, plus the
missing gate.

## Changes

**`skills/copilotkit-debug/references/runtime-debugging.md`** — the line
"Requires `CopilotKitIntelligence` configuration with `apiUrl`, `wsUrl`,
`apiKey`, `tenantId`" carried three errors:
- `apiUrl`/`wsUrl` have been optional with managed defaults since
v1.64.0
- `tenantId` has never existed on `CopilotKitIntelligenceConfig` (it is
a Teams adapter concept)
- there is no org or tenant field to pass at all — the API key's token
format is `cpk-{projectId}_{shortToken}_{longToken}` and app-api
resolves `organizationId` + `projectId` from the key row server-side.
`ThreadSummary.organizationId` is a *response* field, not config.

This mattered because it is the one remaining page that would send a
reader back to hand-filling URLs, which is how the dead host got picked
up in the first place.

**`CopilotKitIntelligence` TSDoc** — the class's own hover docs showed
only `https://intelligence.internal` /
`wss://realtime.intelligence.internal`, so they never named the pair
that serves prod. Now names the managed pair explicitly, and the
override example uses `*.example.com` so it can't be mistaken for a real
host.

**`scripts/validate-intelligence-env-names.ts`** — extended to fail on
either dead host anywhere in the repo. This validator is already the
unfiltered drift guard for exactly this config surface (added by
OSS-881), and both its CI workflow and its lefthook step are
deliberately un-globbed, so the new rule needed no new wiring.

`packages/channels-intelligence/src/realtime-gateway.test.ts` is
allowlisted rather than rewritten: it needs a hostname that genuinely
does not resolve, because `getaddrinfo ENOTFOUND` is the condition under
test. Swapping in a live host would silently void the assertion.

## Testing

**The gate fires (mutation check).** Appended the dead pair to a shipped
skill, then reverted:

```
$ printf 'apiUrl: "https://api.copilotkit.ai"\nwsUrl: "wss://realtime.copilotkit.ai"\n' \
    >> skills/runtime/references/intelligence-mode.md
$ tsx scripts/validate-intelligence-env-names.ts
Found 2 non-canonical Intelligence references:

  skills/runtime/references/intelligence-mode.md:366  api.copilotkit.ai — routes nothing (empty-body 404); use api.intelligence.copilotkit.ai
  skills/runtime/references/intelligence-mode.md:367  realtime.copilotkit.ai — does not resolve; use realtime.intelligence.copilotkit.ai
exit=1
```

**Clean tree passes, and the real hosts are not false-positived**
(`--fixed-strings` means `api.copilotkit.ai` does not match
`api.intelligence.copilotkit.ai`; the tree contains many mentions of the
latter):

```
$ tsx scripts/validate-intelligence-env-names.ts
Intelligence env var names and hosts are canonical.
```

**Host claims re-verified against prod, 2026-08-25:**

```
$ curl -D- -X POST https://api.intelligence.copilotkit.ai/api/threads
HTTP/2 401    x-powered-by: Express    content-length: 241

$ curl -D- -X POST https://api.copilotkit.ai/api/threads
HTTP/2 404    server: awselb/2.0    content-length: 0

$ curl -D- https://api.cloud.copilotkit.ai/          # same ALB, matching host
HTTP/2 404    x-powered-by: Express    content-length: 63

$ dig +short api.copilotkit.ai        -> 3.220.212.155 54.211.52.23 35.168.142.32
$ dig +short api.cloud.copilotkit.ai  -> 54.211.52.23 3.220.212.155 35.168.142.32
$ dig +short realtime.copilotkit.ai   -> (no record)
```

**Other gates:**

```
$ tsc --noEmit --strict scripts/validate-intelligence-env-names.ts     # clean
$ oxfmt --check <changed files>            All matched files use the correct format.
$ tsx scripts/sync-plugin-skills.ts --check    plugin skill mirror in sync
$ vitest run scripts/__tests__/public-skill-drift.test.ts \
             scripts/__tests__/sync-plugin-skills.test.ts
  Test Files  2 passed (2)    Tests  19 passed (19)
```

Committed with `core.hooksPath=/dev/null` — this worktree borrows
`node_modules` by symlink, so lefthook's native deps can't load. The
hooks' own checks were run by hand above.

## Not in this PR

- **The fixture pins.** 45 `both-oss` cells still resolve a pre-1.64.0
runtime; that's in the Intelligence repo.
- **Whether `api.copilotkit.ai` should stop answering.** It is not an
unclaimed name — it is aliased onto the live Copilot Cloud ALB. Making
it the public Intelligence alias would mean adding an Intelligence
listener rule to the *legacy Cloud* balancer, which straddles two
products. Worth its own issue.
- **Compiling the packaged skill snippets.** `scripts/doc-tests` only
extracts from `showcase/shell-docs/src/content`, so the 601 TS snippets
across 75 shipped skill files — the surface an agent-driven install
actually follows — are never type-checked. That is the structural reason
a non-existent config property survived in the runtime skill until
OSS-881 caught it by hand. Too large for this PR; it deserves its own.
2026-08-25 13:55:40 -07:00
Benjamin Taylor b8283ef4e1 refactor(scripts): match dead hosts case-insensitively, carry each reason with its host
DNS is case-insensitive, so a capitalized host in prose would have slipped the
literal match. Env var names stay case-sensitive — `ignoreCase` is opt-in per
rule. The per-host reason moves onto the constant so adding a third host cannot
silently inherit the wrong message.

Also restores the TSDoc's original framing of what an override is for
("non-production or future self-hosted"), matching the runtime skill's wording
rather than diverging from it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 15:45:27 -05:00
Benjamin Taylor b057744684 fix(docs): stop shipping stale Intelligence config claims, and gate the dead hosts (refs OSS-961)
The packaged runtime skill up to v1.62.2 prescribed `api.copilotkit.ai` /
`realtime.copilotkit.ai`. The first host is a CNAME onto the legacy Copilot
Cloud ALB, where no listener rule matches it, so every request gets the ALB
default action: a 404 with an empty body. The second has no DNS record at all.
A reader who followed that page converted a working OSS install into a 502.

The hosts themselves were corrected in v1.64.0, but two shipped surfaces still
carried stale claims about the same step, and nothing stopped the hosts from
coming back a third time:

- The debug skill said Intelligence "requires ... `apiUrl`, `wsUrl`, `apiKey`,
  `tenantId`". Three errors in one line: `apiUrl`/`wsUrl` have been optional
  with managed defaults since v1.64.0, and `tenantId` has never existed on
  `CopilotKitIntelligenceConfig` — the API key carries the project (its token
  format is `cpk-{projectId}_...`) and the platform resolves the organization
  server-side, so there is no org or tenant field for a caller to pass.
- `CopilotKitIntelligence`'s own TSDoc showed only `*.internal` placeholders,
  so the class's hover docs never named the pair that actually serves prod.

`validate-intelligence-env-names` — already the unfiltered guard for this same
config surface (OSS-881) — now also fails on either dead host. The
channels-intelligence realtime test is allowlisted: it needs a hostname that
genuinely does not resolve, since `getaddrinfo ENOTFOUND` is the condition
under test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 15:35:55 -05:00
Tyler Slaton 79c02f0f02 style(web-inspector): fewer layers on the launcher, a lens for its dot (#6688)
## What does this PR do?

Design review on the launcher and its notification dot asked for three
things: a milder face than solid black, fewer borders and background
layers, and a subtle shadow in place of the dot's heavy border. This is
all three, plus the removal of six utilities that never had any effect.

Everything here was compared side by side at production size, on a light
*and* a dark host page, before it was chosen. Two of my own first
proposals were dropped after measuring them, both described below.

**Two concerns, three commits.** `4fa38d91f` and `cd711b369` are the
launcher itself — the package change, 2 files. `295f75495` gives the
react-router lab a dark mode, because a dark host page is what this
change has to be judged against and the lab could not produce one. If
you would rather review those separately, say so and I will split them.

## The face

`#181C1F` at 95%, which review asked for.

Worth recording so it does not come up again: the near-black the review
saw was `#010507`, 20.5:1 against white. What shipped yesterday was
already `#1C1F24` at 16.5:1, so this value is a hair *darker* than the
one it replaces (17.2:1) and the difference between them is a ΔE of 2.3,
at the floor of what an eye can separate. It settles the question rather
than changing the look.

## Fewer layers

Six Tailwind utilities on the launcher set properties the unlayered
`css` block sets again — `bg-slate-950/95`, `border-white/20`, `ring-1`,
`ring-white/10` and the two hover variants. Unlayered declarations beat
layered ones regardless of specificity or source order, so none of them
has ever had any effect. Each was the package's only use, so the
checked-in stylesheet drops 980 bytes.

Of the *visible* layers, two went:

**The outer hairline.** The launcher carried two concentric lilac rings:
the border, and a second one 1px outside it as a box-shadow. The outer
one also hardcoded the lilac rather than reading `--cpk-launcher-edge`,
so it silently could not follow the token. It is replaced by a one-pixel
light edge along the top, which is what keeps the face from reading flat
without drawing a frame.

**`backdrop-blur-md`.** It sat behind a 95%-opaque fill and bought close
to nothing, while mounting a permanent blur compositing layer over a
customer's page.

**The border stays, and this is the finding that changed my mind.** I
first proposed removing it too. Against a dark host page the face
measures 1.10:1 (GitHub dark), 1.04:1 (Tailwind slate-900) and 1.22:1
(black) — indistinguishable from the page. The border is the only thing
that gives the launcher an outline there. It is not decoration.

## The dot

The collar was `1.5px`, opaque, zero blur, and 21% of the dot's
footprint. Because the dot's centre sits *on* the rim, its outer half
painted a hard dark crescent onto the **host page** rather than onto the
launcher — which is what read as "heavy". A hairline plus a soft drop
separates it just as well.

The fill becomes a lens lit from the upper left. Both stops are derived
from `--cpk-launcher-signal`, so a new tone needs no new values;
verified for the rose error tone and the violet announcement tone.

**Dropped after looking at it:** a coloured glow around the dot. It was
the obvious reading of "more premium", but the launcher already pulses
in that same colour when a failure is new, and a permanent glow competes
with the thing that is supposed to draw the eye.

**Also dropped:** tinting the border in the signal colour, which was
suggested in review. On a dark page the border is the entire silhouette,
so tinting it recolours the whole launcher for a state that can persist
for hours.

## One non-obvious consequence

Removing the blur removed a side effect nobody had written down:
`backdrop-filter` promotes the element to its own compositing layer.
Without a layer, the hover `scale(1.05)` re-rasterises the mark every
frame and it visibly jitters — geometrically nothing moves, the mark's
centre holds to three decimals, but the vector is re-rendered at
fractional offsets. `will-change: transform` asks for the layer directly
and the jitter is gone. Confirmed by eye on the running demo before this
was chosen.

## Tests

`packages/web-inspector` stays at **28 files / 611 tests**, all passing.

No new tests. The colour tests here are deliberately token-shaped rather
than value-shaped — they assert the custom property and the *sharing* of
one face and one edge between the launcher and its pill, never a hex —
so face and edge values are free to move and this change is exactly the
kind they were written to allow. The one test that constrains it, `"the
pill and the launcher share one surface and one edge"`, still passes.

What is genuinely unguarded, and was before this PR too: the dot's
collar width, the double hairline, and the Tailwind class list.
Asserting rendered geometry would need a browser test runner, which this
package does not have — jsdom computes no layout.

## How to see it

`pnpm --filter react-router-example dev`, then `http://localhost:5173`.
The launcher is top right; `Break runtime` arms the error tone and
`Break run` the announcement one. Hover it to check the mark no longer
jitters.

One thing worth knowing while reviewing: the launcher anchors top-right
and is `position: fixed` on an element mounted directly under `<body>`,
so on this page it sits over the lab's toolbar. Drag it to the lower
right and it is out of the way.

## The lab's dark mode

A dark host page is where the launcher's border earns its place, and the
lab had no way to produce one, so reviewing this change was not possible
without it.

It follows `examples/v2/react/demo` rather than inventing anything: the
host owns a `theme` state, and `CopilotChat` gets `className="dark"` —
which is what makes the package swap its own variable set. The colours
are the demo's by another route; it writes the oklch literals
CopilotKit's variables use, and those are Tailwind's neutral steps
(`neutral-950` is `oklch(0.145 0 0)`, `neutral-50` is `oklch(0.985 0
0)`, `neutral-800` is `oklch(0.269 0 0)`). Measured identical on the
running lab.

`@custom-variant dark (&:is(.dark *))` is needed in the lab's stylesheet
because Tailwind v4 points `dark:` at `prefers-color-scheme` by default,
so the toggle would have lost to the OS. Same declaration the package
uses for its own sheet.

Two details that are decisions rather than oversights. The **error
banner keeps a rose tint** in dark mode instead of going neutral,
because an error banner that looks like every other surface is not an
error banner. And the **toolbar buttons keep a visible on/off contrast**
— active inverts to a light face, inactive sits on `neutral-800` —
because the lab's whole purpose is knowing which failure is currently
armed.

My first attempt stripped every background instead of theming, and that
is worth recording because it looked plausible: the chat bubble, the
send button, the button states and the banner all collapsed into one
flat grey. The chat paints its own surfaces and has to be told what
theme it is in, not undressed.

## A separate bug found on the way

`CopilotKitProvider` documents `inspectorDefaultAnchor` — *"Default
anchor corner for the inspector button and window"* — and it has no
effect. `defaultAnchor` is typed on the React wrapper and forwarded to
the element, but the string `defaultAnchor` does not occur anywhere in
`packages/web-inspector`, so it lands as `defaultanchor="[object
Object]"` and is ignored. The corner stays hardcoded `{ horizontal:
"right", vertical: "top" }` in two places.

Not fixed here, to keep this PR to one concern. It is worth fixing: any
host with a top navigation bar hits exactly this, finds exactly that
prop, and it does nothing.

## Related PRs and Issues

- Follows #6646
2026-08-25 13:27:06 -07:00
Mike Ryan 35a4820cec chore: release monorepo v1.69.1 (#6692)
## Release monorepo v1.69.1

**Scope:** `monorepo` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `monorepo` packages to `1.69.1`
   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.69.1`
   - Creates git tag `monorepo/v1.69.1`
   - 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.69.1
2026-08-25 12:57:50 -07:00
Benjamin Taylor 04684614ca test(web-inspector): stop the gesture tests racing the beat on real timers
The two real-timer tests in the launcher's error-signal suite each asserted a
pre-beat state 200ms after breaking the connection. The beat is 400ms
(ERROR_GESTURE_MS.beat), so both assertions had a 200ms margin against a wall
clock on a shared runner. When the runner is loaded the 200ms sleep overshoots
400ms, the beat has already fired, and the assertion reports the next phase.

Both claims are already pinned deterministically on the fake clock, where every
phase boundary of the gesture is asserted. The real-timer tests exist only to
show the beat and the gesture run to their end on their own, so the racy
preconditions are removed rather than retimed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 14:46:25 -05:00
MikeRyanDev 6053e4e262 chore: release monorepo v1.69.1 2026-08-25 18:50:37 +00:00
Ben Taylor bf2068734b docs: fix missing space in CONTRIBUTING heading (#6681)
Fix 1)Install -> 1) Install in CONTRIBUTING.md
2026-08-25 12:41:34 -05:00
Ben Taylor a1a14b7876 docs: use em dash in AG-UI protocol line in README (#6680)
Fix dash to em dash for consistency.
2026-08-25 12:40:50 -05:00
Ben Taylor f2390df714 docs: fix typo in Mastra shared state guide (#6686)
## What does this PR do?

Corrects `faciliate` to `facilitate` in the Mastra shared state
documentation.

## Related PRs and Issues

- None.

## Validation

- `codespell
showcase/shell-docs/src/content/docs/integrations/mastra/shared-state/index.mdx`
- `git diff --check`

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/CopilotKit/CopilotKit/blob/main/CONTRIBUTING.md).
- [x] The relevant documentation is updated by this PR.
- [x] "Allow edits by maintainers" is enabled.
2026-08-25 12:32:26 -05:00
Ben Taylor 1602ac3bb7 updated with-mcp-use README to not have stale references (#3614)
Removed redundant text and improved clarity in the README. Removed stale
and internal references

## What does this PR do?

Fixed the Readme references for the open mcp app example, it was
referencing stale docs folder which does not exist.

## 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
2026-08-25 12:32:17 -05:00
Ben Taylor c59c1c891b fix(showcases): make MCP Apps deployable (#6634)
# fix(showcases): make MCP Apps deployable

## Summary

- keep the approved `@copilotkit/*` `1.68.1` upgrade and frontend
Railway health check
- import the endpoint stack and built-in agent from
`@copilotkit/runtime/v2`, matching the current
`examples/integrations/mcp-apps` runtime pattern
- align MCP Apps on one AG-UI `0.0.58` client/core/encoder/proto graph
with `@ag-ui/mcp-apps-middleware@^0.0.3`
- replace the stale standalone pnpm lock with the npm lock consumed by
Docker, and make Docker install it deterministically with `npm ci
--legacy-peer-deps`
- remove only unused frontend direct dependencies;
`@copilotkit/react-core`, `@copilotkit/runtime`, and
`@copilotkit/shared` remain direct `1.68.1` dependencies, while the
separately packaged MCP server retains its own `zod@^4.3.5`

## Root cause and RED evidence

- Baseline commit: `0daa38f9a7e50522e246a273802ef3ffde92556b` on current
`origin/main` `c2abbea9cf`.
- `./node_modules/.bin/tsc -p examples/showcases/mcp-apps/tsconfig.json
--noEmit --pretty false` reproduced three compatibility failures:
`TS2305` for `createCopilotEndpoint`, `TS2305` for
`InMemoryAgentRunner`, and `TS2345` because `MCPAppsMiddleware` and the
runtime resolved incompatible `@ag-ui/client` types.
- Before production edits, `npm test` ran the new compatibility contract
and failed `3/3` assertions for the legacy runtime entrypoint, stale
AG-UI versions, and missing npm lockfile.
- Before changing the Docker install command, the deployment contract
failed `1/4` because the Dockerfile still used `npm install
--legacy-peer-deps`.
- The first Docker build sent `889.11 MB` because no `.dockerignore`
excluded host `node_modules` and `.next`; the final context is `5.64
kB`.

## GREEN and verification evidence

- PASS — scoped formatter: repo-pinned `oxfmt --check` exited `0` for
the changed source, contract, and manifest inputs.
- PASS — scoped lint: repo-pinned `oxlint` reported `0 warnings` and `0
errors` for the changed route and compatibility test.
- PASS — explicit typecheck: standalone `./node_modules/.bin/tsc
--noEmit --pretty false` exited `0`.
- PASS — focused tests: `npm test` passed `5/5` contracts covering the
v2 runtime entrypoint, AG-UI graph, npm lock truth, deterministic Docker
install, and Docker context exclusions.
- PASS — production build: `npm run build` compiled, typechecked,
generated all four static pages, and emitted the
`/api/copilotkit/[[...slug]]` dynamic route.
- PASS — npm lock/install truth: `npm ci --legacy-peer-deps
--ignore-scripts` installed `1,234` packages from `package-lock.json`;
`npm ls @ag-ui/client @ag-ui/core @ag-ui/encoder @ag-ui/proto --all`
showed every instance deduped/overridden to `0.0.58`.
- PASS — no-cache container gate: `docker build --no-cache -f Dockerfile
-t codex-mcp-apps-pr .` completed `npm ci`, the in-image Next production
build, image export, and unpack on `node:20-slim` with exit `0`.
- PASS — scope/secret/type hygiene: the final PR changes only
`examples/showcases/mcp-apps/**`; high-confidence secret patterns, type
suppressions, swallowed catches, and floating calls were absent from the
staged diff.
- PASS — branch commit: `db929211253c5f6e4a9567833bdb0d4b6e7c7d8c`
(`fix(showcases): align MCP Apps runtime graph`), following the approved
deployability commit without modifying its Railway file.

## Non-blocking warnings

- npm reports 10 transitive audit findings (5 low, 1 moderate, 4 high);
this compatibility change does not force unrelated breaking upgrades.
- The Node 20 container install emits `EBADENGINE` warnings for
transitive `@azure/*`, `@typespec/ts-http-runtime`, and `openai@7.5.0`
packages that declare Node 22, but the clean Node 20 install and
production build both complete successfully. The existing `node:20-slim`
base is intentionally unchanged because the gate did not reproduce a
failure.
- Local Next builds inside the monorepo warn about the root pnpm lock
plus the standalone npm lock; the standalone build still completes
successfully and Docker uses only the showcase-local npm lock.
2026-08-25 12:01:09 -05:00
Ben Taylor 1ebeae46c2 fix(world): preserve LangGraph assistant config (#6631)
# fix(world): preserve LangGraph assistant config

## Summary

- restore the World demo's browser-supplied OpenAI key under
`assistantConfig`
- preserve the behavior of the authoritative legacy World commit
- add a dependency-free migration contract test

## Verification

- rebased cleanly onto current `main` at
`c2abbea9cf6a48c22b1dcd19e9dc469d5cfd458f`
- verified the authoritative legacy source still uses `assistantConfig`
at `markmdev/copilotkit-world@7a27a37b628b9e339d1284ec42382008e67884f5`
- verified `@ag-ui/langgraph@0.0.7` declares, stores, and merges
`assistantConfig`
- regression check against current `main` failed for the expected
missing-`assistantConfig` reason; the branch's dependency-free Node test
passed (`1/1`)
- `oxfmt --write` plus `oxfmt --check` passed on both changed files
- Nx reported no affected lint targets; direct `oxlint` completed with
`0` errors and one pre-existing `NextRequest` type-import warning on the
unchanged import line
- root dependency bootstrap passed with the repository-pinned pnpm
`10.33.4` and `--frozen-lockfile --ignore-scripts`
- `nx run @copilotkit/runtime-client-gql:build` and its 13 dependency
builds passed; this does not make that package resolvable from the
independently managed nested ChatKit Studio workspace
- exact-base and branch World typechecks used the same pnpm `9.15.0`
provisioning: base reports seven diagnostics, branch reports six, and
the branch-only diagnostic count is zero
- the removed base diagnostic is the changed route's invalid `config`
property; all six remaining diagnostics are identical current-`main`
errors in unchanged `page.tsx`, `useCountryData.ts`, and
`countryData.ts`
- both production builds compiled the optimized application source; base
then failed on the invalid `config`, while the branch advanced past the
changed route and stopped on the pre-existing undeclared
`@copilotkit/runtime-client-gql` import
- Python agent compilation passed
- worktree is clean; commit `a25941f9017fcd2a0a8e7660fe768c69cfaa28b6`
changes only the World route and its migration contract test

## Existing baseline debt

The branch introduces no type or production-build regression and removes
the route error it targets. Six unrelated current-`main` type
diagnostics remain:

```text
src/app/page.tsx(14,42): error TS2307: Cannot find module '@copilotkit/runtime-client-gql'
src/hooks/useCountryData.ts(2,50): error TS2307: Cannot find module 'geojson'
src/hooks/useCountryData.ts(4,51): error TS2307: Cannot find module 'topojson-specification'
src/hooks/useCountryData.ts(32,27): error TS2345: Argument of type 'unknown' is not assignable to parameter of type 'string'.
src/hooks/useCountryData.ts(43,12): error TS7006: Parameter 'country' implicitly has an 'any' type.
src/utils/countryData.ts(1,40): error TS2307: Cannot find module 'geojson'
```

The nested ChatKit Studio frozen install also reproduces the separate
current-`main` Playground importer drift covered by D006. D007 no longer
changes `package.json`, so it does not duplicate that lockfile fix.

Legacy source: `markmdev/copilotkit-world` at
`7a27a37b628b9e339d1284ec42382008e67884f5`.
2026-08-25 12:00:59 -05:00
Ben Taylor 96773e56dc fix(showcases): sync chatkit studio lockfile (#6630)
# fix(showcases): sync chatkit studio lockfile

## Summary

- remove stale ESLint importer entries from the shared ChatKit Studio
lockfile
- restore frozen-lockfile validation for Playground, Studio, and World
without reserializing the lockfile
- keep the change limited to the shared dependency contract required by
the Playground deployment

## Verification

- Formatter: N/A for the changed YAML lockfile. The repository's oxfmt
0.36.0 does not accept YAML targets (`Expected at least one target
file`); its full-repository check listed only 28 pre-existing files
outside this branch's one-file diff.
- YAML parsing and structural validation passed: lockfile version 9.0,
four exact manifest importers, 1,431 packages, and 1,431 snapshots.
- The shared four-project workspace completed `pnpm install
--frozen-lockfile --ignore-scripts` with repository-pinned pnpm 9.15.0;
the resolution step was skipped because the lockfile is current.
- Playground and Studio passed explicit `tsc --noEmit`; their Next.js
production builds also passed. Both Python agent modules compiled
successfully, and the workspace defines no JavaScript test suite.
- Lint is N/A for this lockfile-only diff: Playground and Studio's
existing `next lint` scripts prompt to create an ESLint configuration,
while World defines no lint script.
- World's existing source/dependency type errors reproduce in both `tsc
--noEmit` and `next build`; they are unrelated to the removed
ESLint-only importer metadata. World otherwise compiled before its
existing type-validation failure.
- Diff, scope, secret, and worktree hygiene passed: the commit changes
only `examples/showcases/chatkit-studio/pnpm-lock.yaml` with 24
deletions and no additions.
2026-08-25 12:00:43 -05:00
Lukas Moschitz 295f75495e feat(examples): give the inspector lab a dark mode like the react demo
The launcher floats over a customer's page, and its border only earns its
place against a dark one -- so reviewing this branch needs a dark host page,
and the lab had no way to produce one.

Copied from `examples/v2/react/demo` rather than invented: the host owns a
theme state, and `CopilotChat` gets `className="dark"`, which is what makes
the package swap its own variable set. My first attempt stripped every
background instead, which is why the chat bubble, the send button, the
toolbar's on/off states and the error banner all vanished into one flat
grey -- the chat paints its own surfaces and has to be told, not undressed.

The colours are the demo's, by another route: it writes the oklch literals
that CopilotKit's variables use, and those are Tailwind's neutral steps --
`neutral-950` is `oklch(0.145 0 0)`, `neutral-50` is `oklch(0.985 0 0)`,
`neutral-800` is `oklch(0.269 0 0)`. Measured identical on the running lab.

`@custom-variant dark (&:is(.dark *))` is needed because Tailwind v4 points
`dark:` at `prefers-color-scheme` by default, so the toggle would have been
ignored in favour of the OS. Same declaration the package uses for its own
sheet.

The toggle sits top left, where the react demo puts it. Top right is where
the launcher floats.
2026-08-25 17:42:01 +02:00
Ben Taylor 4319370f7e docs(react-native): say how to prove the integration without a browser (#6685)
## Why

The React Native page covers building the integration thoroughly and
never says how to
establish that it **works**. `verify` appeared zero times in its 646
lines, and the only
verification content was a reactive troubleshooting entry ("no response
from the runtime →
check `/info`").

That gap is sharper here than on the web frontends. There, "open it and
look" is an
unstated fallback that genuinely works. On React Native there is no
browser, so a reader
who follows this page to the end has no proof step at all — and the
obvious substitutes
each prove less than they appear to.

## What this adds

A **Proving it works** section with three checks, explicit that none is
sufficient alone:

1. **`copilotkit verify --round-trip`** — proves an agent answered, with
no browser and no
device. Its limits are stated rather than left to be discovered: it
sends a *fixed*
prompt and records the answer's length, never its text, so it cannot
tell you what came
back; and it proves an agent answered under the declared id, not *which*
deployment
   answered.
2. **A device capture** — `adb exec-out screencap -p`, plus `adb logcat`
for an unresolved
redbox. iOS has no `adb` equivalent short of full Xcode, and the Command
Line Tools do
   not ship `simctl`, so the section says so instead of implying parity.
3. **Checking the answer against the records the app holds** — the only
step that separates
a correct answer from a fluent one about records that do not exist. That
failure is
invisible in a screenshot, in a video, and to any reviewer unfamiliar
with the data.

Two smaller fixes on the same page:

- **`useAgentContext` is now a callout, not a list entry.** It sat in
the shared-hooks list
described as behaving "the same as on the web", which undersells the one
hook whose
absence fails *silently*. Rendering a list puts it in the view tree, not
in the agent's
context — separate steps. An agent missing the second still answers
plausibly, the tool UI
paints, and nothing errors. React Native has no browser console to
notice it in.
- **`@react-native-community/cli` is now a prerequisite.** React Native
0.87 no longer
bundles it, so an *upgraded* app needs it in `devDependencies` or
`react-native bundle`
and `react-native start` refuse to run. A freshly `init`ed app already
has it, which is
  why the quickstart path never surfaced this.

## Notes for review

- **Docs-only.** One `.mdx` file, +78 lines, no code or config touched.
- The three anchor links used (`#connecting-from-a-device-or-bench`,
`#which-hooks-are-shared-and-which-arent`, `#known-limitations`) all
resolve to existing
  headings, and match the anchor style the page already uses elsewhere.
- Prose is unwrapped to single-line paragraphs and callout bodies are
2-space indented, to
  match the file's existing convention.
- `npm run lint` in `docs/` exits 0 with no new warnings. `vitest run`
gives 188 passed / 29
failed-to-load — **identical to a pristine `origin/main` worktree**,
which I ran to confirm;
  those failures are a local module-resolution issue, not this change.
- Deliberately **not** included: documenting `copilotkit verify`
generally. It is currently
undocumented across the whole docs tree (Angular and Vue score zero on
"verif" too), which
wants its own change and probably a shared page rather than a
per-frontend section.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-25 10:35:51 -05:00
Ben Taylor a2e5abb0e6 Make React SPA a supported onboarding path (docs half) (#6675)
Closes the docs half of OSS-938. The Intelligence half (graph node flip,
`frontend/plan.md`, fixture conversion, replacement unsupported cell)
lands as a separate PR in `CopilotKit/Intelligence` — it cannot share a
PR across the repo boundary.

## Why

React SPA was the only frontend the onboarding graph could not route to
a validated outcome, and the gap was exactly one step: **where Copilot
Runtime lives.** Every framework quickstart hosts the runtime in a
Next.js route handler and sets a relative
`runtimeUrl="/api/copilotkit"`. That path resolves only 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 the instruction had nowhere to
land.

The rest of the React tree already works unchanged in a SPA —
`/frontend-tools`, `/generative-ui`, `/human-in-the-loop`, `/headless`,
`/prebuilt-components` and `/reference/v2` contain no Next.js-specific
steps. So this adds one page for the one difference and links out for
everything else, rather than forking a parallel React tree.

## What's here

**1. `docs/frontends/react-spa.mdx`** — the standalone Node runtime
server (the `angular.mdx` / `vue.mdx` shape), the absolute `runtimeUrl`
it requires, `cors: true`, the two-dev-server port story, and links back
out to the root React pages.

**2. `react-spa` registered in
`showcase/shared/frontend-registry.json`** — required, not cosmetic.
Frontend route resolution is gated on `isFrontendId`
(`src/app/[framework]/[[...slug]]/page.tsx:96`, and the `/frontends/x` →
`/x` flattening at line 133), and `isFrontendId` reads the registry. An
unregistered MDX file 404s. Vue and React Native are the precedent —
both registered, both a single page, neither with a namespaced subtree —
so this does **not** create the `/react-spa/**` mirror tree OSS-938
rules out. `feature_support_required: false` matches them, so no
feature-support matrix entries are needed.

**3. The `runtimeUrl` sweep — 13 quickstarts**, each getting a callout
on its provider step noting that the relative path assumes a
Next.js-served origin.

## Two things worth a reviewer's attention

**`cors: true` is load-bearing and easy to omit.** `resolveCorsConfig`
(`packages/runtime/src/v2/runtime/core/fetch-handler.ts:784`) is `if
(!cors) return null`, and `createCopilotNodeListener` passes options
straight through to `createCopilotRuntimeHandler`. So CORS is **off by
default** on exactly the adapter a standalone SPA runtime uses — while
Express (`endpoints/express.ts:126`, `cors: corsOption = true`) and Hono
(`endpoints/hono.ts:105`) default permissive. Since the SPA's app and
runtime are on different origins, omitting it fails every request on
preflight. The page calls this out twice.

**The sweep callouts deliberately contain no root-relative links.** My
first attempt linked `/react-spa`, `/vue` and `/react-native`, which
broke `angular-docs-content.test.ts` → "keeps every rendered
backend-specific Angular link in context" with 45 leaks. That test is
right and the links were wrong: `resolveAngularDoc` falls back to
`frameworkContentSlug`, so the Angular surface **reuses these same
integration quickstarts**, and its link contract deliberately keeps
readers inside `/angular/**`. Enumerating three other frontends was also
wrong content for an Angular reader. The callout now names the guide
paths as inline code instead.

The cost is that the sweep no longer hands the reader a clickable link —
discoverability for React SPA comes from the frontend selector entry
instead. Doing both properly needs a frontend-conditional content
component (the `WhenAngularBackend` pattern, but keyed on frontend),
which is more than this sub-task should carry. Worth a follow-up.

## Corrections to the issue found while implementing

- **There is no doctest harness.** OSS-938 says the snippets are
doctest-gated. `shell-docs` runs vitest over `src/**/*.test.{ts,tsx}`
only — MDX code blocks are never executed. The real gates are that suite
plus `tsc --noEmit`.
- **The docs do have a frontend selector**, backed by the registry (6
entries before this change). The issue's "no frontend selector anywhere"
is true only of MDX *tab groups*.
- **The sweep is 13 source files, not 18 pages.** The langgraph,
microsoft-agent-framework and aws-strands variants are tab groups inside
one page each, so `langgraph-fastapi`/`-python`/`-typescript` collapse
to one file, the three `ms-agent-*` to one, and
`strands`/`strands-typescript` to one.
- **The root `/quickstart` needs nothing.** It is a 17-line routing shim
that 308-redirects to `/` and carries no runtime step at all. The issue
counts it among the 18.
- **The issue's page list misses `built-in-agent` and the root-level
`agent-spec/quickstart.mdx`**, both of which do carry the relative
`runtimeUrl`. Both are swept here.
- **Vue and React Native are already done**, in both repos — both ship
the absolute-`runtimeUrl` recipe and both already route to
`credentials/finalize-plan`. The issue defers them as out of scope, but
that also means the sweep fixes the mis-route for one frontend, not
three.

## Testing

Run in the worktree against `origin/main` (`0943c5196e`).

**`tsc --noEmit` — no new errors.** Baselined by setting my changes
aside on a pristine checkout: 8 pre-existing errors, all from
`@clerk/nextjs` / `@testing-library/react` / `jsdom` being undeclared
and uninstalled in `showcase/shell-docs/package.json` (verified absent
in `main` too, so this is repo state, not this branch). With the changes
applied: the same 8, zero added.

The type widening surfaced one real error, now fixed —
`FRONTEND_REFERENCE_SLUGS` in `frontend-page-content.ts` is `satisfies
Record<FrontendPageId, string>` and needed a `react-spa` entry. It maps
to `"reference"`, the root React reference, same as Vue.

**Test suite — no new failures.**

```
$ npx vitest run src/lib src/app
 Test Files  1 failed | 41 passed (42)
      Tests  1 failed | 380 passed (381)
   × renders one dependency-complete canonical tool-rendering example for mastra
```

That mastra failure is pre-existing — reproduced on a pristine
`origin/main` checkout with every change of mine removed:

```
$ git checkout -- <changed files> && mv react-spa.mdx aside
$ npx vitest run src/lib/__tests__/llm-text.test.ts
      Tests  1 failed | 39 passed (40)
   × renders one dependency-complete canonical tool-rendering example for mastra
```

**The new page is covered by an existing registry-driven guard, and the
coverage is real.** `frontend-options.test.ts` → "maps every non-React
frontend to an MDX guide page" iterates `FRONTEND_PAGE_IDS` and asserts
`loadDoc(getFrontendContentSlug(id))?.fm.title`. Mutation-checked rather
than assumed — removing the page fails it, restoring it passes:

```
$ mv src/content/docs/frontends/react-spa.mdx /tmp/ && npx vitest run frontend-options.test.ts
   × maps every non-React frontend to an MDX guide page
      Tests  1 failed | 21 passed (22)
$ mv /tmp/react-spa.mdx src/content/docs/frontends/ && npx vitest run frontend-options.test.ts
      Tests  22 passed (22)
```

**Sweep coverage checked programmatically, not by eye.** For each of the
13 files, asserted exactly one `runtimeUrl="/api/copilotkit"`
occurrence, the callout inserted immediately after that code block's
closing fence, and the `runtimeUrl` line exactly 9 lines above it. Then
re-derived the target set from the tree to confirm nothing was missed —
the only quickstart still carrying an un-annotated relative `runtimeUrl`
is `aws-strands`, which is excluded on purpose.

**Route wiring confirmed live.** `GET /frontends/react-spa` → `301` on
the local dev server, which is the registry-gated flattening redirect
firing for a registered id. Full HTML render could not be verified
locally: the dev server 500s on `@clerk/nextjs` for *every* page
(`/vue.md` fails identically), because that dep is undeclared and
uninstalled — pre-existing and not specific to this branch.

**Every outbound link on the new page resolves** — probed
`/frontend-tools`, `/generative-ui`, `/human-in-the-loop`, `/headless`,
`/prebuilt-components`, `/reference/v2`, `/model-selection`,
`/backend/runtime-endpoints`: all 200 or 301-to-canonical.

**Fixed a stale mock while here.**
`src/app/llms-mdx/[[...slug]]/route.test.ts` hard-codes the frontend
list instead of reading the registry, so it silently omitted
`react-spa`. Added it to both lists; suite passes (15/15). The
hard-coding is still a latent divergence worth a follow-up.

## Not done here

- **`aws-strands/quickstart.mdx` is excluded from the sweep**, per the
standing hands-off arrangement while Mark leads the Strands
rejuvenation. That leaves `strands` and `strands-typescript` carrying
the un-annotated Next.js step.
- **A clickable cross-link in the sweep callouts**, per the
Angular-contract finding above.
- The Intelligence-side changes, as noted at the top.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-25 10:35:35 -05:00
Mike Ryan 21871d8829 docs(mastra): document the remote-agent path and lead with it for existing services (#6663)
## What

`MastraAgent.getRemoteAgents` appeared **nowhere** in this repo — not in
shell-docs, not in an example, not in a snippet. The only wiring the
Mastra docs taught was `getLocalAgents({ mastra })` behind `import {
mastra } from "@/mastra"`.

That was on the **"Use an existing agent"** branch of the quickstart —
the branch for readers who already have a Mastra service. Two steps
earlier the same branch tells you to `npx create-next-app
my-copilot-app`, a *separate* directory. `@/mastra` cannot resolve
across that boundary, and the shape it teaches moves a running Mastra
service into the frontend, deleting the process the reader was trying to
preserve.

All four cells of the 2026-08-21 Mastra × Next.js sweep reached the
agent over HTTP, and all four derived how on their own. `both` filed
"the remote-Mastra API is entirely undocumented" as its largest
friction; `empty` filed it as its worst papercut at ~10 minutes.

## Changes

**`mastra/quickstart.mdx`** (existing-agent branch)
- Route wired with `getRemoteAgents` over a `MastraClient`, so the agent
keeps running as its own process. Passed as a **factory**, so the agent
list is fetched per request — see the note below on why the promise form
is unsafe.
- `MASTRA_BASE_URL` convention, as `.env.local` in the frontend beside
the route that reads it: `process.env.MASTRA_BASE_URL ??
"http://127.0.0.1:4111"`.
- A **"Start your agent"** step, which the branch never had. Its
liveness check is `/api/agents`, not `GET /` — Mastra serves its console
on the agent port and answers 200 whether or not an agent is registered
(straight from the `empty` cell's evidence).
- A warning that `next dev` rewrites the `tsconfig.json` at its own root
— forcing `esModuleInterop`, `isolatedModules`, `resolveJsonModule` and
`jsx`, setting `noEmit: true`, and replacing `include`/`exclude`.
`noEmit` is the sharp one for an agent project that compiles with `tsc`.
A sibling package is why this path goes over HTTP. `agent-only` derived
this independently.

**`mastra/copilot-runtime.mdx`** — a new **"Local vs remote agents"**
section that decides between the two by *where the agent runs*, not by
preference, plus:
- the full `GetRemoteAgentsOptions` contract (`mastraClient`,
`resourceId`, `observationalMemory`, `tracingOptions`);
- why the factory form is the one to use — `agents` does accept the
promise itself, but that starts the HTTP call at module load with
nothing awaiting it, so an agent server that is not up yet produces an
unhandled rejection and **Node terminates the process**. The factory has
no such window: a failure is a 500 and the next request retries, so a
route that started first recovers on its own. Cost is one `listAgents()`
per request;
- the local-only options (`requestContext`, `untilIdle`) — so a run that
needs background tasks has to be embedded.

**CI gating** — the new route fence carries `doctest="component"` with a
mastra `doctest.json`. The old fence could never have been gated:
`@/mastra` does not resolve. Mastra now has its first typechecked route
snippet, 21 gated fences → 22.

## Verified

```
shipped fence, agent down -> up  -> 500, then 200; process survived, no restart needed
promise form, agent down         -> node terminated on an unhandled rejection
next build, NodeNext tsconfig    -> 13 keys written; module/moduleResolution NOT touched
mastra CLI                       -> serverPort 4111, getPort over 4111..4131, apiPrefix /api
doc-test extraction              -> 21 -> 22 fences; mastra sidecar selected, not the root one
extracted fence, runner config   -> tsc exit 0 (runtime 1.68.3; @ag-ui/mastra 1.1.1 and 1.1.2)
mutation: drop resourceId        -> tsc exit 1, TS2741 (the gate is live)
previously documented local shape-> tsc exit 2, resourceId missing
MDX compile, both files          -> OK (mutation-checked: unclosed tag -> FAIL)
validate-intelligence-env-names  -> exit 0
```

The behavioural checks drove the **extracted fence itself** against a
stub agent server, not a paraphrase of it — including confirming it
calls `/api/agents` (the same path the quickstart gives as the liveness
check) and that it honours `MASTRA_BASE_URL`.

The extracted fence was byte-compared against the file that typechecked
green.

**Not run:** the shell-docs vitest suite. No test reads either file,
there are no snapshots, and no page was added or moved — nav, sitemap
and `llms.txt` are unchanged. Lefthook could not run in the worktree
(`tsx: command not found`, no `node_modules`); its one non-skipped gate,
`check:intelligence-env-names`, was run manually against this tree and
passes, and the commit message passes commitlint.

## Second commit

The first commit shipped the promise form and named `moduleResolution:
"NodeNext"` as what `next dev` clobbers. Self-review caught both: the
promise form crashes the process, and `nodenext` is in Next's *accepted*
set for `module` and `moduleResolution`, so it is never rewritten.
`7f068c6` corrects both plus the `MASTRA_BASE_URL` shell, each against a
real run rather than a source read.

## Found along the way, not fixed here

`getLocalAgents({ mastra })` — no `resourceId` — **does not typecheck**
against current `@ag-ui/mastra`; `resourceId` is required in
`GetLocalAgentsOptions`. That shape still ships on four other pages
(`shared-state/` ×3, `background-tasks.mdx`), and
`examples/integrations/mastra/src/agent.ts` carries a `//
@ts-expect-error - ignore for now, typing error` over exactly this call.
Left alone to keep this PR focused; happy to file it or fix it in a
follow-up.

Refs OSS-925.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-25 08:22:17 -07:00
Tyler Slaton 2dd8478c46 feat(web-inspector): add a hover HUD on the closed launcher (#6684) 2026-08-25 08:00:26 -07:00
Lukas Moschitz cd711b369c style(web-inspector): one hairline on the launcher, a lens for the dot
Review asked for fewer borders and boxes, and for the dot's heavy border to
become a subtle shadow.

The launcher had two concentric lilac hairlines: the border, and a second
ring 1px outside it as a box-shadow, which also hardcoded the lilac instead
of reading the edge token. The outer one goes. What replaces it is a
one-pixel light edge along the top, which keeps the face from reading flat
without drawing a frame.

The border itself stays, and it is not decoration: against a dark host page
the face measures 1.10:1 (GitHub dark) and 1.04:1 (Tailwind slate-900), so
without it the launcher has no outline there at all.

`backdrop-blur-md` goes too. It sat behind a 95%-opaque fill and bought
almost nothing, while mounting a permanent blur layer over a customer's
page. It did have one real side effect -- promoting the launcher to its own
compositing layer -- and without that the hover scale re-rasterises the mark
every frame and it visibly jitters. `will-change: transform` asks for the
layer directly, and the jitter is gone.

The dot loses its opaque 1.5px collar. That collar was 21% of the dot's
footprint, and because the dot's centre sits *on* the rim, its outer half
painted a hard dark crescent onto the host page rather than onto the
launcher -- which is what read as "heavy". A hairline plus a soft drop
separates it just as well. The fill becomes a lens lit from the upper left,
both stops derived from the signal colour so a new tone needs no new values.

Considered and dropped: tinting the border in the signal colour. On a dark
page the border is the whole silhouette, so tinting it recolours the entire
launcher for a state that can persist for hours -- and the launcher already
pulses in that colour when the failure is new.
2026-08-25 16:42:54 +02:00
Ben Taylor 37d3616959 Merge branch 'main' into ben1/oss-938-react-spa-supported-path 2026-08-25 09:21:19 -05:00
github-actions[bot] 3667885fd1 style: auto-fix formatting 2026-08-25 14:08:09 +00:00
Mark 4bfcb5ce2c fix(showcase): make harness worker recycling durable (#6664)
## Summary

- make planned max-job harness-worker recycling exit cleanly after
ordered teardown, while escaped teardown failures still exit non-zero
- declare Railway `ALWAYS` as the staging and production
`harness-workers` restart policy in the existing service source of truth
- apply and verify the worker policy in the existing staging redeploy
and production promotion paths
- preserve the policy for direct named `harness-workers` pins and block
direct worker rollback with safer pin guidance

## Operational contract

- Railway remains the only worker supervisor; this does not add an
in-process restart manager
- staging worker update/readback failures are release-blocking for that
target, while independent staging services continue
- production promotion verifies the exact newly-created deployment ID,
serving digest, and active restart policy before reporting success
- no retry-max field is managed, and this does not claim unlimited
restarts for pathological crash loops
- generic restore, generic non-worker pin, and generic production
redeploy behavior are intentionally unchanged

## Review fixes

- require production promotion readback to match the deployment ID just
created
- preserve SSOT restart policy and replica settings on direct named
worker pin
- narrow the runbook language to the direct named worker pin path
actually covered by the implementation

## Verification

- `@copilotkit/showcase-scripts`: 78 test files / 2,539 tests passed
- `@copilotkit/showcase-harness`: 177 test files passed / 2 skipped;
3,723 tests passed / 18 skipped
- Ruby 3.3 Railway suite: 195 runs / 782 assertions / 0 failures
- formatter, explicit Nx typecheck, affected Nx build, and `git diff
--check` passed
- Tier-3 CR loop converged with zero mandatory findings after two
confirmation rounds

## Post-merge validation

Observe one unchanged staging worker identity through more than ten
ordinary recycle/restart cycles, confirm it continues claiming work, and
confirm the active deployment remains `SUCCESS` with `restartPolicyType:
ALWAYS`. This is bounded staging evidence, not an infinite-restart
guarantee.
2026-08-25 07:06:56 -07:00
Alem Tuzlak 6870c4a926 test(react-core): give the 100-message chat render a 20s test timeout 2026-08-25 16:06:37 +02:00
Alem Tuzlak 3d23675968 test(web-inspector): type HUD helpers as Node so check-types passes 2026-08-25 15:52:03 +02:00
Lukas Moschitz 4fa38d91f2 refactor(web-inspector): drop the launcher's dead chrome and soften its face
Six Tailwind utilities on the launcher set properties the unlayered `css`
block sets again, and unlayered declarations beat layered ones regardless of
specificity -- so `bg-slate-950/95`, `border-white/20`, `ring-1`,
`ring-white/10` and the two hover variants have never had any effect. Each
was the package's only use, so the checked-in stylesheet drops 939 bytes.
No visual change: verified the three rules are gone from the generated sheet
and the 611 tests still pass.

Kept deliberately: plain `border` (the hand CSS sets only `border-color`),
`rounded-full` (nothing else sets the radius) and the focus-visible trio
(the hand CSS sets only `outline-color`).

The face moves to `#181C1F` at 95%, which review asked for. It is a hair
darker than the `#1C1F24` this replaces -- 17.2:1 against white rather than
16.5:1, a ΔE of 2.3, at the floor of what an eye can separate -- so this
settles the question rather than changing the look.
2026-08-25 15:49:55 +02:00
Sun Han Bing 4990c8027b docs: fix typo in Mastra shared state guide 2026-08-25 21:49:07 +08:00
github-actions[bot] ec8ad0c33a style: auto-fix formatting 2026-08-25 13:30:54 +00:00
Benjamin Taylor 2ee4d6bc6b Merge branch 'main' into ben1/oss-925-the-remote-mastra-path-is-undocumented-and-all-four-cells
Resolves the Mastra quickstart runtime step, where this branch's remote-agent
wiring and main's Intelligence wiring landed on the same lines. Kept both: the
per-request `getRemoteAgents` factory now sits alongside `intelligence` and
`identifyUser`, `runner: new InMemoryAgentRunner()` is gone (main dropped it
with the runner), and `resourceId` is derived from the same request header as
`identifyUser` rather than left hardcoded to "user-1", which would have scoped
Mastra's memory and CopilotKit's threads to different users. Both env blocks
and both callouts are preserved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 08:29:48 -05:00
Alem Tuzlak 7dd47cc2dd Merge branch 'main' into alem/oss-903-inspector-bubble-emanations 2026-08-25 15:28:55 +02:00
Tyler Slaton c61f0f32ed feat(web-inspector): surface failures on the launcher and open the Inspector on them (#6646) 2026-08-25 06:28:17 -07:00
Benjamin Taylor 06fe1d8232 docs(react-native): say how to prove the integration without a browser
The page tells you how to build the integration and never how to establish that it
works. `verify` appeared zero times in 646 lines, and the only verification content
was a reactive troubleshooting entry. That gap is sharper here than on the web
frontends: there, "open it and look" is an unstated fallback that actually works. On
React Native there is no browser, so a reader following this page to the end has no
proof step at all.

Adds a "Proving it works" section with three checks, and is explicit that none of them
is sufficient alone, because each one looks more conclusive than it is:

- `copilotkit verify --round-trip` proves an agent answered with no browser and no
  device. It sends a fixed prompt and records the answer's length, never its text, so
  it cannot tell you what came back -- and it proves an agent answered under the
  declared id, not which deployment answered.
- A device capture proves the tool UI rendered. Android gets `adb exec-out screencap`;
  iOS has no equivalent short of full Xcode, and the Command Line Tools do not ship
  `simctl`, so that is stated rather than left to be discovered.
- Checking the answer against the records the app holds is the only step that
  separates a correct answer from a fluent one about records that do not exist. That
  failure is invisible in a screenshot, in a video, and to any reviewer unfamiliar
  with the data.

Also on this page:

- `useAgentContext` was listed in the shared-hooks list as behaving "the same as on
  the web", which undersells the one whose absence fails silently. Rendering a list
  puts it in the view tree, not in the agent's context. An agent missing it still
  answers plausibly and nothing errors -- and React Native has no browser console to
  notice it in. Now a callout.
- React Native 0.87 no longer bundles `@react-native-community/cli`, so an upgraded
  app needs it in devDependencies or `bundle` and `start` refuse to run. A freshly
  `init`ed app already has it, which is why the quickstart path never hit this. Now a
  prerequisite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 08:27:30 -05:00
Ben Taylor d85b0b5db9 fix(runtime): reject runner passed alongside intelligence (closes OSS-933) (#6670)
## Problem

`runner` and `intelligence` are mutually exclusive by construction, but
the exclusivity was enforced in only one direction and only for object
literals.

`CopilotIntelligenceRuntime` hardcodes `new
IntelligenceAgentRunner(...)` into its `super()` call
(`runtime.ts:582`), and `runner?` is declared only on
`CopilotSseRuntimeOptions` (`runtime.ts:239`). The type system catches a
`runner:` key on an Intelligence-shaped **object literal** via
excess-property checking — but that is the only barrier. A JS caller, an
`as any`, or a non-literal options object routes through
`CopilotRuntimeShim`'s `hasIntelligenceOptions()` dispatch into the
Intelligence constructor and has `runner` **silently dropped**, with no
diagnostic.

The mirror case is already guarded: `CopilotSseRuntime` throws on
`channels`, and the comment there states the exact reasoning that
applies here — "the type forbids it, but a JS / `as any` caller ...
would otherwise land here and have `channels` silently dropped — fail
loud instead." The Intelligence constructor validates `identifyUser`,
`channels`, `memory`, and `ɵlearning`. Same file, same pattern, one case
missing.

### It also made a shipped skill lie

`packages/runtime/skills/runtime/SKILL.md:87` asserted:

> Passing both `runner` and `intelligence` to `CopilotRuntime` is
rejected at construction.

It was not. And that contradicted the skill's own reference page,
`references/agent-runners.md`, which correctly described the silent
drop. Two files in the same shipped skill said opposite things about the
same behaviour.

## Change

- **Guard** (`runtime.ts:512`) — `CopilotIntelligenceRuntime` now throws
when `runner` is present, mirroring the `channels` guard in
`CopilotSseRuntime`. The message names the exclusivity and points out
that an in-memory/SQLite runner is unnecessary in Intelligence mode,
where durability is managed by the service.
- **`SKILL.md:87` unchanged** — the guard makes it accurate.
- **`references/agent-runners.md` updated** — this is *not* optional.
That page was the accurate one before this change; adding the guard
makes its "the auto-wired Intelligence runner wins regardless of what
you pass" false. Leaving it would fix SKILL.md's lie by creating the
same lie in the reference — rotating the contradiction rather than
resolving it. Its stale `:149-173,285-294` source citation is corrected
to the real line numbers too. Root `skills/` mirror regenerated via
`pnpm sync:plugin-skills`.

## Behavior notes

- Explicit `runner: undefined` still constructs. This matches how the
sibling `identifyUser` / `channels` / `memory` guards treat `undefined`,
and avoids breaking callers that spread an options object.
- The v1 deprecated compat path is unaffected:
`copilot-runtime.ts:492-509` already omits `runner` from its
Intelligence branch, so nothing routes a `runner` into this constructor
from v1.

## Tests

Two tests in `channels-option.test.ts`, alongside the existing `sse
runtime rejects channels` mirror:

- `intelligence runtime rejects a caller-supplied runner` — written
first and confirmed **red** against the unpatched constructor
(`AssertionError: expected [Function] to throw an error`), green after.
- `intelligence runtime tolerates an explicitly undefined runner` — pins
the undefined-tolerance above so the guard cannot over-throw.

## Verification

| Gate | Result |
|---|---|
| `nx test @copilotkit/runtime` | 144 files, **2080 passed, 0 failed** |
| `nx check-types @copilotkit/runtime` | Successfully ran (+22 deps) |
| `oxlint` (changed files) | 0 warnings, 0 errors |
| `oxfmt --check` | no issues in changed files |
| lefthook pre-commit + commit-msg | all green |

Closes OSS-933.
2026-08-25 08:22:06 -05:00
Alem Tuzlak 3a3df106b5 fix(web-inspector): show Learning on or off in the HUD 2026-08-25 14:53:30 +02:00
Lukas Moschitz 23e3287fa1 test(web-inspector): stop the privacy check tripping over a random id
"no telemetry payload anywhere carries the failure message" serialises the
whole property bag and asserts it does not contain "503". Two of those
properties are the anonymous distinct id -- random hex -- so a three-digit
numeric needle lands inside one roughly once in a few hundred runs. It just
failed a CI job that way, which reads like a privacy breach and is not one.

The ids are excluded by name and asserted to still be strings, rather than
weakening the needles, which are the point of the test. Proven both ways:
with an id seeded to contain "503" the test passes with this change and
fails without it, and it still goes red when a real message is attached to
the payload.
2026-08-25 14:49:17 +02:00
Alem Tuzlak 7d8a3b611e fix(web-inspector): ease HUD details, land Intelligence on Home, add Learning 2026-08-25 14:49:07 +02:00
Alem Tuzlak 303ff4a00e fix(web-inspector): make the whole HUD row open its view 2026-08-25 14:43:07 +02:00
Lukas Moschitz 3ba4a8fbda fix(web-inspector): let the event-error guard narrow a plain string
`refocusEventErrorLanding` reads the subject back out of the card's
`data-cpk-event-error` attribute, where the DOM can only offer
`string | undefined`, and handed that to a guard typed for
`LauncherSignalKey`. Narrowing untrusted input is what the guard is for, so
it takes a string; every caller that already holds a key still satisfies it.

Tests do not typecheck, so the suite stayed green and only `check-types`
saw it -- which failed three CI jobs on the same one line.
2026-08-25 14:37:07 +02:00
Alem Tuzlak afa5e29959 feat(web-inspector): show a hover HUD on the closed launcher 2026-08-25 14:29:51 +02:00
Alem Tuzlak 5391c4886b feat(web-inspector): add view thread in your app (#6562)
## What does this PR do?

Lets a developer open a saved Inspector thread in the live official
chat.

- New header action: **View in your app**
- Official React and Vue chat switch to that thread
- A pinned `threadId` does not block the switch
- **Stop viewing** or an app thread change restores the previous thread
- Example threads have no action
- Production builds hide the action
- Same agent only. No matching official chat shows an error in the
Inspector

Core owns a two-way EventClient bridge
(`@tanstack/devtools-event-client`). The root import is a no-op in
production.

Docs: Inspector guide, section **View a thread in your app**.

## Related PRs and Issues

-
https://linear.app/copilotkit/issue/OSS-871/new-features-add-a-new-view-thread-in-your-app-feature

## 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
2026-08-25 13:52:52 +02:00
Tyler Slaton d7ac976636 fix(web-inspector): preserve independent error signals 2026-08-25 13:46:59 +02:00
Lukas Moschitz cf062dd375 chore(web-inspector): regenerate the stylesheet artifact after the merge 2026-08-25 12:10:52 +02:00
Lukas Moschitz 306ddaa5df Merge remote-tracking branch 'origin/main' into lukas/oss-903-presentation-wire-errors-notifications-to-emanate-from-and
# Conflicts:
#	packages/web-inspector/src/lib/__tests__/telemetry.test.ts
#	packages/web-inspector/src/styles/generated.css
2026-08-25 12:09:52 +02:00
Alem Tuzlak 8c12aba792 chore: remove merge-only import formatting 2026-08-25 11:28:41 +02:00