14980 Commits

Author SHA1 Message Date
Tyler Slaton 34b10737b0 chore: release monorepo v1.68.3 (#6601)
## Release monorepo v1.68.3

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

---

### How this release process works

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

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

3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.

4. **When this PR is merged**, the `release / publish` workflow
automatically:
   - Builds all packages
   - Publishes the `monorepo` packages to npm at version `1.68.3`
   - Creates git tag `monorepo/v1.68.3`
   - Creates a GitHub Release with the final release notes

### Before merging

- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)

---

> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
v1.68.3
2026-08-20 10:44:28 -07:00
BenTaylorDev aa3fb29dce chore: release monorepo v1.68.3 2026-08-20 10:27:07 -07:00
Tyler Slaton 7efa3b64f0 fix(runtime): send global telemetry properties on the field the sink reads (#6603) 2026-08-20 10:25:54 -07:00
David McKay 16e2c3a69a fix(runtime): send global telemetry properties on the field the sink reads
Folded into `properties`, they arrived in the per-event slot. That works and it
is the wrong place: the sink treats `global_properties` as the pass-through bag
for `oss.runtime.*` and spreads it into the analytics event, and v1's client
sends package name and version there for the same reason.

Sending them as their own field keeps a process-level fact separable from an
event's own properties the whole way to the warehouse.

It also moves conflict resolution. Two fields cannot collide in the SDK, so a
shared key survives on both and the sink decides, spreading the global bag last
and therefore letting the global win. That is the opposite of what most readers
expect from the word global, so the field docs now say not to reuse a key an
event already sets, and a test pins the behaviour rather than leaving it to be
discovered.
2026-08-20 10:19:12 -07:00
Ben Taylor dcaface1ed feat(runtime): let a caller name itself on the telemetry it already sends (#6599)
## The problem

The v2 telemetry client sends exactly the properties each call site
passes. There is no way for a product built on this runtime to be told
apart in the events that already go.

That leaves one option open to such a product: send its own events.
Which means a second pipeline describing the same runs, a second
namespace at the ingest gate, and two sources of truth for "how much
traffic came through us".

## What this adds

`telemetryProperties` on the runtime, merged into every event the client
sends.

```ts
new CopilotRuntime({
  agents,
  telemetryProperties: { accessibility_title: "OpenBot" },
});
```

Set beside the license token, in the shared base, for the same reason
that is: it describes the caller rather than the call, so every event
carries it whichever handler fired, `instance_created`,
`copilot_request_created` or any of the `agent_execution_stream_*`.

## Two decisions worth naming

**Per-event properties win on conflict.** A call site describing one
event knows more than a value set once at construction, so the general
must not overwrite the specific. Tested.

**No egress on its own.** Unset, nothing changes. Telemetry off, nothing
is sent, so nothing carries this. It adds a field to existing events
rather than adding events.

## Why

OpenBot needs to be separable from other runtime traffic in the existing
OSS analytics, and the ask there was explicitly *one field, no new
events, no new pipeline, no new namespace*. Without a seam here, the
only way to answer "which requests came through OpenBot" is to build the
thing nobody wanted.

The client is a private module singleton and the package `exports` map
does not expose it, which is correct, so a consumer cannot reach it to
set this itself. Verified by probing the deep import from a consuming
package: blocked.

## Tests

Six new, at the send boundary rather than on the instance, because a
field held correctly and dropped on the way out is the failure that
matters:

- carried on an event that sets none of its own
- carried alongside an event's own properties
- successive calls merge rather than replace
- an event's own property wins on conflict
- nothing sent at all when telemetry is disabled
- nothing added when none are set

`packages/runtime`: 11 telemetry tests pass, 1270 v2 tests pass, 0
failures. Build clean, `telemetryProperties` present in the emitted
`.d.mts`. Pre-commit gate green (lint 0 warnings, monorepo tests,
commitlint).
2026-08-20 12:08:56 -05:00
David McKay 86a9f9b016 feat(runtime): let a caller name itself on the telemetry it already sends
The v2 telemetry client sends exactly the properties each call site passes, so
there is no way for a product built on this runtime to be told apart in the
events that already go. The only route open to one was to send its own events,
which is a second pipeline describing the same runs.

`telemetryProperties` on the runtime is merged into every event. Set beside the
license token and for the same reason: it describes the caller rather than the
call, so every event should carry it whichever handler fired.

Per-event properties win on conflict. A call site describing one event knows
more than a value set once at construction, and letting the general overwrite
the specific would be the wrong way round.

No behaviour change when unset, and nothing is sent when telemetry is off, so
this adds no egress on its own.
2026-08-20 09:49:36 -07:00
Ben Taylor 9df63beeef fix(runtime): name useSingleEndpoint when a single-route envelope hits a multi-route runtime (#6579)
Closes
[OSS-882](https://linear.app/copilotkit/issue/OSS-882/add-to-existing-journeys-reach-for-the-v1-compat-copilotkit-wrapper).

## The failure

The v1-compatible `<CopilotKit>` provider pins `useSingleEndpoint` to
`true`
([`copilotkit.tsx:108`](https://github.com/CopilotKit/CopilotKit/blob/main/packages/react-core/src/components/copilot-provider/copilotkit.tsx#L108)),
so its startup handshake POSTs `{ method: "info" }` at the base path. A
multi-route runtime — the default — matches no route for that path and
answered a bare `{"error":"Not found"}`, indistinguishable from a wrong
`basePath` or an unmounted handler.

Two independent onboarding validation runs hit this on their first
browser attempt and each had to guess the cause. Both were
*add-to-existing-app* journeys; the greenfield one reached for
`CopilotKitProvider` and never saw it.

## What changed

**The runtime says what happened.** `detectSingleRouteEnvelope`
recognises a POST whose JSON body carries a `method` the single-route
endpoint accepts, and the multi-route handler uses it at the one point
routing gives up. The 404 now carries a `code` and a message naming the
prop, plus a `logger.warn` so it lands in the dev-server terminal too.
Deliberately conservative — wrong verb, non-JSON, unknown method, or a
JSON POST that isn't an envelope all stay ordinary 404s, unchanged in
status and shape.

**The client stops discarding it.** All four `/info` callers (two in
`agent-registry.ts`, two in `agent.ts`) threw away the response body and
reported only the status, so a server-side diagnosis reached nobody.
They now go through `runtimeInfoError`, which folds a string `message`
from the body into the thrown error. Any future server-side diagnosis
reaches the developer for free.

**Docs.** Five pages paired a v2 multi-route handler with `<CopilotKit>`
and never mentioned the prop. Rather than a warning under a snippet that
is still wrong to copy, the snippets themselves now pass
`useSingleEndpoint={false}`, with a short callout linking to the
provider/handler mapping.

Two pages were deliberately left alone: `backend/runtime-endpoints.mdx`
already documents the pairing in full, and `cookbook/arcade.mdx` uses
`mode: "single-route"` on purpose and already explains it.
`backend/copilot-runtime.mdx` keeps its snippet as-is — it pairs with
the v1 endpoint, where the default is correct — and gains the caveat
only on its "switch to v2 handlers" note.

Option 3 in the issue (reconsidering the compat default) is **not** in
this PR.

## Testing

### Both halves connect, end to end

Real `createCopilotRuntimeHandler` + real `CopilotKitCore` configured
the way the v1 wrapper configures it — no mocks on either side:

```
code   : runtime_info_fetch_failed
message: Runtime info request failed with status 404: Received a single-route
         request envelope ({ method: "..." }) but this runtime is mounted in
         multi-route mode, so the request matched no route. If the frontend uses
         <CopilotKit> from @copilotkit/react-core/v2, pass useSingleEndpoint={false}
         — that provider defaults it to true. Otherwise mount the runtime with
         mode: "single-route" to serve this envelope.

PASS — the diagnostic reached the client
```

The server-side `logger.warn` fired in the same run, carrying `{ url,
path, method: 'info' }`.

### Unit tests

`packages/runtime` — `single-route-envelope-diagnostic.test.ts` (2
positive, 5 control):

```
 ✓ src/v2/runtime/__tests__/single-route-envelope-diagnostic.test.ts (7 tests) 26ms
      Tests  7 passed (7)
```

`packages/core` — `runtime-info-error-detail.test.ts` (2 positive, 5
control):

```
 ✓ src/__tests__/runtime-info-error-detail.test.ts (7 tests) 267ms
      Tests  7 passed (7)
```

### Mutation checks

Every new test was verified to fail when its mechanism is broken, in
both directions.

Detector forced to `return null` — the two positives die, the four
controls hold:

```
   × names useSingleEndpoint when the envelope is an info call
   × diagnoses every method the single-route envelope accepts
   ✓ leaves an ordinary unmatched route as a plain 404
   ✓ leaves a JSON POST that is not an envelope as a plain 404
   ✓ leaves an unrecognized method name as a plain 404
   ✓ does not diagnose a non-JSON POST
```

Detector forced to `return "info"` — the controls die instead, proving
they are not vacuous:

```
   ✓ names useSingleEndpoint when the envelope is an info call
   ✓ diagnoses every method the single-route envelope accepts
   × leaves an ordinary unmatched route as a plain 404
   × leaves a JSON POST that is not an envelope as a plain 404
   × leaves an unrecognized method name as a plain 404
   × does not diagnose a non-JSON POST
```

`runtimeInfoError` with the detail dropped, then with the `typeof
message === "string"` guard removed — each kills a different pair:

```
mutation: detail dropped              → 2 failed | 5 passed
mutation: accept any message field    → 2 failed | 5 passed
restored                              → 7 passed
```

### Full suites, builds, docs

| Check | Result |
|---|---|
| `packages/core` full suite | `Test Files 60 passed (60)` / `Tests 662
passed (662)` |
| `packages/runtime` full suite | `Test Files 142 passed (142)` / `Tests
2067 passed (2067)` |
| `packages/core` `tsc --noEmit` | clean |
| `packages/runtime` `tsdown` | `416 files` — build complete |
| MDX compile, 5 edited pages | all `OK` |
| pre-commit `nx run-many -t test,publint,attw` | passed across affected
projects |
| CI on `f94d1ab0` | 72 pass, 3 skipping, 0 fail |

Both suites are fully green. An earlier revision of this description
reported 6
runtime failures as pre-existing on `main`; they were not. They were
artifacts
of a worktree whose `node_modules` had been assembled by hand, and a
proper
`pnpm install` cleared all of them along with the inspector-metadata
failures
from a stale `@copilotkit/shared` dist. `main` is clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-20 10:37:40 -05:00
Benjamin Taylor f36deaaa61 fix(docs): keep the useSingleEndpoint guidance mode-aware on multi-mode pages
Two pages document both transport modes and carry a single "point your
frontend at it" snippet serving every front door on the page. Baking
`useSingleEndpoint={false}` into those snippets traded one silent mismatch for
its mirror image: correct for the multi-route majority, wrong for anyone who
followed the `mode: "single-route"` example.

Both now state the rule conditionally next to the snippet instead of asserting
one side of it. Pages with a single handler mode (auth, custom-agent) are
unambiguous and keep the prop inline.

Also pins the one path where the diagnostic could have cost more than it gives:
`clone()` throws once a before-request middleware has drained the body, so the
detector must return null and let the plain 404 stand rather than surfacing a
500. The guard existed; nothing held it in place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 09:39:21 -05:00
renovate[bot] 76892a03b9 chore(deps): update depot/setup-action digest to 91bc849 (#6593)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [depot/setup-action](https://redirect.github.com/depot/setup-action)
([changelog](https://redirect.github.com/depot/setup-action/compare/15c09a5f77a0840ad4bce955686522a257853461..91bc8495a33ebfc504ffc89e5674379ccf23c29c))
| action | digest | `15c09a5` → `91bc849` |

---

### Configuration

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

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

🚦 **Automerge**: Enabled.

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

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

---

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

---

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

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zNS40IiwidXBkYXRlZEluVmVyIjoiNDQuMzUuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
2026-08-20 14:16:31 +00:00
renovate[bot] d4f147aee8 chore(deps): update depot/setup-action digest to 91bc849 2026-08-20 13:58:50 +00:00
Murat Sari 6a36bfda7d feat: implement interrupt handling in AgentStore and add injectInterr… (#6538)
## Summary

Closes #6509.

Expose an interrupt controller directly on Angular’s `AgentStore`:

```ts
const store = injectAgentStore("ticketing");

store().interruptController.hasInterrupt();
store().interruptController.resolve({ approved: true });
```

This keeps messages, state, run status, and pending interrupts on the
same conversation-scoped object. Consumers no longer need to separately
inject a controller and repeat the agent ID for the common case.

## What changed

- Added an eager, readonly `AgentStore.interruptController`.
- Bound the controller to the store’s agent and thread.
- Destroy the controller when its store is replaced or destroyed.
- Made store teardown idempotent and unregister its `DestroyRef`
callback.
- Prevent stale interrupts from resuming after the agent changes
threads.
- Kept `injectInterrupt` for typed, filtered, and preprocessed interrupt
handling.
- Added a convenient agent-selection API matching `injectCapabilities`:

  ```ts
  injectInterrupt();
  injectInterrupt("ticketing");
  injectInterrupt(agentIdSignal);
  injectInterrupt("ticketing", { enabled, handler });
  ```

- Preserved the original options-only form:

  ```ts
  injectInterrupt({ agentId: "ticketing", enabled, handler });
  ```

- Moved `injectInterrupt` into its own module to avoid an
`agent`/`interrupt` circular dependency.
- Documented the coexistence limitation between store and specialized
controllers.

## Design

The store controller is created eagerly with the store. This avoids
global coordination, lazy getters, and shared controller registries.

Each store owns two local agent observers:

1. Store projection for messages, state, and run status.
2. Interrupt lifecycle observation.

These are in-memory observers on the same agent, not additional network
or SSE connections. Both are released during teardown.

## Specialized controllers

`injectInterrupt` remains the advanced API for:

- Typed interrupt payloads.
- `enabled` filters.
- Asynchronous or synchronous `handler` preprocessing.
- Components following an ambient or explicitly selected agent.

Controllers do not claim interrupts from each other. If a store
controller and specialized controller observe the same agent, both can
expose the same decision. Applications should render only one controller
for a given decision.


## Testing

- Three focused `AgentStore` integration tests.
- Two `injectInterrupt` convenience and lifecycle tests.
- Stale-thread behavior added to the existing controller lifecycle test.
2026-08-20 15:40:51 +02:00
Rainer Hahnekamp bf18677145 preserve interrupted run IDs when resuming Angular agents 2026-08-20 15:37:07 +02:00
Rainer Hahnekamp 736742f6f9 test(angular): document unobservable thread changes 2026-08-20 15:37:07 +02:00
Murat Sari ba41a31d7c feat: implement interrupt handling in AgentStore and add injectInterrupt function 2026-08-20 15:37:07 +02:00
Mark 85af4bedfb fix(showcase): gen-ui-tool-based uses shared pie-chart contract for all slugs (#6587)
## The bug (shared-probe-rule violation)

`showcase/harness/src/probes/scripts/d5-gen-ui-custom.ts` branched on
`integrationSlug` via a stale `CHART_INTEGRATIONS` allowlist: only ~5
slugs (langgraph-python, ms-agent-python, spring-ai, google-adk, mastra)
got the pie-chart prompt + assertions. Every other `gen-ui-tool-based`
slug got an **obsolete** `generate_haiku` prompt + `HaikuCard`
assertion.

This violates Showcase **iron rule 1** (one shared probe, no per-slug
`if slug === …` branch in the test) and no longer matches the product:

- All **21** `gen-ui-tool-based` pages register `render_bar_chart` +
`render_pie_chart` via `useComponent`.
- Every committed D6 `render-a2ui.json` fixture already carries the
chart exchange.
- On staging the non-allowlisted cells failed: the assistant container
renders but the `HaikuCard` assertion reports "rendered but has no text
content". Routing the cell through the pie-chart path greens it (SVG
renders, pie validated, narration appears).

## The change (probe only)

- Every `gen-ui-tool-based` integration now sends
`PIE_CHART_USER_MESSAGE` ("Show me a pie chart of revenue by category").
- The pie-chart SVG shape assertions + second-leg narration token check
run for **every** slug.
- Removed the slug-dependent `CHART_INTEGRATIONS` set /
`isChartIntegration` branch.
- Removed the obsolete haiku prompt + `HaikuCard` fallback (verified no
other usage anywhere in `showcase/harness/src`).

No allowlist was substituted with a larger allowlist. **No** fixture
re-record, backend, frontend, npm, or aimock change. Net `-248` lines
across the probe + its unit test.

## Red → Green (local proof)

The unit test was updated **before** the implementation so a
formerly-non-allowlisted slug (`ms-agent-dotnet`) fails pre-fix.

**RED (test updated, old implementation):** 6 failed / 2 passed
```
× buildTurns sends the pie chart message for a formerly-non-allowlisted slug (ms-agent-dotnet)
  → expected 'Write me a haiku about nature' to be 'Show me a pie chart of revenue by category'
× NO slug selects a different probe contract — every gen-ui-tool-based slug sends the pie chart message
  → slug ms-agent-dotnet must send the shared pie chart message: expected 'Write me a haiku about nature' to be 'Show me a pie chart of revenue by category'
× pie chart: assertion FAILS when the rendered component has no <svg> (ms-agent-dotnet)
  → got 'gen-ui-custom: matched cascade selector … but no haiku card or rendered component found in DOM'
× pie chart: assertion FAILS when SVG has too few drawing children (pydantic-ai)
× pie chart: assertion FAILS when assistant follow-up is missing expected tokens (ms-agent-dotnet)
× pie chart: assertion PASSES on a healthy donut render with full narration (ms-agent-dotnet)
```

**GREEN (after implementation):** 8 passed / 8
```
✓ src/probes/scripts/d5-gen-ui-custom.test.ts (8 tests) 23ms
Test Files  1 passed (1)
     Tests  8 passed (8)
```

**Mutation-verified:** setting `PIE_CHART_USER_MESSAGE = "MUTANT"` fails
the two contract tests; disabling the narration token check (`if
(false)`) fails the missing-tokens test. Clean restore confirmed by
diff.

## Command results (from a fresh worktree off origin/main)

| Command | Result |
| --- | --- |
| `nx run @copilotkit/showcase-harness:test` (target file) | ✅ 8 passed
|
| `nx run @copilotkit/showcase-harness:test` (full) | ✅ 177 files / 3721
tests passed, 2 skipped, 0 failed |
| `nx run @copilotkit/showcase-harness:typecheck` | ✅ clean |
| `nx run @copilotkit/showcase-harness:build` | ✅ success |
| `showcase/bin/showcase fixtures validate` | ✅ exit 0, "All fixtures
valid" |

> Note: the full suite / typecheck initially reported failures in
`frontend-matrix.test.ts` and `d0-gone-predicate.test.ts`. These are
**pre-existing** and depend on gitignored generated artifacts
(`frontend-catalog.json`, `registry.json`) absent from a fresh worktree
— confirmed by reproducing them on pristine `origin/main` with my
changes stashed. After running the repo's `generate-registry` step they
pass. Neither file references gen-ui-custom.

## Value tests — HANDED OFF (control-plane unreachable here)

The production-shaped control-plane value tests must run **without**
`--direct`. Docker is not reachable in my environment (`docker info` /
`docker ps` exit 1; `--isolate` fails at `docker compose … ps`), so I
did **not** run them and did **not** substitute `--direct`. Please run
live:

```
showcase/bin/showcase test ms-agent-dotnet:gen-ui-tool-based --d5 --isolate --verbose
showcase/bin/showcase test pydantic-ai:gen-ui-tool-based --d5 --isolate --verbose
showcase/bin/showcase test langgraph-typescript:gen-ui-tool-based --d5 --isolate --verbose
showcase/bin/showcase test langgraph-python:gen-ui-tool-based --d5 --isolate --verbose   # control
```

Expect: RED on the formerly-haiku cells (ms-agent-dotnet, pydantic-ai,
langgraph-typescript) before this change, GREEN after; langgraph-python
stays GREEN.

## Rollout

Needs a harness / control-plane deploy + a D5 sweep rerun to flip the
affected `gen-ui-tool-based` cells. **No** npm publish, backend, aimock,
or fixture re-record required.

Refs the `gen-ui-tool-based` shared-probe contract. Separate/unrelated:
LlamaIndex `done-signal-missing` (not touched here).
2026-08-19 23:36:38 -07:00
Jordan Ritter 8f4adc9d1a fix(showcase): gen-ui-tool-based uses shared pie-chart contract for all slugs
The D5 gen-ui-custom probe branched on integrationSlug via a stale
CHART_INTEGRATIONS allowlist: ~5 slugs got the pie-chart prompt +
assertions, everyone else got an obsolete generate_haiku prompt +
HaikuCard assertion. That violates Showcase iron rule 1 (one shared
probe, no per-slug branching in the test) and no longer matches the
product — all 21 gen-ui-tool-based pages register render_bar_chart +
render_pie_chart, and every committed D6 render-a2ui fixture carries
the chart exchange.

Collapse to the single shared contract: every integration sends
"Show me a pie chart of revenue by category" and runs the SVG/pie-chart
shape + second-leg narration assertions. Remove the CHART_INTEGRATIONS
allowlist / isChartIntegration branch and the now-unused haiku prompt +
HaikuCard fallback (verified no other usage).

No fixture, backend, frontend, npm, or aimock changes.
2026-08-19 22:54:34 -07:00
Mark 60e8877e05 chore(showcase): upgrade CopilotKit to 1.68.2 (lands #6576 readiness fix) (#6585)
## Summary

Mechanical dependency-pin bump advancing the canonical Showcase
CopilotKit pin from `1.68.1` → `1.68.2`. This picks up the merged
**#6576** CopilotChat readiness fix onto staging. No source-logic
changes — pins + regenerated lockfiles only. This is the same pattern as
**#6510**.

## What changed

- `showcase/scripts/showcase-canonical-pins.json`:
`canonicalCopilotKitVersion` → `1.68.2` (overrides remain `{}`).
- Propagated the `@copilotkit/*` exact pins (`1.68.1` → `1.68.2`) across
all 22 showcase integrations + the Showcase shell (dependencies,
`overrides`, and `pnpm.overrides`).
- Regenerated every affected `package-lock.json` (`npm install
--package-lock-only`; integrations with `--legacy-peer-deps` per the
existing cmdk/react peer setup, shell strict — matching #6510).

**47 files changed:** 23 `package.json` + 23 `package-lock.json` + 1
canonical-pins JSON.

## Verification

- All `@copilotkit/*` siblings confirmed published on npm at `1.68.2`
(react-core, runtime, shared, core, react-ui, voice, a2ui-renderer,
sdk-js, web-inspector).
- Pin ratchet: `validate-pins.ts` → `FAIL=26`, hash unchanged
(`04b3415f…`) — matches the existing baseline exactly, so **no
re-baseline needed** (the bump neither added nor healed any FAIL).
- Shell `npm ci --ignore-scripts` succeeds and resolves
`@copilotkit/*@1.68.2` from the registry (matches the
`showcase_validate` workflow gate).
- Integration `npm ci` spot-check (built-in-agent) succeeds, installs
`@copilotkit/react-core@1.68.2`.
- Lockfile diffs are pin-only for integrations (0 structural churn); the
shell shows only npm's harmless `dev`↔`devOptional` reclassification
(same direction as #6510).

Refs #6576.
2026-08-19 20:34:17 -07:00
Jordan Ritter 069501d6dc chore(showcase): upgrade CopilotKit to 1.68.2 (lands #6576 readiness fix) 2026-08-19 20:16:28 -07:00
Mark 7cf869766b chore: release monorepo v1.68.2 (#6583)
## Release monorepo v1.68.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.68.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.68.2`
   - Creates git tag `monorepo/v1.68.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.68.2
2026-08-19 19:28:56 -07:00
contextablemark b0233c4eb0 chore: release monorepo v1.68.2 2026-08-20 02:19:06 +00:00
Mark bef2c440ba fix(react-core): gate CopilotChat submission on runtime readiness (empty assistant response) (#6576)
## Problem

Fleet-wide "empty assistant response": the assistant-message container
mounts but never receives text. #5801 (first released 1.63.0) deferred
the runtime `/info` call to a React effect, widening the "provisional
agent" window; 1.63.2 exposed an `isReady` signal on `useAgent` but
`CopilotChat` never consumed it. A chat submitted during the provisional
window is committed to the provisional agent and then lost when `/info`
swaps in the real agent — the user message and streamed assistant text
disappear, so the assistant bubble renders empty.

This was confirmed with a controlled SSE A/B: stock 1.68.1 does forward
`TEXT_MESSAGE_START → TEXT_MESSAGE_CONTENT → TEXT_MESSAGE_END` (the
runtime is fine — not the in-memory runner / #5837), but the `/info`
agent swap drops the rendered messages; restoring a readiness guard
makes the identical SSE render correctly.

## Fix

`CopilotChat` now consumes `isReady` from `useAgent` and withholds
`onSubmitMessage` until the runtime is ready:

```
- const { agent } = useAgent({ ... });
+ const { agent, isReady } = useAgent({ ... });
...
- onSubmitMessage: onSubmitInput,
+ onSubmitMessage: isReady ? onSubmitInput : undefined,
```

`CopilotChatInput` already derives `canSend` (and its Enter handler)
from `onSubmitMessage`, so withholding it while not-ready (a) disables
the send control and (b) makes Enter a no-op that **preserves** the
composer text — the message can't be committed to the doomed provisional
agent. No runtime/runner changes; no fixture re-recording.

## Red–green proof

New test `CopilotChat.readinessGate.test.tsx` drives the real readiness
race against the real `CopilotChat` submit path: holds the runtime in
Connecting (deferred `/info`), sends during the provisional window, then
resolves `/info` (the real status-change re-render that flips `isReady`)
and asserts the message survives to render an assistant response.

- **RED** (fix reverted): the chat body contains only chrome text — no
user message, no assistant response (the empty-container symptom).
- **GREEN** (fix applied): assistant text renders; passes 3×
consecutively (deterministic).
- Mutation-verified: reverting the fix reproduces RED.

## Verification

- react-core: **1468 tests pass** (0 regressions; 3 pre-existing
web-inspector `localStorage` jsdom-env file errors are unrelated and
present with and without this change).
- react-ui: **69 tests pass**.
- react-core typecheck (`tsc --noEmit`): **0 errors**.

## Follow-up (not in this PR)

The showcase D4 probe driver
(`showcase/harness/src/probes/drivers/d4-chat-roundtrip.ts`) should wait
for the send control to be enabled before pressing Enter (poll
`[data-testid="copilot-send-button"]` `disabled === false` after
typing). Omitted here because it can't be red-green'd without a live
showcase backend. Note this fix makes the follow-up more relevant: with
send gated, a probe that types + Enters during the provisional window
now silently no-ops.

Ref: #5801

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

https://claude.ai/code/session_017t7HsmM31NHNmUQrHF47pm
2026-08-19 19:09:31 -07:00
Jordan Ritter 88aa50ee65 fix(showcase): D4 driver guards readiness-wait budget and baselines after the wait
Two probe false-red guards around the react-core readiness gate:

- Guard the readiness waitForSelector(SEND_ENABLED) against an exhausted
  budget BEFORE issuing it. A type that drains the first-token envelope
  would otherwise issue a doomed ~1ms readiness wait that Playwright
  rejects and the outer catch mis-classifies as a generic level-error.
  Below SEND_READY_MIN_BUDGET_MS it now throws ReadinessBudgetExhausted
  (errorDesc: delayed-readiness) — an observable, specific red.

- Capture the per-attempt turn-lifecycle baseline AFTER type + the
  enabled-send wait and immediately before Enter (for both the initial
  attempt and retries). Taking it before the wait let a run completing
  DURING the wait land its edge past the snapshot, so the poll mistook it
  for THIS submitted turn finishing and false-red'd an empty container.

Adds a delayed-readiness/budget regression and a
counter-advances-during-wait regression; restructures the existing
press-guard test to drain during the readiness wait (via a new
sendEnableDelayMs fake option) so it still targets send-budget-exhausted.
2026-08-19 16:59:23 -07:00
Jordan Ritter e34bdb9fc3 fix(react-core): hide suggestion pills until ready; rework SSE test to real wrapper
Withholding onSelectSuggestion left the pill visually enabled but inert,
silently dropping a click during the provisional (!isReady) window. Hide
the pills until isReady (pass an empty suggestions list so the view's
existing hasSuggestions gate keeps them off-screen); retain the handler
gate as defense-in-depth for custom chatView slots. The suggestion-gate
test now asserts the pill is absent while provisional and appears/works
once ready.

Rework the production-shaped SSE regression to render the real public
CopilotKit wrapper (components/copilot-provider/copilotkit) with
runtimeUrl + agent="agentic_chat", advertising agentic_chat in the mocked
single-endpoint info response and relying on the wrapper's default
useSingleEndpoint=true — removing the synthetic GET /info 404 fallback so
the test exercises the same provider chain and POST info/agent-run path
as Showcase.
2026-08-19 16:52:47 -07:00
Benjamin Taylor f94d1ab0fb fix(runtime): name useSingleEndpoint when a single-route envelope hits a multi-route runtime
The v1-compatible `<CopilotKit>` provider pins `useSingleEndpoint` to `true`,
so it POSTs `{ method: "info" }` at the base path. A multi-route runtime — the
default — matches no route for that path and answered a bare `{"error":"Not
found"}`, which is indistinguishable from a wrong `basePath` or an unmounted
handler. Two independent onboarding validation runs hit this on their first
attempt and had to guess the cause.

The runtime now recognises the envelope at the one point multi-route routing
gives up, and answers the 404 with a message naming the prop, plus a
`logger.warn` so it also lands in the dev server terminal. Status and shape are
unchanged for every other miss.

That message was reaching nobody: all four `/info` callers threw away the
response body and reported only the status. They now route through
`runtimeInfoError`, which folds a string `message` from the body into the
error — so any future server-side diagnosis reaches the developer too.

Docs: five pages paired a v2 multi-route handler with `<CopilotKit>` without
mentioning the prop. Their snippets now pass `useSingleEndpoint={false}` and
link to the provider/handler mapping. `backend/runtime-endpoints.mdx` already
documents the pairing and is untouched; `cookbook/arcade.mdx` deliberately uses
single-route mode and already explains it.

Closes OSS-882

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 17:40:31 -05:00
Jordan Ritter f39d5a5b97 fix(showcase): D4 driver waits for enabled send control before Enter (readiness gate)
The react-core readiness fix disables the send control and no-ops Enter
while useAgent().isReady is false (the provisional agent /info swaps out).
An early probe Enter during that window is a silent no-op, leaving an empty
assistant response that falsely reds the cell. sendTurn now waits for
[data-testid="copilot-send-button"]:not([disabled]) after typing and before
pressing Enter, so the send lands on the real bound agent. Adds a
driver-ordering test (type -> wait-for-enabled-send -> Enter) with a fake
page that models the readiness gate.
2026-08-19 15:04:24 -07:00
Tyler Slaton 0d56e704f1 feat(web-inspector): pop the Inspector into its own window (#6563)
## What
The Inspector can open in a real browser window. The same live session
stays in that window. The app page hides the Inspector until you close
the extra window.

## Why
The Inspector covers the app. Some people want it beside the app, like
Chrome DevTools or a YouTube pop-out.

## How
The existing Inspector client opens a blank named popup. It portals the
same instance into that window. There is no second app, no extra page,
and no new backend.

## Notes
- Close the extra window to put the Inspector back. It stays open in the
same float or dock mode.
- If the browser blocks the popup, allow popups for the site.
- A refresh closes the extra window. It does not reopen as a pop-out.
- Docs update for this feature is landing in a follow-up commit on this
branch.

Draft until the last docs and a manual check are done.
2026-08-19 15:01:52 -07:00
Jordan Ritter f8160e09b9 fix(react-core): gate suggestion submission on readiness + production-shaped SSE regression test 2026-08-19 14:33:25 -07:00
Tyler Slaton 9087121bd5 fix(web-inspector): preserve state across pop-out 2026-08-19 14:03:12 -07:00
Alem Tuzlak 1ef16b6789 feat(web-inspector): pop the Inspector into its own window
Keep the same live Inspector session in a named browser popup, restore it when the popup closes, and document the workflow.
2026-08-19 14:03:12 -07:00
Tyler Slaton 463f589b4c feat(react-core): add local message inspector links (#6575) 2026-08-19 13:43:46 -07:00
Jordan Ritter 9b0e3dc88a fix(react-core): gate CopilotChat submission on runtime readiness (empty assistant response) 2026-08-19 13:37:03 -07:00
Tyler Slaton f1b26e4aa8 fix(react-core): hide local inspector action in production 2026-08-19 13:28:26 -07:00
Tyler Slaton 367e7bda15 feat(react-core): add local message inspector links 2026-08-19 13:28:26 -07:00
Ben Taylor 092058b224 docs(a2ui): require a literal-or-binding union for bound props (refs OSS-857) (#6573)
Four defects were reported from a LangGraph TypeScript + Next.js
onboarding run. Two
were unverified and one was unsound as stated, so each was reproduced or
traced to
source before anything was written. Two needed a fix, one needed a fix
plus a package
re-export, and one turned out to be correct as documented.

## Per-defect findings

**Defect 1 — bound props need a literal-or-binding union schema. REAL,
and the page said the opposite.**
Confirmed, with the mechanism. `scrapeSchemaBehavior` in
`@a2ui/web_core`'s
`GenericBinder` decides whether to resolve a `{ path }` binding by
inspecting the prop's
Zod type: a `ZodUnion` containing an object with a `path` key (and no
`componentId`)
becomes `DYNAMIC`; everything else falls through to `STATIC`, whose
handler is
`case 'STATIC': return value;`. So a bound prop declared as a plain
`z.string()` is never
resolved and the raw `{ path: "/origin" }` object reaches the renderer,
where the first
thing that renders it as text throws React error #31.

The page was not merely silent about this — it asserted the opposite:

> The A2UI binder resolves those paths *before* the React renderer runs,
so renderer
> props are typed as their resolved values (plain `z.string()`, not a
path-or-literal union).

The reference cell has declared the union all along and carries a
comment naming the exact
React error, but that comment sits *outside* the
`@region[definitions-types]` marker, and
`extractRegion` returns only the lines between the markers — so it never
reaches the page.
The rule is now stated where the reader declares the prop, with the
failure mode.

**Defect 2 — `DynamicStringSchema` is not re-exported. Explanation 2:
the symbol exists in a package that had not been searched.**
It is real and it is not a wished-for helper. It lives in
`@a2ui/web_core` at
`src/v0_9/schema/common-types`, reachable on the export map as
`@a2ui/web_core/v0_9`, and
it is a three-member union (`z.string()`, `DataBindingSchema`,
`FunctionCallSchema`) —
slightly wider than the two-member `DynString` the reference cells
hand-roll. The earlier
search was correct that it appears nowhere under `packages/`; it is a
dependency symbol.

It is also genuinely unreachable for users: `@a2ui/web_core` is a plain
`dependency` of
`@copilotkit/a2ui-renderer`, so application code cannot rely on
importing it. Re-exported
from `@copilotkit/a2ui-renderer` with its numeric/boolean/list siblings
and their types,
and noted in the docs as an alternative to hand-rolling the union.

**Defect 3 — the quickstart recommended the host form that fails. REAL,
verified independently for both runtimes.**
The advice was *"try using `0.0.0.0` or `127.0.0.1` instead of
`localhost`"*, in shared prose.
For the Node runtime that is exactly backwards, and it was verified from
source and by
running it, not taken on report. Rewritten and split across the page's
existing
Python/TypeScript language tabs so neither runtime sees the other's
advice. Also corrected
the `0.0.0.0` half, which is wrong for both: it is a bind-all address
for a server, not a
target for a client URL.

**Defect 4 — `useSingleEndpoint` guidance for the compat component. NOT
A DEFECT. Nothing changed.**
The docs are right. The compat wrapper resolves its default at

`packages/react-core/src/components/copilot-provider/copilotkit.tsx:108`:

```tsx
useSingleEndpoint={props.useSingleEndpoint ?? true}
```

and the v2 provider maps `true → "single"`, `false → "rest"`, `undefined
→ "auto"`
(`CopilotKitProvider.tsx:616-620`, again at `777-781`). So omitting the
prop really does
keep a single-route default, and `useSingleEndpoint={false}` really is
what a multi-route
Runtime needs. The same `CopilotKit` component is exported from both
`@copilotkit/react-core`
and `@copilotkit/react-core/v2`, so the guidance holds for either
import. Reported as one
observation from one run rather than an established finding — it did not
survive checking.

## Two things worth flagging

**The URL is served by the root page, not the LangGraph one.** Both
`generative-ui/a2ui/fixed-schema.mdx` and
`integrations/langgraph/generative-ui/a2ui/fixed-schema.mdx` exist, and
the resolution is the
opposite of what the directory layout suggests: all three langgraph
slugs are
`docs_mode: generated` in their manifests, and in that branch root MDX
wins
(`[framework]/[[...slug]]/page.tsx:836-841`). Confirmed live — the
LangGraph-scoped copy is a
thinner, older duplicate that is **not served at that URL for any
framework**. Left in place,
but it is a trap for the next person and probably wants deleting
separately.

**Overlap with #6569.** That PR is still open and edits both files this
one touches.
`git merge-tree` against its head merges clean, so no action needed, but
the two should be
read together.

## Testing

Worktree off `origin/main` (which already contains #6566 and #6568).

**Defect 1 — mechanism, against the locked `@a2ui/web_core@0.10.4`.**
Schema classification:

```
plain z.string()  -> {"type":"STATIC"}
literal|binding   -> {"type":"DYNAMIC"}
```

End-to-end through the real `GenericBinder`, feeding `{ path: "/origin"
}` against a data
model of `{ origin: "SFO" }`:

```
z.string()      : typeof=object value={"path":"/origin"}
literal|binding : typeof=string value="SFO"
z.string() -> renderable as a React child? NO — React throws: Objects are not valid as a
              React child (found: object with keys {path})
literal|binding -> renderable as a React child? yes
```

**Defect 2 — the re-export works from the built entry point**, and
behaves identically to the
hand-rolled union in the binder (it has a third union member, so this
needed checking):

```
DynamicStringSchema parses a literal: "SFO"
DynamicStringSchema parses a binding: {"path":"/origin"}
hand-rolled union   -> {"type":"DYNAMIC"}
DynamicStringSchema -> {"type":"DYNAMIC"}
plain z.string()    -> {"type":"STATIC"}
```

**Defect 3 — both runtimes verified from CLI source, and the Node
binding reproduced.**
`@langchain/langgraph-cli@1.4.4` `dist/cli/dev.mjs:19` defaults `--host`
to `"localhost"`
and passes it to `serve({ hostname })`; `langgraph_cli-0.4.31`
`cli.py:664-666` defaults
`--host` to `"127.0.0.1"`. Reproducing what Node does with `{ host:
"localhost" }` on this
dual-stack machine:

```
node version: v22.14.0
bound to: {"address":"::1","family":"IPv6","port":42024}
  localhost   -> CONNECTED
  127.0.0.1   -> ECONNREFUSED
  ::1         -> CONNECTED
  0.0.0.0     -> ECONNREFUSED
```

`127.0.0.1` is refused by the very server `localhost` reaches — so the
old advice broke a
working setup.

**Rendered checks (`next dev`, body-inspected — this site soft-404s, so
no status codes were trusted).**
`/langgraph-typescript/...` and
`/langgraph-python/generative-ui/a2ui/fixed-schema`: root-file
marker present, LangGraph-file marker absent, new prose and the
React-error callout present,
old wrong sentence gone. The `{path}` braces render literally inside
`<code>` and the
`#declare-the-component-definitions` anchor resolves to a real heading
id.

Quickstart troubleshooting tabs resolve per framework, so the gating is
right:

```
/langgraph-typescript/quickstart   Python selected=false   TypeScript selected=true
/langgraph-python/quickstart       Python selected=true    TypeScript selected=false
/langgraph-fastapi/quickstart      Python selected=true    TypeScript selected=false
```

The `<Tabs>` nested in a list item renders as a real `<ul><li>` with a
working tablist, not
broken MDX.

**Suites.**

| Check | Result |
| --- | --- |
| `packages/a2ui-renderer` `tsc --noEmit` | pass |
| `packages/a2ui-renderer` build (`tsdown`) | pass, 143 files |
| `packages/a2ui-renderer` `vitest run` | 4 files, 22 tests passed |
| `oxlint` on the changed source | 0 warnings, 0 errors |
| `shell-docs` `npm run typecheck` | pass (exit 0) |
| `shell-docs` `npm run lint` | pass (exit 0) |
| `shell-docs` `npm run test` | 58/59 files, 420/421 tests |

The one failing test is `channels-docs.test.ts > publishes the Channels
overview only through
provider navigation`. It is **pre-existing on `origin/main`** and
unrelated to these files —
verified by reverting all three changes to a pristine checkout and
re-running it, where it
fails identically (`1 failed | 29 passed`).

## Conventions pass

Checked the added prose against the docs tree's actual conventions
rather than by ear, which
turned up four things worth changing:

- **`Callout type="warn"`** is the house spelling (84 uses vs 11
`warning`) — already correct.
- **Code identifiers in Callout titles are backticked** (95-odd
precedents, e.g.
``title="`identifyUser` is not an authentication gate"``). Mine wasn't;
fixed. Note these
render as *literal* backticks — verified that existing titles behave
identically on `/auth`,
  so this matches the site rather than diverging from it.
- **Dropped a hand-written code fence.** The first draft illustrated the
union with a synthetic
`ts` block that (a) wasn't valid TypeScript — an orphaned object
property with no enclosing
object — and (b) duplicated the `<Snippet region="definitions-types" />`
rendered immediately
below it. Hand-copied code next to the generated snippet is exactly the
drift the snippet
architecture exists to prevent, so the prose now names `DynString` and
`Airport`'s `code` and
lets the snippet carry the code. Confirmed those two names are present
in **all 21**
integration cells that feed this page, since the root page serves every
framework.
- **Matched local line-style.** The quickstart's other troubleshooting
bullets are single
unwrapped lines, so the new bullet's prose is too; the a2ui page wraps
at ~70–80 columns and
  the new paragraphs match that.

Also tightened two things for accuracy over emphasis: the binder rule
now says "a union with a
`{ path }` member" rather than "a union containing an object with a
`path` key", which was
over-broad (a `{ componentId, path }` member is classified `STRUCTURAL`,
not `DYNAMIC`), and
the package comment was cut from nine lines to six to sit better among
that file's one-line
section labels.

Re-verified after the rewrite: `tsc` pass, `vitest` 22 passed, `oxlint`
clean, `oxfmt` clean,
shell-docs typecheck/lint pass, tests unchanged at 420/421 with the same
pre-existing channels
failure, and both pages re-rendered — anchor still resolves, tabs still
resolve per framework
(`langgraph-python` → Python, `langgraph-typescript` → TypeScript).

Out of scope and untouched: `snippets/shared/premium/inspector.mdx`. No
changeset added.
Does not close OSS-857.
2026-08-19 15:27:44 -05:00
Benjamin Taylor 4df1e3dccd docs(a2ui): require a literal-or-binding union for bound props (refs OSS-857)
Three findings from a LangGraph TypeScript onboarding run, plus the
supporting re-export.

The A2UI binder decides whether to resolve a `{ path }` binding by
inspecting the prop's Zod type: `scrapeSchemaBehavior` classifies a
`ZodUnion` containing an object with a `path` key as DYNAMIC and
everything else as STATIC, and STATIC returns the value untouched. A
bound prop declared as a plain `z.string()` therefore reaches the
renderer as the raw `{ path: "/origin" }` object, and the first thing
that renders it as text throws React error #31. The fixed-schema page
said the opposite — that renderer props are "plain z.string(), not a
path-or-literal union" — so the obvious declaration produced an opaque
crash. The reference cell already declares the union and carries a
comment explaining why, but that comment sits outside the
`definitions-types` region marker and so never reaches the page.

`DynamicStringSchema` is real; it lives in `@a2ui/web_core`, which is a
transitive dependency of `@copilotkit/a2ui-renderer` and so not
reliably importable from application code. Re-exported here with its
numeric/boolean/list siblings and their types.

The LangGraph quickstart's troubleshooting advice told everyone with a
connection problem to swap `localhost` for `0.0.0.0` or `127.0.0.1`.
That is backwards for the Node runtime: `langgraphjs dev` defaults to
`--host localhost`, which Node resolves to IPv6 and binds `::1` only,
so `127.0.0.1` is refused by the same running server. The Python CLI
defaults to `--host 127.0.0.1` and behaves the other way, so the advice
is now split across the page's existing Python/TypeScript language tabs
instead of stated once in shared prose.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 14:48:49 -05:00
Ben Taylor 68d6c5c62d docs(inspector): document mounting the Inspector in Angular (refs OSS-857) (#6572)
## What this fixes

The Inspector is `cpk-web-inspector`, a framework-agnostic web component
from
`@copilotkit/web-inspector`. `@copilotkit/angular` does not reference
that package
and does not mount the element, so an Angular application has to create
it
itself. Nothing in the docs said so.

It was worse than a missing paragraph. `ANGULAR_DOC_REDIRECTS` mapped
the
`inspector` slug onto `guides/troubleshooting`, so
`/angular/*/inspector`
**redirected away from the Inspector**, and the Angular sidebar's
"Observe & Operate" section contained a single entry — the VS Code
extension:

```
== Observe & Operate
- VS Code Extension [vs-code-extension]
```

## What I documented

New Angular-owned page, `frontends/angular/inspector.mdx`, sourced from
`examples/integrations/adk-angular/src/app/web-inspector.ts`:

- the Inspector is a web component and `@copilotkit/angular` does not
mount it
- the mount component: `afterNextRender`, reuse-or-create, append to
`document.body`
- `inspector.core = copilotKit.core` plus `auto-attach-core="false"`,
and why —
  given no core the element hunts for development globals such as
`window.__COPILOTKIT_CORE__`, so turning the search off is what
guarantees it
  observes the app's core and never a different one
- anchoring the launcher bottom-left, clear of a chat panel's close
button
- keeping it out of production builds via `@defer (when isDev)` +
`isDevMode()`
- server rendering (`afterNextRender` + the deferred import vs.
`customElements`)
- cleanup through `DestroyRef.onDestroy`

Plus: the redirect is gone so the page is reachable, and
`frontends/angular/guides/troubleshooting.mdx` links to it.

**React's Inspector content is untouched** — not edited, not moved, not
gated.

### House style

Checked against the eleven existing Angular-owned pages rather than
written to
taste, which changed four things from my first draft:

- **`## Next steps` with a bare link list.** Every Angular guide closes
that way;
  I had `## Related` with a prose gloss per link.
- **No `<video>`.** No Angular-owned page embeds media, and none uses
`<Callout>`
or `<Steps>` either — that surface is prose, tables, and fences. I had
carried
  the Inspector video over from the shared snippet.
- **Imperative task headings**, matching "Send the current session" /
  "Validate every runtime request" in `auth.mdx`: "Mount the element",
  "Supply the application's core", "Position the launcher". I had
  "Mount it yourself" and "Hand it the application's core".
- **Declarative sentences, no rhetorical fragments.** "The mount is
yours, so the
exclusion is yours as well." and a bare "`DestroyRef.onDestroy` does."
are not
  this surface's register; both are now plain statements of mechanism.

Frontmatter (`title`/`description`/`icon`/`doc_type: how-to`), h2-only
structure, ~80-column wrapping, and the `{runtimeUrl}` placeholder
convention all
follow the siblings. `<AngularSnippet region=…>` does **not** apply —
that
component pulls code extracted from the Angular Showcase at build time,
and this
mount component is not in the Showcase. Nav needs no `meta.json` entry
either:
`frontends/meta.json` carries only a title, and the Angular sidebar is
derived in
`getAngularDocsNavTree`. Verified the entry renders anyway.

### Two deviations from the brief, both deliberate

**1. Structural gating instead of `<FrontendOnly frontend="angular">` in
the
shared snippet.** The brief described
`snippets/shared/premium/inspector.mdx` as
the real Inspector content with the per-framework pages as shims onto
it. On
current `main` that is only half true: `docs/inspector.mdx` is now a
131-line
standalone page that does **not** render `<Inspector />`, and it is what
the
Angular root and every `docs_mode: generated` framework resolve to. I
built the
`FrontendOnly` version first and it forced the Angular guide to be
duplicated
into two files that had already diverged. An Angular-owned page instead
matches
how all eleven existing Angular guides work, keeps one source of truth,
and
gates by resolution rather than by branch.

The repo's own test agrees on the direction —
`angular-docs-content.test.ts`
lists `<FrontendOnly` in `REACT_ONLY_CONTENT`, i.e. it treats the tag as
something that should not reach the Angular surface.

That test also gave me a real mutation check for free. My first attempt
leaked
React's `<CopilotKit … enableInspector={false}>` into 19 Angular pages,
and the
suite caught every one:

```
× keeps the complete Angular surface free of another frontend's code
+   "inspector: <CopilotKit
+   publicLicenseKey={process.env.NEXT_PUBLIC_COPILOTKIT_LICENSE_KEY}
+   enableInspector={false}
+ >",
× keeps every Angular and backend combination frontend-native
    expected [ …(18) ] to deeply equal []
```

**2. I document the CSS override for positioning, not
`setAttribute("anchor", …)`.**
The scaffold sets that attribute, but `cpk-web-inspector` never reads
it. Runtime
proof against the built `dist`:

```
observedAttributes: ["auto-attach-core"]
static properties keys: ["core","autoAttachCore","_capabilitiesVersion"]
'anchor' observed? -> false
```

There is no `getAttribute("anchor")` anywhere in the package, and
`defaultAnchor`
(the prop React's `CopilotKitInspector` accepts) is not consumed either.
What
actually moves the panel is the CSS in the scaffold's own `styles.css` —
as its
comment already says: "CSS in styles.css enforces this too." So the docs
describe
the mechanism that works. **The scaffold has one dead line** its owner
may want
to drop; I did not touch it (see below).

## The adk-angular dependency is discharged

`examples/integrations/adk-angular` is planned for removal, and its
`web-inspector.ts` comment was the only written record of this pattern.
That
pattern is now documented. **Whoever removes that scaffold no longer
needs to
preserve it.** I only read the scaffold — no file under
`examples/integrations/adk-angular` is modified by this PR.

## The VS Code extension claim: both halves reproduced

The report said Angular users are pointed at a VS Code extension
instead, that
its `cpk-debug-events` endpoint is documented at the wrong path, and
that it
produced no events for a real run. I verified each independently rather
than
acting on the report.

**Pointed at the extension — confirmed.** See the one-entry sidebar
above.

**Wrong path — confirmed, and fixed.** The router suffix-matches
`cpk-debug-events`, but a runtime mounted with a `basePath` rejects
anything
outside it. Against a real runtime on `basePath: "/api/copilotkit"`:

```
runtime mounted at basePath=/api/copilotkit, NODE_ENV=development
/cpk-debug-events                -> 404  application/json  {"error":"Not found"}
/api/copilotkit/cpk-debug-events -> 200  text/event-stream  ": connected\n\n"
/api/copilotkit/info             -> 200  application/json   {"version":"1.64.1",…}
```

The docs said "available at `GET /cpk-debug-events` on your CopilotKit
runtime"
and gave the panel default as the bare origin `http://localhost:4000`,
so a
reader supplying their server's origin gets a 404. Now documented as
`GET {runtimeUrl}/cpk-debug-events`, base-path-relative, with the worked
`localhost:8200` example and a `curl` check, in both
`troubleshooting/event-inspector.mdx` and `vs-code-extension.mdx`.

**No events for a real run — confirmed, cause is runtime mode.** The
debug bus is
fed from exactly one place, `handlers/shared/sse-response.ts`, reached
only by
`handlers/sse/run.ts` and `handlers/sse/connect.ts`. An
Intelligence-configured
runtime dispatches to `handlers/intelligence/run.ts` and
`handlers/intelligence/connect.ts`, which return `Response.json` and
hand the
browser a realtime connection — no AG-UI event ever passes through the
runtime's
SSE layer. Neither file mentions `debugEventBus`. So on an
Intelligence-backed
runtime the endpoint connects, emits `: connected`, and then stays
silent
forever. That is now a callout on the event-inspector page pointing
readers at
the in-app Inspector, which reads the events client-side.

I did not change runtime code for this — it is a docs-accuracy gap, and
whether
the Intelligence path *should* feed the bus is a product decision, not
mine to
make here.

## Testing

From `showcase/shell-docs`:

**`npm run test`** — 403 passed, 1 failed, and that failure is
pre-existing on
`origin/main`. Verified in a pristine `origin/main` worktree with no
changes:

```
❯ src/lib/__tests__/channels-docs.test.ts (30 tests | 1 failed)
    × publishes the Channels overview only through provider navigation
```

It asserts `channels-architecture-dark.png` in the Channels overview
source and
is unrelated to anything here. The seven `angular-docs-content.test.ts`
tests —
the ones that police frontend separation — all pass.

**`npm run typecheck`** — identical output on my branch and on a
pristine
`origin/main` worktree (5 pre-existing `@testing-library/react`
resolution
errors from my symlinked `node_modules`, all in test files I did not
touch). No
new errors.

**`npm run lint`** — exit 0, no warnings in any file I changed.

**`npx oxfmt --check`** on the one `.ts` file — "All matched files use
the
correct format."

### Render check, both namespaces

`next dev`, following redirects, checking bodies rather than status
codes since
this site soft-404s:

| URL | http | Angular mount content | React `enableInspector` |
| --- | --- | --- | --- |
| `/angular/langgraph-typescript/inspector` | 200 | yes
(`afterNextRender`, `auto-attach-core`, `cpk-web-inspector`) | **no** |
| `/langgraph-python/inspector` | 200 | **no** | yes |

Each namespace shows only its own instructions. The only `tsx` string on
the
Angular page is Next.js dev chunk filenames, not content.

Before this change `/angular/langgraph-typescript/inspector` answered
`307 -> /angular/langgraph-typescript/guides/troubleshooting`.

Also confirmed 200-with-content, no redirect, and the mount instructions
present
on `/angular/inspector`, `/angular/google-adk/inspector`, and
`/angular/mastra/inspector`; the sidebar now carries
`href="/angular/langgraph-typescript/inspector"` under "Observe &
Operate"; the
Angular troubleshooting page links to it; and the new event-inspector
callouts
render in both the React and Angular namespaces with `/inspector`
correctly
rewritten to `/angular/<backend>/inspector`.

## Notes for reviewers

- **OSS-857 stays open** — other defects on it are unresolved.
- No changeset, per this repo's release process.
- Follow-up for the adk-angular owner, not done here:
`setAttribute("anchor", "bottom-left")` in `web-inspector.ts` is a no-op
and
can be deleted; the `styles.css` rule below it is what positions the
panel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-19 14:46:03 -05:00
Ben Taylor d77cd3815d test(sdk-python): revalidate the poetry lock and cover the partialjson parse path (#6547)
## What does this PR do?

Follow-up to #6123, which relaxed `partialjson` to `>=0.0.8,<2.0.0`
(issue #4131). Two loose ends from reviewing that change:

1. **`sdk-python/poetry.lock` was left invalid.** Any edit to the
dependency table invalidates the lock's content-hash, so on current
`main` a bare `poetry install` in `sdk-python` fails with
`pyproject.toml changed significantly since poetry.lock was last
generated`. Refreshed here.
2. **The parse path had zero test coverage.** `partialjson` has exactly
one consumer — `JSONParser().parse(...)` at `copilotkit/runloop.py:306`
— sitting inside a bare `except` that returns `None`. Every failure mode
therefore degrades to "no predicted state was emitted", which nothing
asserted. A widened range with no tripwire under it means a future
release inside `<2.0.0` could regress streaming predicted state
silently.

## Choices worth flagging

- **Refreshed the lock with Poetry 2.1.3**, the generator the lock
itself names. Poetry 2.4.x rewrites the header and adds unrelated
entries (~120 lines of churn); 2.1.3 keeps the diff to the hash.
- **Moved `partialjson` to 1.1.0 in the lock** (`poetry update
partialjson`, 7 insertions) so CI exercises the version a fresh install
now resolves, rather than the floor of the range.
- **Left `ag-ui-langgraph` at 0.0.42 on purpose.** A from-scratch
resolve (deleting the lock rather than refreshing it) pulls 0.0.43,
which fails four `test_intercepted_tool_call_events` tests on
`AttributeError: 'LangGraphAGUIAgent' object has no attribute
'emit_raw_events'`. That latent break is worth its own issue; it is not
addressed here.
- **Tests are version-agnostic about intermediate frames.** Values
mid-stream legitimately differ across the allowed range — 1.1.0
preserves trailing whitespace inside a partially streamed string where
0.0.8 dropped it — so the assertions pin what the range must keep: a
completed payload parses exactly, and a prefix yields a prefix.
- Not touched here:
`examples/integrations/langgraph-{fastapi,python}/Dockerfile:50` still
hardcode `"partialjson>=0.0.8,<0.0.9"`, a hand-copy of the old pin that
keeps those two images on 0.0.8.

## Testing

**Lock validity — the actual bug, before and after**

Pristine `main` before #6123 was consistent; #6123's one-line edit is
what broke it. Verified in a clean worktree:

```
# main + only the constraint edit
$ poetry check --lock
Error: pyproject.toml changed significantly since poetry.lock was last generated.
$ poetry install --with dev
Installing dependencies from lock file
pyproject.toml changed significantly since poetry.lock was last generated. Run `poetry lock` to fix the lock file.

# with this PR's lock
$ poetry check --lock
(no error)
```

**CI simulated exactly** (`poetry lock && poetry install --with dev`, as
in `test_unit-python-sdk.yml:54`):

```
resolved partialjson in venv: 1.1.0
locked ag-ui-langgraph: version = "0.0.42"
230 passed, 11 skipped, 18 warnings in 0.75s
```

225 were passing before; the 5 new tests are the difference.

**New tests pass on every version the range admits**

```
partialjson 0.0.8 -> 5 passed
partialjson 0.0.9 -> 5 passed
partialjson 0.1.0 -> 5 passed
partialjson 1.0.0 -> 5 passed
partialjson 1.1.0 -> 5 passed
```

**Mutation-checked, three ways** — the tests were confirmed to fail when
the mechanism is broken, not merely to pass:

| Mutation to `JSONParser.parse` | Result |
| --- | --- |
| baseline (unmodified) | 5 passed |
| `raise RuntimeError` | 4 failed, 1 passed |
| return values with strings reversed | 3 failed, 2 passed |
| always return `{}` (the realistic silent regression) | 3 failed, 2
passed |

For contrast, the same "disable `parse` entirely" mutation against the
pre-existing suite left **all 225 tests passing** — which is what
motivated this file.

The one test that survives every mutation is
`test_unterminated_escape_does_not_escape_predict_state`, by design: it
asserts that a prefix older versions reject stays contained by the bare
`except`, so a raising parser is the case it exists to tolerate.

**Behavioural evidence that 1.1.0 is safe in the lock** (from reviewing
#6123): a differential fuzz over every prefix of 9 realistic streamed
tool-call payloads, 1232 cases per version across 0.0.8 / 0.0.9 / 0.1.0
/ 1.0.0 / 1.1.0 — zero parse-to-raise regressions on any version, 75
cases improve from raise to parse, all 9 complete payloads parse
identically. Driving the real `predict_state()` at chunk sizes 1/3/7/20
gives a byte-identical final `predicted_state` on all five versions.
2026-08-19 14:38:51 -05:00
Ben Taylor 3801de3708 docs(runtime): map the provider/handler pairs and guard BuiltInAgent (refs OSS-857) (#6569)
Follow-up to #6566. Fixes **defects 5 and 9** of OSS-857, plus the half
of **defect 6** that lives on the Built-in Agent quickstart. Defects **1
and 2** are deliberately left — they land with the non-interactive
`project list`/`select` work, since the real fix is tooling that
provisions and names the key, not prose.

**Do not close OSS-857 on this PR** — 1 and 2 remain.

## The finding that reframes defect 5

The three names are **not interchangeable**. They pair up, and nobody
had written the pairing down. Traced through source, not inferred:

| Provider | `useSingleEndpoint` | Transport | Needs handler |
| --- | --- | --- | --- |
| `<CopilotKit>` (v1 wrapper) | omitted → `true` | `single` |
single-route |
| `<CopilotKit>` | `{false}` | `rest` | multi-route |
| `<CopilotKitProvider>` (v2) | omitted | `auto`, detected from `/info`
| either |
| `<CopilotKitProvider>` | `{true}` | `single` | single-route |

`copilotkit.tsx:108` is the whole story:
`useSingleEndpoint={props.useSingleEndpoint ?? true}`. The v1 wrapper
renders `<CopilotKitProvider>` internally and **pins single-route
transport unless you pass the prop.** So the LangGraph quickstart is
internally coherent — v1 provider asks for single,
`copilotRuntimeNextJSAppRouterEndpoint` serves single — which is exactly
why chat works there and Threads cannot.

### The constraint nobody had documented

I swept every v1-era wrapper:

- `copilotRuntimeNextJSAppRouterEndpoint` →
`createCopilotEndpointSingleRoute`
- `copilotRuntimeNodeHttpEndpoint` → `createCopilotEndpointSingleRoute`
- `copilotRuntimeNextJSPagesRouterEndpoint`,
`copilotRuntimeNodeExpressEndpoint`, `copilotRuntimeNestEndpoint` → all
delegate to `copilotRuntimeNodeHttpEndpoint`

**Every one builds its handler with `mode: "single-route"` and exposes
no option to change it.** There is no v1-shaped multi-route handler
anywhere in the package.

The consequence is sharper than defect 5 as filed: **Rich Threads and
the Inspector are unreachable from the wiring both quickstarts teach, at
any provider setting.** Setting `useSingleEndpoint={false}` cannot fix
it — it just points the browser at routes the wrapper will not serve.
You need a v2 `CopilotRuntime` from `@copilotkit/runtime/v2` plus
`createCopilotRuntimeHandler`. That is a server-side change, not a
provider prop, and it is the structural reason defect 3's trap exists.
Worth its own ticket.

## Why I did not converge the quickstarts on v2

That was the original plan for this PR and I abandoned it after checking
the backend half. The split matters:

- **Frontend would have been free.** Both quickstarts already import
from `@copilotkit/react-core/v2`, where `CopilotKit` is labelled in
source as a *"V1 backward-compat re-export"*. `CopilotKitProvider` ships
from that same entry, and `CopilotSidebar` already depends on
`useLicenseContext` from it. Swapping is an import change.
- **Backend would not.** The multi-route handler takes a v2
`CopilotRuntimeLike`; the quickstart's v1 `CopilotRuntime` only reaches
it via an internal `.instance` getter that lazily news up a
`CopilotRuntimeVNext`. Converging means teaching v2 runtime construction
and dropping `ExperimentalEmptyAdapter` mid-quickstart — a real v1→v2
migration for every reader of the two highest-traffic pages.

v1 is supported, so the default path stays put. The mapping documents
all pairs instead, and the Threads upgrade stays a labelled, complete
recipe on the page the quickstarts already link to.

## What changed

**`backend/runtime-endpoints.mdx`** — new "Provider and handler pairs"
section: the provider table, the handlers-by-mode table, the
deprecated-alias mapping (`createCopilotEndpoint`,
`createCopilotEndpointSingleRoute`, and the Express pair), the wrapper
constraint above, and a "read the symptom" callout (a mismatch fails at
discovery — `GET {basePath}/info` 404s, or the Runtime rejects the
envelope — never in your application code).

**Both quickstarts** — a short callout naming the pair the page uses and
linking the mapping.

**Defect 9, `integrations/built-in-agent/quickstart.mdx`** — this is
what `/quickstart` actually serves (verified: both URLs return the
identical 8175-byte body; the root `quickstart.mdx` is a 17-line routing
shim that 308-redirects to `/`). `BuiltInAgent` extends `AbstractAgent`
and calls the model directly via `streamText`, so registering it as
`default` replaces the developer's agent rather than connecting to it.
Added a caution: readers with an existing agent take the frontend steps
here and the runtime wiring from their framework's quickstart.

**Defect 6, second half** — same page installed `@copilotkit/react-ui`
and never used it, importing `CopilotKit`/`CopilotSidebar` from
`@copilotkit/react-core/v2`. Dropped, matching #6566.

## Testing

```
$ npx vitest run
Test Files  1 failed | 58 passed (59)
     Tests  1 failed | 417 passed (418)
```

The one failure is `channels-docs.test.ts > publishes the Channels
overview only through provider navigation` — pre-existing, and proven so
in #6566 by stashing on a clean tree.

**I broke two tests and fixed them, which is worth recording** because
it caught a real defect in my first draft.
`angular-docs-content.test.ts` flagged:

```
built-in-agent/backend/runtime-endpoints: @copilotkit/react
langgraph-python/backend/runtime-endpoints: @copilotkit/react
... 10 surfaces total
```

`backend/runtime-endpoints.mdx` also serves the **Angular** surface, and
my provider prose named React packages there. Correct fix, not a
suppression: the provider axis is React-only — Angular's
`provideCopilotKit` has no `useSingleEndpoint` — so the provider table
is now `<FrontendOnly frontend="react">` with an Angular branch saying
only the handler half applies. Both Angular tests pass.

### Render checks

Per surface, `.md` and HTML:

| surface | provider table | Angular note | `@copilotkit/react` |
wrapper callout |
|---|---|---|---|---|
| langgraph-python | ✅ | — | 3 | ✅ |
| langgraph-typescript | ✅ | — | 3 | ✅ |
| angular | — | ✅ | **0** | ✅ |

The wrapper-constraint callout correctly stays on all three: it is a
server-side fact that applies to Angular too.

Defect 9 / 6b on `/quickstart` and `/built-in-agent/quickstart` — both
8175 bytes, caution present, `react-ui` gone from the install line, pair
pointer present.

Every link I added was **body-verified, never by status code** (this
site soft-404s with HTTP 200):

```
/langgraph-python/quickstart                 bytes=428497  soft404=0  h1=Quickstart
/                                            bytes=248255  soft404=0  h1=CopilotKit
/backend/runtime-endpoints                   bytes=375782  soft404=0  h1=Runtime HTTP endpoints
/langgraph-python/backend/runtime-endpoints   bytes=395130  soft404=0  h1=Runtime HTTP endpoints
```

New anchors confirmed present (`id="provider-and-handler-pairs"`,
`id="which-handlers-serve-which-mode"`), and the pointer rewrites into
the reader's namespace correctly — `/langgraph-python/backend/...` from
the LangGraph page, `/backend/...` from the root surface.

## Voice pass

A third commit runs a tone/voice check over everything added for
OSS-857, measured against the corpus instead of guessed. It also
corrects the wording that already landed in #6566, so the whole ticket
reads in one voice.

**Second person stays.** It is emphatically the house voice: 22 of 29
top-level and backend pages use `you`/`your`, and the three pages
involved used it **17, 23 and 64 times** before any of these edits.
Stripping it would make the new prose stand out, not blend in.
Mid-sentence `**bold**` also stays — the corpus does that 17 times.

What genuinely drifted, and is now fixed:

| Issue | Was | Now |
|---|---|---|
| British spelling | `honours` | `serves` |
| Third person on a second-person page | `A developer adding A2UI to an
agent they already wrote…` | `If you added A2UI to an agent you already
wrote…` |
| Essay register | `That default is the one thing to remember:` | plain
statement of the fact |
| Meta phrasing | `so this is the mapping` | `so this table is the
mapping` |
| Conversational | `no provider pairing to get wrong` | `to configure` |
| Conversational | `` `uvicorn` is told to listen on `8123` `` | ``
`main.py` sets uvicorn's port to `8123` `` |
| Literary | `you may also meet these deprecated aliases` | `Older code
may use these deprecated aliases` |
| Coinage | `agent construct` | `how the agent itself is built` |
| Coinage | `without that steer` | `Without it, the model tends to…` |
| Aphoristic Callout title | `Mismatched pair? Read the symptom, not the
code` | `A mismatched pair fails at discovery` |
| Epigram | `It replaces your agent; it does not connect to one.` | `It
replaces your agent rather than connecting to it.` |
| Redundancy | `nothing supplies persistence for you` | `nothing
supplies persistence` |

Two of these were objective, not stylistic: the corpus is American
English (`behavior` 66:6, `customize` 77:4, `serialize` 18:1, `organize`
15:0) and its only `honour` was mine; and it contains exactly two
instances of `a developer`, one of which was mine on a page that
addresses the reader directly throughout.

Callout titles were checked against the house set — declarative or plain
question (`v1 behaves differently`, `Three routes are not user-scoped`,
`Using a custom backend?`) — which is why the aphorism was the one
outlier.

Re-verified after the rewording: tests back to the single pre-existing
failure, every reworded string renders on the right surface, Angular
still shows **zero** React package mentions, and the `StateGraph` step
is still gated to langgraph-python + langgraph-fastapi only.

## Coordination

Draft PR #6112 (onsclom) also touches
`integrations/built-in-agent/quickstart.mdx`, but only two prose lines —
the signup sentence and the "Already have an app?" callout. My hunks are
the install line and the runtime step, so they should merge cleanly.
Flagging rather than assuming.

## Follow-ups this surfaced

- **Threads needs a v2 server migration** from either quickstart's
starting point. No v1-shaped multi-route handler exists. Own ticket.
- **Defects 1 and 2** ride the non-interactive project-selection work.
2026-08-19 14:38:47 -05:00
Benjamin Taylor 9ffe2546ce docs(inspector): document mounting the Inspector in Angular
The Inspector is the framework-agnostic `cpk-web-inspector` web component.
`@copilotkit/angular` does not reference or mount it, so an Angular app has to
create the element itself — and nothing said so. Worse, the Angular docs mapped
the `inspector` slug onto `guides/troubleshooting`, so `/angular/*/inspector`
redirected away from the Inspector entirely and the only thing left under
"Observe & Operate" was the VS Code extension.

Add an Angular-owned Inspector page covering the mount component, the
`core` handoff with `auto-attach-core="false"`, positioning, production
exclusion, server rendering, and cleanup on destroy. Drop the redirect so the
page is reachable, and point at it from the Angular troubleshooting guide.
React's Inspector content is untouched and unmoved.

Also correct the `/cpk-debug-events` path: it is relative to the runtime's
mounted `basePath`, not the server origin, and it only carries events for a
self-hosted SSE runtime — an Intelligence-backed runtime answers runs over the
platform's realtime connection, so the stream connects and stays empty.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 14:37:03 -05:00
Martha Kelly Schumann 7cb5205437 feat: add AWS Strands TypeScript starter (#6555)
## Summary

- add a standalone AWS Strands TypeScript starter with shared state,
tools, A2UI, Channels, and Docker support
- register the starter in parity, migration, docs, and PR smoke coverage
- publish the canonical `aws-strands-ts` command in the shared AWS
Strands documentation
- keep the starter out of the live Railway fleet map until a service is
provisioned

## Why

FAC-126 reports that `copilotkit create` offers AWS Strands only in
Python. The maintained TypeScript integration already exists in the
showcase, but there was no standalone starter for the CLI to download.

This is part 1 of 2.
[CopilotKit/Intelligence#878](https://github.com/CopilotKit/Intelligence/pull/878)
adds the CLI catalog entry and aliases. Merge this PR first, then merge
#878.

The CLI PR is pinned to reachable commit
`20e481b749141db40fb3126ed39d73e74e8b197c`.

## Validation

- `npm run typecheck` in the starter agent
- `npx tsc --noEmit` in the starter root
- `npm run build` in the starter root
- `pnpm parity:verify --target=strands-typescript`
- focused shell-docs tests and typecheck
- slug-map and starter-mapping drift tests
- repository config allowlist check
- agent module startup and strict CSV load
- GitHub starter Docker image build
- `git diff --check`

The local Docker smoke attempt could not extract the Playwright image
because the machine ran out of disk. GitHub CI runs the same Compose
smoke stack.

Linear:
[FAC-126](https://linear.app/copilotkit/issue/FAC-126/aws-strands-typescript-starter-missing-from-copilotkit-create)
2026-08-19 11:55:37 -07:00
Benjamin Taylor 6c1a9eb4b4 docs: match the house voice in the OSS-857 prose (refs OSS-857)
A voice pass over everything added for OSS-857, measured against the
corpus rather than guessed.

Second person stays: it is emphatically the house voice — 22 of 29
top-level and backend pages use you/your, and the three pages involved
used it 17, 23 and 64 times before any of these edits. Mid-sentence
`**bold**` for emphasis also stays; the corpus does that 17 times.

What actually drifted:

- `honours` → `serves`. The corpus is American English (behavior 66:6,
  customize 77:4, serialize 18:1, organize 15:0) and the single
  `honour` in it was mine.
- `A developer adding A2UI to an agent they already wrote…` → second
  person. The corpus contains exactly two `a developer`, and one was
  mine; the page around it addresses the reader directly throughout.
- Essay register: "That default is the one thing to remember:" → a plain
  statement of the fact. "so this is the mapping" → "so this table is
  the mapping".
- Conversational: "no provider pairing to get wrong" → "to configure";
  "`uvicorn` is told to listen on 8123" → "`main.py` sets uvicorn's port
  to 8123"; "you may also meet these deprecated aliases" → "older code
  may use these deprecated aliases".
- Coinages: "agent construct" → "how the agent itself is built"; "that
  steer" → "Without it, the model tends to…".
- Aphoristic Callout title "Mismatched pair? Read the symptom, not the
  code" → "A mismatched pair fails at discovery". House titles are
  declarative or plain questions ("v1 behaves differently", "Three
  routes are not user-scoped", "Using a custom backend?").
- Epigram: "It replaces your agent; it does not connect to one." → "It
  replaces your agent rather than connecting to it."
- Redundancy: "nothing supplies persistence for you" → "nothing
  supplies persistence".

The a2ui and LangGraph quickstart wording landed in #6566; those files
are corrected here so the whole ticket reads in one voice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 13:50:19 -05:00
copilotkit-qa-bot[bot] 90d36a62c7 Merge main into codex/fac-126-strands-ts-starter 2026-08-19 11:46:47 -07:00
Ben Taylor 1cafb58b4a ci: stop Playwright browser installs shelling out to apt (#6567)
Ports
[CopilotKit/website#529](https://github.com/CopilotKit/website/pull/529)
to this repo. CI workflow config only — six files under
`.github/workflows/`, no package or source code touched.

## The problem

Every Playwright browser install in this repo passes `--with-deps`,
which runs `apt-get update` before downloading the browser. apt on the
runners cannot always reach `azure.archive.ubuntu.com`; when it can't,
it retries for many minutes before falling back to `archive.ubuntu.com`.
In the website repo that burned ~14 of a job's 15 minute
`timeout-minutes` budget and the job was killed before it ran a single
test — and GitHub renders a `timeout-minutes` kill as "The operation was
canceled", so it shows up in the checks list looking like a test failure
rather than an infrastructure hang.

One thing is worse here than in website. Website only took the apt
branch on PRs that touched `pnpm-lock.yaml` (a cache-key interaction).
**This repo has no Playwright browser cache at all** — `grep -rn
ms-playwright .github/` returns nothing — so all six of these jobs shell
out to apt unconditionally, on every run.

## The change

```diff
-        run: pnpm exec playwright install --with-deps chromium
+        run: pnpm exec playwright install chromium
```

Chromium's system libraries are already present on the Ubuntu runner
images, and **every one of these six steps installs chromium only**, so
the browser download is all any of them needs. A comment records the
reasoning in-file at each site so the next person doesn't helpfully
restore `--with-deps`.

| Workflow | Step | Job timeout |
|---|---|---|
| `test_unit.yml` | Install Chromium for packed Angular browser smoke |
25m |
| `test_e2e-legacy-v1.yml` | Install Playwright browsers | 20m |
| `test_e2e-showcase-on-demand.yml` | Install Playwright | 15m |
| `test_showcase-frontend-matrix.yml` | Install Chromium | 45m / 15m |
| `showcase_eval.yml` | Install Playwright chromium | 45m |
| `showcase_capture-previews.yml` | Install Playwright | 30m |

## Verification

The substantive claim — "Chromium launches without `install-deps` on the
runner images" — is about the runner image and cannot be checked
locally. **This PR's own CI run is the verification, and it holds:** 23
checks pass, 2 skipped, 0 failures.

Two jobs here actually launch a browser:

- **`test / e2e / legacy-v1`** — success. All five example legs
(`chat-with-your-data`, `form-filling`, `research-canvas`,
`state-machine`, `travel`) ran their Playwright suites to completion on
`depot-ubuntu-24.04-4`.
- **`test / unit`** — success. The packed Angular browser smoke launched
Chromium on both Node 22.x legs.

Confirmed independently in ag-ui-protocol/ag-ui#2468, where all 24 `dojo
/ *` legs ran their Playwright suites green on `depot-ubuntu-24.04`
after the same change. Between the two PRs that is 31 browser-launching
jobs on Depot images with no `install-deps` anywhere.

### Measured effect

Same step, same workflow, `main` vs this branch:

| | `Install Chromium for packed Angular browser smoke` |
|---|---|
| `main` — run
[32271967199](https://github.com/CopilotKit/CopilotKit/actions/runs/32271967199)
| **86s**, **104s** |
| this PR — run
[32281720527](https://github.com/CopilotKit/CopilotKit/actions/runs/32281720527)
| **7s**, **8s** |

apt was ~92% of that step even on a run where it *wasn't* hanging. The
failure mode this PR removes is the tail, not the mean.

Static checks:

```
$ python3 -c "yaml.safe_load each touched workflow"
ok .github/workflows/test_unit.yml
ok .github/workflows/showcase_capture-previews.yml
ok .github/workflows/showcase_eval.yml
ok .github/workflows/test_e2e-showcase-on-demand.yml
ok .github/workflows/test_e2e-legacy-v1.yml
ok .github/workflows/test_showcase-frontend-matrix.yml
```

`actionlint` on the six touched files, `origin/main` vs this branch
(line:col stripped so comment insertions don't shift the comparison):

```
$ diff before.txt after.txt
before=27 after=27
IDENTICAL — no new actionlint findings
```

The 27 findings are pre-existing on `main` — `depot-ubuntu-*` runner
labels actionlint doesn't know, and shellcheck `SC2086`/`SC2129` info in
unrelated steps.

Current timings, for what the change is worth: on run
[32271967199](https://github.com/CopilotKit/CopilotKit/actions/runs/32271967199)
the `Install Chromium` step took **86s and 104s** across the two Node
22.x matrix legs. So apt is reachable from our runners *today* — this is
preventive, plus ~1–1.5 min per job, not a fix for something currently
red.

## Risk

~~Guido proved the no-`install-deps` claim on GitHub's `ubuntu-latest`.
Three of these jobs run on `depot-ubuntu-24.04-*`, and Depot mirroring
GitHub's image is the one thing this PR's CI needs to confirm.~~
**Resolved** — see Verification above; Chromium launches on the Depot
images.

The residual risk is coverage, not the claim. Four of the six workflows
are `workflow_dispatch`/`issue_comment`-gated and so do not run on this
PR: `test_e2e-showcase-on-demand`, `test_showcase-frontend-matrix`,
`showcase_eval`, `showcase_capture-previews`. Their edit is textually
identical to the two that *were* exercised and all six installed
chromium only, so this is one shared claim rather than four independent
ones — but say the word and I'll dispatch any of them against the branch
before merge.

If a library does turn out to be missing, the fallback is `timeout 300
pnpm exec playwright install-deps chromium` — bounding the hang instead
of removing it — rather than restoring the unbounded `--with-deps`.

## Notes for review

- **A companion change is needed in ag-ui.**
`apps/dojo/e2e/package.json` there has `"postinstall": "playwright
install --with-deps"`, and our `test_e2e-dojo.yml` installs that package
with `pnpm install` (scripts enabled), so our dojo job has been pulling
all three browser engines through apt while `playwright.config.ts`
declares a chromium project only. ag-ui's own workflow dodges it with
`--ignore-scripts`. Fixed in
[ag-ui-protocol/ag-ui#2468](https://github.com/ag-ui-protocol/ag-ui/pull/2468);
since e2e-dojo pulls ag-ui at floating `main`, it reaches this repo's CI
as soon as that lands.
- `showcase_capture-previews.yml` still does `sudo apt-get update &&
apt-get install -y ffmpeg` one step earlier, so that job keeps an apt
call with the same hang exposure. Left alone — separate concern, happy
to bound it in a follow-up.
- Dockerfiles under `showcase/` keep `--with-deps` deliberately: those
build on Debian/Alpine images that genuinely lack the libraries.
Local-dev docs (`examples/e2e/AGENTS.md`) are unchanged for the same
reason.
- No changeset: CI config only, nothing published.
2026-08-19 13:44:14 -05:00
Benjamin Taylor 4078a11f36 docs(runtime): map the provider/handler pairs and guard BuiltInAgent (refs OSS-857)
Fixes defects 5 and 9 from the OSS-856 phase 1 validation run, plus the
half of defect 6 that lives on the Built-in Agent quickstart. Every claim
was traced through package source.

Defect 5 — three provider/handler names presented as interchangeable.
They are not interchangeable; they pair up, and the pairing is what was
undocumented. Added a "Provider and handler pairs" section to
`backend/runtime-endpoints.mdx`:

- The v1 `<CopilotKit>` wrapper renders `<CopilotKitProvider>` internally
  and pins `useSingleEndpoint` to `true` unless the prop is passed
  (`copilotkit.tsx:108`), so it asks for single-route transport even
  against a multi-route Runtime. `<CopilotKitProvider>` with the prop
  omitted resolves to `auto` and detects from `/info`.
- A table of which handlers serve which mode, and the deprecated aliases
  (`createCopilotEndpoint`, `createCopilotEndpointSingleRoute`, and the
  Express pair) mapped to their replacements.
- The constraint nobody had written down: every `copilotRuntime*Endpoint`
  wrapper builds its handler with `mode: "single-route"` and exposes no
  option to change it. Next.js App Router and node-http call the
  single-route helper directly; pages-router, node-express and nest all
  delegate to node-http. So Rich Threads is unreachable from the wiring
  the quickstarts teach at ANY provider setting — it needs a v2
  `CopilotRuntime` plus a multi-route handler. That is the structural
  reason behind defect 3.
- Provider half is scoped to `<FrontendOnly frontend="react">` with an
  Angular branch, because this page also serves the Angular surface and
  `provideCopilotKit` has no `useSingleEndpoint`.

Both quickstarts gain a short callout naming the pair they use and
linking the mapping.

Defect 9 — the Built-in Agent quickstart (what `/quickstart` actually
serves) instantiates `new BuiltInAgent(...)` as the `default` agent with
nothing warning a reader who already has one. `BuiltInAgent` extends
`AbstractAgent` and calls the model directly via `streamText`, so
registering it replaces the developer's agent rather than connecting to
it — the `user_code_preservation` violation the ticket describes. Added a
caution telling readers with an existing agent to take the frontend steps
here and the runtime wiring from their framework's quickstart.

Defect 6, second half — the same page installed `@copilotkit/react-ui`
and never used it, importing `CopilotKit` and `CopilotSidebar` from
`@copilotkit/react-core/v2`. Dropped it, matching the LangGraph fix in
#6566.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 13:35:43 -05:00
copilotkit-qa-bot[bot] 573a614112 Merge main into codex/fac-126-strands-ts-starter 2026-08-19 11:34:30 -07:00
copilotkit-qa-bot[bot] b9d41c0e3a Merge main into codex/fac-126-strands-ts-starter 2026-08-19 11:31:21 -07:00
Mike Ryan f40e532822 docs(backend): construct the Intelligence client and name its key (refs OSS-857) (#6568)
Fixes OSS-857 defects 1 and 2, which turn out to be one root cause.

## The problem

`backend/runtime-endpoints.mdx` shows this:

```ts
const runtime = new CopilotRuntime({
  agents,
  intelligence,
  identifyUser: async (request) => { /* ... */ },
});
```

`intelligence` is a bare identifier. No import, no shape, no env source,
and no
page on the web path constructs it — so the example is not copyable.

That is also why `INTELLIGENCE_API_KEY` appeared to have no consumer,
which the
ticket called its "defect that matters most". `copilotkit project
select` writes
that key into `.env`, and `apiKey` on the Intelligence client is what
reads it.
Because no web page ever built the client, the variable looked orphaned.

## The fix

The construction was already documented correctly — but only on the
Channels
pages (`frontends/slack.mdx:120`, `frontends/teams.mdx`). This adds a
step to
the web path using that same pattern rather than inventing a second
vocabulary
for it:

```ts
import { CopilotKitIntelligence } from "@copilotkit/runtime/v2";

const intelligence = new CopilotKitIntelligence({
  apiKey: process.env.INTELLIGENCE_API_KEY!,
});
```

It also documents the paired-override rule for `apiUrl` / `wsUrl`,
because the
API and realtime planes are separate hosts and setting one alone logs a
warning.

## The Inspector page was not wrong

`NEXT_PUBLIC_COPILOTKIT_LICENSE_KEY` is the correct variable for the
Inspector.
The defect is that a reader whose `.env` holds `INTELLIGENCE_API_KEY`
cannot tell
whether the two are the same credential. So
`snippets/shared/premium/inspector.mdx`
now disambiguates them — publishable browser key versus server-side
project key,
with a pointer to what consumes the latter — rather than substituting
one for the
other.

## Verified against source, not inferred

- `CopilotKitIntelligence` is publicly exported from
`@copilotkit/runtime/v2`
  (`intelligence-platform` → `v2/runtime/index.ts` → `v2/index.ts`)
- `apiKey` is the only required field of `CopilotKitIntelligenceConfig`
- `apiUrl` and `wsUrl` default to the managed platform, and
`warnOnPartialHostOverride` logs a warning when one is set without the
other

## What I could not verify

The site build and its vitest suite did not run: this was authored in a
fresh
worktree with no installed toolchain, and `oxlint` covers JS/TS rather
than MDX,
so it would not have exercised these edits anyway. CI is the first real
gate.

Checked instead:

- `<Step>`, `<FrontendOnly>` and code-fence balance in both files
- both new links against existing usage in the content tree —
`](/inspector)`
  appears 7 times and `](/backend/runtime-endpoints)` 10 times. The site
soft-404s on unknown paths, so a link cannot be verified by status code.

## Scope

Defects 3, 4, 6, 7, 8, 10, 11 and 12 landed in #6566. Defects 5 and 9
are being
handled separately and are untouched here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-19 11:31:04 -07:00
Benjamin Taylor 99d3f99f4c docs(backend): construct the Intelligence client and name its key (refs OSS-857)
Fixes OSS-857 defects 1 and 2, which are one root cause. No page on the web
path ever constructs `CopilotKitIntelligence`, so `intelligence` reads as an
undefined identifier in the `new CopilotRuntime({ agents, intelligence,
identifyUser })` example, and `INTELLIGENCE_API_KEY` reads as a credential
with no consumer. `apiKey` IS that consumer.

The construction was already documented correctly, but only on the Channels
pages (frontends/slack.mdx, frontends/teams.mdx). This lifts the same pattern
onto the web path rather than inventing a second vocabulary for it.

Verified against packages/runtime source rather than inferred:

- `CopilotKitIntelligence` is exported publicly from `@copilotkit/runtime/v2`
  via intelligence-platform -> v2/runtime/index.ts -> v2/index.ts
- `apiKey` is the only required field of `CopilotKitIntelligenceConfig`
- `apiUrl` and `wsUrl` default to the managed platform, and
  `warnOnPartialHostOverride` logs a warning when one is set without the other,
  which is why the docs now say to override both together

The Inspector page was NOT wrong to show `NEXT_PUBLIC_COPILOTKIT_LICENSE_KEY` --
that is the correct variable for that purpose. The defect is that a reader whose
.env holds `INTELLIGENCE_API_KEY` cannot tell whether the two are the same
credential. So that page disambiguates rather than substitutes: publishable
browser key versus server-side project key, with a pointer to what consumes the
latter.

Not verified: the site build and its vitest suite. A fresh worktree has no
installed toolchain (oxlint is absent), and oxlint covers JS/TS rather than MDX,
so it would not have exercised these edits. What was checked instead: <Step>,
<FrontendOnly> and code-fence balance in both files, and both new links against
existing usage -- `](/inspector)` appears 7 times and
`](/backend/runtime-endpoints)` 10 times elsewhere in the content tree. The site
soft-404s on unknown paths, so a link cannot be verified by status code.

Defects 5 and 9 remain open and are not addressed here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 13:16:39 -05:00
Ben Taylor 15ee7c6e5f docs(langgraph): fix 8 verified defects in the LangGraph onboarding docs (refs OSS-857) (#6566)
Fixes 8 of the 12 defects in OSS-857 (the OSS-856 phase 1 validation
run). Defects **1, 2, 5 and 9** are owned by a parallel session working
in `backend/runtime-endpoints.mdx`,
`integrations/langgraph/inspector.mdx` and the root `quickstart.mdx` —
**do not close OSS-857 on this PR.**

Every claim below was re-derived from installed package source or a live
run. Nothing here is recalled.

## Scope correction worth flagging first

The task's file table mapped
`/langgraph-python/generative-ui/a2ui/fixed-schema` to
`integrations/langgraph/generative-ui/a2ui/fixed-schema.mdx`. That is
not the file the URL serves. `langgraph-python` is `docs_mode:
generated`, so `page.tsx` tries `loadDoc(slugPath)` first and the
**root** `generative-ui/a2ui/fixed-schema.mdx` wins. Defects 10, 11 and
12 all live in the root file, which is shared by 21 frameworks — a much
larger blast radius than the table implied. Confirmed by rendering, not
by reading:

```
$ curl -sL localhost:3013/langgraph-python/generative-ui/a2ui/fixed-schema | grep -c "Load the schema JSON at startup"
2                      # root file's schema-loading branch
$ ... | grep -c "Action handler details"
0                      # the langgraph-scoped file's heading — never served
```

**The langgraph-scoped
`integrations/langgraph/generative-ui/a2ui/fixed-schema.mdx` is dead
content at every URL.** Same for the sibling `index.mdx` and
`dynamic-schema.mdx`. Only `advanced.mdx` and `styling.mdx` survive,
because they have no root counterpart — and even those are unreachable
from the sidebar. Worth its own ticket; not touched here.

## What changed, defect by defect

### 3 — Route shape (partial, as scoped) ✅

Added a caution at the route step. Deliberately did **not** rewrite the
route.

The precise mechanism, which matters for how the caution is worded:
`copilotRuntimeNextJSAppRouterEndpoint` calls
`createCopilotEndpointSingleRoute`
(`packages/runtime/src/lib/integrations/nextjs/app-router.ts:39`). So
the quickstart's POST-only route is **single-route mode** and chat
genuinely works. It is not a broken route — it is the wrong mode for
Threads. The caution says exactly that, names the `[[...slug]]`
catch-all with `GET`/`POST`/`PATCH`/`DELETE`, ties it to the Inspector's
"Finish setting up Rich Threads" state, and links to
`/backend/runtime-endpoints#enable-rich-threads-routes` (anchor verified
present).

I did not assert that you can just add verbs to this handler — that
would be wrong, and it is entangled with defect 5, which the other
session owns.

### 4 — Port mismatch ✅

**Verified the default rather than trusting the ticket.** Both CLIs
default to **2024**:

```
$ npx @langchain/langgraph-cli@latest dev --help
  -p, --port <number>   port to run the server on (default: "2024")

$ grep -n -A3 '"--port"' langgraph_cli/cli.py
670:    default=2024,
```

The page was already internally consistent on 8123 — the trap is that
nothing said the bare command differs, and 8123 is the convention across
every sibling page (`deep-agents.mdx`, `deepagents/quickstart.mdx`, the
showcase integration). So I took the second option in the brief: keep
`--port 8123` and name the default. Added a callout at the start
command, a per-tab note on which port each path serves, and a
troubleshooting line.

Also fixed a **factually wrong comment** in
`showcase/integrations/langgraph-python/.env.example`, which claimed
"langgraph dev runs on port 8123 by default". That is the same
mis-belief, committed in the repo. One comment line, no behavior change;
called out here because it is outside the two files named in the brief.

### 6 — `@copilotkit/react-ui` installed and never used ✅

Checked the exports; **the install line was wrong**, not the import.

- `CopilotSidebar` is at
`packages/react-core/src/v2/components/chat/CopilotSidebar.tsx`,
exported via `@copilotkit/react-core/v2`.
- `@copilotkit/react-ui`'s `exports` map has no `./v2` JS entry at all —
only `./v2/styles.css`. Its root export does carry a v1
`CopilotSidebar`, which would be the wrong component under a v2
provider.
- `examples/v2/react/demo` does not depend on `@copilotkit/react-ui`.

Dropped it from the install line and said where the components come
from. No unused import added.

### 7 — Checkpointer guidance ✅

Added the reason to each tab; kept the code difference, which is
correct.

Both directions verified:

**LangSmith tab (bare compile).** `langgraph dev` sets
`LANGSMITH_LANGGRAPH_API_VARIANT="local_dev"`
(`langgraph_api/cli.py:271`), and under that variant `graph.py:821`
raises on a compiled-in checkpointer. Reproduced with a real boot:

```
error  Graph 'sample_agent' failed to load: ValueError: Heads up! Your graph 'graph'
from './main.py' includes a custom checkpointer (type <class
'langgraph.checkpoint.memory.InMemorySaver'>). With LangGraph API, persistence is
handled automatically by the platform, so providing a custom checkpointer ... isn't
necessary and will be ignored when deployed.
```

The server does not start. So a reader who "helpfully" adds
`MemorySaver()` here breaks their deployment — exactly the failure the
ticket predicted.

**FastAPI tab (`MemorySaver()` required).** `ag_ui_langgraph/agent.py`
calls `graph.aget_state(config)` (lines 236, 474), and
`Pregel.get_state`/`aget_state` raise `ValueError("No checkpointer
set")` when none is configured (`langgraph/pregel/main.py:1402`). The
checkpointer is not optional on that path.

### 8 — "Existing agent" install line ✅

Narrowed to `uv add langgraph langchain-openai langchain-core
python-dotenv`.

One correction to the ticket's framing: the over-add is **one** package,
not two. The line is shared by both tabs, and only `copilotkit` is
unused by the LangSmith path — the FastAPI tab already re-adds it in its
own step alongside `ag-ui-langgraph`. And `python-dotenv` stays: the
code in **both** tabs does `from dotenv import load_dotenv`, so removing
it because `langgraph.json` declares `"env"` would break the shown
snippet. Removing an import's package is not a docs fix.

Added the pinned-dependency warning.

`doctest.json` **needs no change**: its dep list backs the
`doctest="server"` snippet (the FastAPI `main.py`), which still imports
all eight. Checked rather than assumed.

### 10 — Three identical conditional branches ✅ (root cause was code,
not prose)

The HTML page was already correct — only the `schema-loading` branch
renders for langgraph-python. The defect is entirely in the **`.md` view
the validation run read**: `renderPageToLlmText` applied
`filterFrontendScopedBlocks` and `filterAngularBackendScopedBlocks` but
never `filterFrameworkScopedBlocks`, so raw Markdown emitted all three
branches *with the literal JSX tags*, and every `<Snippet>` inside them
resolved against the one requested framework.

Before:

```
286:<WhenFrameworkHas flag="a2ui_pattern" equals="schema-loading">
402:<WhenFrameworkHas flag="a2ui_pattern" equals="schema-inline">
519:<WhenFrameworkHas flag="a2ui_pattern" equals="llm-driven">
```

…with byte-identical Python under each. That is what put "the host
language doesn't ship a `load_schema` JSON loader" directly above a
snippet calling `a2ui.load_schema` — the prose was never wrong for its
own framework, it was just being shown to the wrong one.

Fixed by gating on the same framework the snippets resolve to (routed
through `pickFramework`, so prose and code agree even on unscoped
`/<slug>.md`). This also repairs the same class of bug for the other
five gated flags across every framework's `.md`. The filter is flat-only
by design, so the new block is a sibling, not nested.

Regression test added and **mutation-checked** — disabling the filter
fails it:

```
× raw Markdown keeps only the active framework's <WhenFrameworkHas> branch
```

### 11 — `StateGraph` example + missing install ✅

**Install half:** the `definitions-types`, `catalog-creation` and
`renderers-tsx` snippets all import `@copilotkit/a2ui-renderer`, and no
page installed it. Added a step for it plus `zod` (both are explicit
deps of the langgraph-python cell). `@copilotkit/a2ui-renderer` is a
real published package at 1.68.1, not private.

**`StateGraph` half:** added, with **verified** code — not a guess.

A re-verification pass sharpened this defect. `create_agent` appears on
that page **only as an import** — both snippet regions
(`backend-schema-json-load`, `backend-render-operations`) stop inside
the tool body, so the agent construction is never shown at all:

```
$ grep -n "create_agent" <rendered .md>
315:from langchain.agents import create_agent      # import only
347:from langchain.agents import create_agent      # import only
```

So the page leaves `create_agent`, `CopilotKitMiddleware` and
`ChatOpenAI` as imports the reader cannot act on. My first draft wrongly
said "the snippet above is the reference cell's `create_agent` form";
that is corrected in the second commit, and the step now also carries
over the cell's system-prompt caveat (the prompt tells the model to call
`display_flight` once and stop, because the tool result *is* the card).

Two probes, offline:

1. `ChatOpenAI(model="gpt-4.1-mini").bind_tools([display_flight])`
constructs (no network).
2. A `StateGraph` + `ToolNode` + `tools_condition` graph, bare
`compile()`, driven by a fake chat model, puts the A2UI container in the
`ToolMessage`:

```
operation kinds: ['createSurface', 'updateComponents', 'updateDataModel']
PROBE2 OK (bare compile, ToolNode, tools_condition, bind_tools)
```

Placement needed care, because the root page is shared by 21 frameworks
and `WhenFrameworkHas` gates only on manifest flags — `a2ui_pattern:
schema-loading` covers 14 non-LangGraph integrations, so putting it
there leaked LangGraph code to LlamaIndex/ADK/Pydantic-AI **and Python
code to langgraph-typescript**. Confirmed by rendering before gating:

```
llamaindex             stategraph=1     # wrong
langgraph-typescript   stategraph=1     # wrong: Python on a TS framework
```

So I added a narrow docs flag, `a2ui_agent_form: langgraph-state-graph`,
via the extension path `when-framework-has.tsx` documents (manifest
schema → `Integration` → `SupportedFlag` → manifest). Set on
`langgraph-python` and `langgraph-fastapi` only. After gating:

```
langgraph-python       stategraph=1
langgraph-fastapi      stategraph=1
langgraph-typescript   stategraph=0
llamaindex / google-adk / pydantic-ai / mastra / ms-agent-dotnet   stategraph=0
```

**Known gap, stated plainly:** `langgraph-typescript` gets no
`StateGraph` form. I did not write one, because I have not verified a
TypeScript LangGraph + A2UI form and a plausible-but-unrun TS snippet is
worse than the gap. Adding it is a follow-up; the flag is the seam for
it.

**One caveat on this snippet:** every other code block on that page is
machine-extracted from a running showcase cell. This one is hand-written
and probe-verified. Backing it with a real cell (a `StateGraph` A2UI
backend + fixture) would be the durable fix and is worth a follow-up.

### 12 — Two unreconciled doc trees ✅

`/integrations/langgraph/generative-ui/a2ui/fixed-schema` was not merely
the wrong prefix — it **301s straight back to the page it sits on**:

```
$ curl -o /dev/null -w "%{http_code} %{redirect_url}" .../integrations/langgraph/generative-ui/a2ui/fixed-schema
301 http://localhost:3013/langgraph-python/generative-ui/a2ui/fixed-schema
```

So the sentence promising "the full pattern" linked to itself, and for a
langgraph-typescript reader it also silently switched framework.
Repointed at the reference it actually promises, relative so it resolves
in the reader's own namespace — matching the `./dynamic-schema`
convention already on this page.

**Body-verified, not status-verified** (the site soft-404s with HTTP
200):

```
langgraph-python       bytes=353527  id="action-handlers"=1  onAction=9  soft404=0
langgraph-typescript   bytes=354215  id="action-handlers"=1  onAction=9  soft404=0
```

Non-LangGraph frameworks get the graceful "topic specific to other
integrations" page listing where it exists — not a dead end.

## Found while double-checking — reported, not changed

Both are on the page I own, both are real, and I left both alone
deliberately.

**The LangSmith tab runs a Python agent with the JavaScript CLI.** The
start command is `npx @langchain/langgraph-cli dev`, but the agent is
`main.py` and `langgraph.json` declares `"python_version": "3.12"`. I
expected this to fail. It does not — the JS CLI detects the Python
config and delegates, but prints:

```
warn: Launching Python server from @langchain/langgraph-cli is experimental.
      Please use the `langgraph-cli` package from PyPi instead.
info: Downloading uv 0.9.11 for darwin...
Installed 76 packages in 53ms
```

It then boots normally and honours `--port`. So this is not a copy-paste
failure — it is an experimental path that LangChain itself advises
against, plus a surprise toolchain download. I did **not** change the
command: it works, the recommended alternative would change the
dependency set (defect 8's territory), and the identical command appears
on three other pages (`integrations/langgraph/deep-agents.mdx`,
`integrations/deepagents/quickstart.mdx`, `deepagents/index.mdx`), so
changing one page in isolation would just create a new inconsistency.
Worth its own ticket across all four.

**The quickstart's runtime helper is deprecated.**
`copilotRuntimeNextJSAppRouterEndpoint` →
`createCopilotEndpointSingleRoute`, which carries `@deprecated Use
createCopilotHonoHandler with mode: "single-route" instead`
(`packages/runtime/src/v2/runtime/endpoints/hono-single.ts:24`). That is
defect 5's territory — the parallel session owns the handler-name
mapping — so per the brief I renamed nothing.

The upside of tracing it: it let me word the defect-3 caution precisely.
`createCopilotEndpointSingleRoute` calls `createCopilotRuntimeHandler({
mode: "single-route" })`, i.e. literally the same mode the
runtime-endpoints page documents, so the caution can say "runs the
runtime in single-route mode" as a fact rather than an inference.

## Not resolved / left for others

- **Defects 1, 2, 5, 9** — parallel session's files. Untouched,
including cross-references. In particular I renamed **no** providers or
handlers.
- **`langgraph-typescript` has no `StateGraph` form** — see defect 11
above.
- **The langgraph-scoped A2UI tree is dead content.**
`integrations/langgraph/generative-ui/a2ui/{index,fixed-schema,dynamic-schema}.mdx`
are shadowed at every URL; `advanced.mdx` and `styling.mdx` are
reachable but absent from the sidebar (`buildFrameworkOverridesNav`
surfaces top-level overrides like `subgraphs` and `configurable`, but
not these nested ones). Needs its own ticket.
- **The bring-your-own path on the shared quickstart is Python-only**
(`uv init`, `uv add`, `main.py`) for all three LangGraph frameworks,
including `langgraph-typescript`. Pre-existing; not widened by this PR,
but it is a real defect on a shared page.
- **`showcase/integrations/langgraph-fastapi`'s a2ui cell has a source
comment** pointing at
`docs/integrations/langgraph/generative-ui/a2ui/fixed-schema.mdx` — i.e.
the shadowed file. Cosmetic, and inside a cell, so left alone.
- **`generative-ui/a2ui/index.mdx` links `./fixed-schema-streaming`**,
which does not exist at root. Noticed while verifying; out of scope.

## Testing

All from `showcase/shell-docs`.

```
$ npx tsc --noEmit
(clean)

$ npm run lint
0 errors  (pre-existing warnings only, none in touched files)

$ npx vitest run
Test Files  1 failed | 58 passed (59)
     Tests  1 failed | 417 passed (418)
```

The single failure is `channels-docs.test.ts > publishes the Channels
overview only through provider navigation`, about channels architecture
images. **Proven pre-existing** — `git stash -u` on this branch and
rerun gives the identical `1 failed | 29 passed`.

Manifest schema change validated:

```
$ cd showcase/scripts && npx vitest run __tests__/generate-registry-pattern.test.ts __tests__/create-integration.test.ts
Test Files  2 passed (2)      Tests  39 passed (39)

$ npm run pretypecheck   # regenerates registry.json from the manifests
langgraph-python  -> langgraph-state-graph
langgraph-fastapi -> langgraph-state-graph
```

Formatted with `oxfmt` (no changes needed). No changeset added.

### Render checks

Dev server on **3013**, not 3003 — 3003 was already held by another
worktree's docs server (`CopilotKit-oss844`), and reusing it would have
verified the wrong tree.

`quickstart` — identical across all three LangGraph frameworks, `.md`
and HTML:

| check | py | ts | fastapi |
|---|---|---|---|
| `npm install @copilotkit/react-core @copilotkit/runtime` | 1 | 1 | 1 |
| `uv add langgraph langchain-openai langchain-core python-dotenv` | 1 |
1 | 1 |
| route caution + `[[...slug]]` | 1 | 1 | 1 |
| "Port 8123 is not the default" / "serves on **2024**" | 1 | 1 | 1 |
| both checkpointer callouts | 1 | 1 | 1 |
| pinned-dependency warning | 1 | 1 | 1 |
| stale "Install LangGraph and AG-UI" heading | 0 | 0 | 0 |

Callouts confirmed rendering as components, not literal text (checked
the emitted React payload). Caught and fixed one real rendering bug
while doing this: Callout `title` is a plain string, so a backticked
title rendered its backticks literally.

Also re-checked the two directional cross-references, since those are
easy to get backwards: the "route below" note sits at line 289 and the
route step at 334 (below ✓), and the "route above" note at 487 (above
✓).

Cross-page link body-verified for all three LangGraph frameworks — the
`.md` view rewrites it into the reader's own namespace
(`/langgraph-python/backend/runtime-endpoints#enable-rich-threads-routes`),
and the target carries the anchor with no soft-404:

```
langgraph-python       bytes=374111  id="enable-rich-threads-routes"=1  soft404=0
langgraph-typescript   bytes=374871  id="enable-rich-threads-routes"=1  soft404=0
langgraph-fastapi      bytes=374301  id="enable-rich-threads-routes"=1  soft404=0
```

The `#registering-the-runtime` anchor referenced from the new A2UI step
was likewise confirmed present on that page.

`generative-ui/a2ui/fixed-schema` — `.md` across eight frameworks:

| framework | install step | StateGraph | old cross-tree link | new link
| raw JSX tags |
|---|---|---|---|---|---|
| langgraph-python | 1 | 1 | 0 | 1 | 0 |
| langgraph-fastapi | 1 | 1 | 0 | 1 | 0 |
| langgraph-typescript | 1 | 0 | 0 | 1 | 0 |
| llamaindex | 1 | 0 | 0 | 1 | 0 |
| google-adk | 1 | 0 | 0 | 1 | 0 |
| pydantic-ai | 1 | 0 | 0 | 1 | 0 |
| mastra | 1 | 0 | 0 | 1 | 0 |
| ms-agent-dotnet | 1 | 0 | 0 | 1 | 0 |

Branch selection now matches HTML per framework (`schema-loading` for
langgraph, `llm-driven` for mastra, `schema-inline` for ms-agent-dotnet)
with zero `WhenFrameworkHas` tags leaking into Markdown.

`doctest.json` was **not** treated as a gate — there is no runner for it
in the repo, and its dep set is unchanged anyway.
2026-08-19 12:58:12 -05:00
Benjamin Taylor f24c4e2f22 docs(langgraph): correct the A2UI StateGraph framing after re-verification (refs OSS-857)
Three fixes found on a second pass over the docs changes:

- The new StateGraph step claimed "the snippet above is the reference
  cell's `create_agent` form". It is not: `create_agent` appears on that
  page only as an *import* — both snippet regions stop inside the tool
  body, so the agent construction is never shown at all. Reworded to say
  that, which is the sharper version of defect 11: the page never shows
  how the tool attaches to any agent, leaving `create_agent`,
  `CopilotKitMiddleware` and `ChatOpenAI` as imports the reader cannot act
  on.
- Carry over the system-prompt caveat. The reference cell steers the model
  to call `display_flight` once and stop because the tool result *is* the
  card; a StateGraph reader who drops that gets repeat tool calls.
- The install note named only two of the four packages the FastAPI tab
  adds on top of the shared line. List all four.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 12:39:11 -05:00