## Release monorepo v1.69.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.69.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.69.3`
- Creates git tag `monorepo/v1.69.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.
## What does this PR do?
- Replaces the retired GitHub Models setup in the Microsoft Agent
Framework .NET starter with direct OpenAI.
- Updates the live .NET guides to the current Agent Framework AG-UI
hosting, session, state, and response APIs.
- Pins quickstart packages to the versions tested by the starter.
- Adds a recursive retired-guidance guard and runs it in docs CI for
docs or starter-only changes.
GitHub Models was fully retired on July 30, 2026, so the published setup
can no longer work.
## Related PRs and Issues
- Companion CLI cleanup:
https://github.com/CopilotKit/Intelligence/pull/1029
## Validation
- `npm exec -- vitest run
src/lib/__tests__/ms-agent-dotnet-provider.test.ts` (5 tests passed)
- `npm run typecheck`
- `npm run lint` (existing warnings only)
- `npm run build`
- `pnpm run validate:model-names`
- Workflow syntax and formatting checks passed.
- `docker build -f docker/Dockerfile.agent agent`
- `docker compose -f docker-compose.test.yml config`
- Compiled all nine full .NET guide examples against the pinned Agent
Framework and AG-UI packages.
- TestServer proof returned `200 text/event-stream` and emitted the
expected `STATE_SNAPSHOT` through `WithMetadata(streamOptions)`.
- Full shell-docs test run: 469 tests passed; one unrelated existing
Mastra fixture test failed in `llm-text.test.ts`.
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
## Summary
- pin all active LangGraph Python and LangGraph FastAPI starter runtimes
to the published `copilotkit==0.1.96` lifecycle fix and compatible
`ag-ui-langgraph==0.0.43` / `ag-ui-protocol==0.1.19` versions
- add bounded Excalidraw guidance to the LangGraph Python starter: one
`create_view` call, unique IDs, labeled shapes, arrows, one final camera
update, and a one-sentence response
- retain lifecycle regression coverage in the Python SDK, where the real
intercepted-tool implementation is owned and tested; starter smoke
builds verify both consumer environments resolve the released fix
## Release sequence
This is the second FAC-124 PR. #6728 merged first, and
`copilotkit==0.1.96` is verified live on PyPI with the lifecycle
materialization and duplicate-suppression code. This starter PR should
merge next. After it merges, the final PR will update the Intelligence
catalog/provenance to this merge commit.
Linear:
https://linear.app/copilotkit/issue/FAC-124/langgraph-py-cli-starter-excalidraw-mcp-pill-is-nondeterministic
## Validation
- `uvx --from uv==0.8.24 uv lock --check` — LangGraph Python and
LangGraph FastAPI locks
- `docker build -f docker/Dockerfile.agent -t
fac-124-langgraph-python-final agent` — passed with `copilotkit==0.1.96`
- `docker build -f docker/Dockerfile.agent -t
fac-124-langgraph-fastapi-final agent` — passed with
`copilotkit==0.1.96`
- `pnpm parity:check` — passed; LangGraph FastAPI 84 ok, 0 errors
- `git diff --check`
The Railway production image previously installed the corrected Python
pins and compiled the Next.js frontend; local export stopped only when
the Docker host ran out of disk while copying the standalone bundle.
## What does this PR do?
The React Native guide imports `CopilotKitProvider`, `useAgent` and
`useCopilotKit` from `@copilotkit/react-native/headless` and names no
version. That subpath first ships in **1.64.0** (#6142), so a project
pinned to 1.63.x or earlier fails every one of those imports with:
```
Unable to resolve module @copilotkit/react-native/headless
```
On Metro that reads like a broken install rather than a version skew,
and it sends the reader into the package-exports and polyfill debugging
the same guide warns about a few sections later. The actual fix is one
word in an import path.
This states the boundary in the three places a reader meets the subpath.
Prose only — no snippet in this page changed.
### The version boundary, verified against the registry
Not inferred from a changelog — `npm view @copilotkit/react-native@<v>
exports` on each:
| version | `./headless` |
| -- | -- |
| 1.62.0, 1.62.2, 1.62.3 | absent |
| 1.63.0, 1.63.1, 1.63.2 | absent |
| **1.64.0** | **present** |
| 1.64.1+, 1.65.0, 1.69.2 | present |
`./components` and the polyfill subpaths exist across all of the above,
so `/headless` is the only path in the guide that carries a version
boundary. Introduced by 0a582df4dd / #6142.
### What changed
- **Intro line** — `/headless` is marked `1.64.0+` where the three
import surfaces are first introduced.
- **Install step** — a `type="warn"` callout, placed where the resolved
version is actually decided. States the boundary, quotes the exact Metro
error, gives `npm ls @copilotkit/react-native` to check what you
resolved, and covers the fallback.
- **Import surfaces table** — a new "Available since" column.
- **Import surfaces prose** — records what `/headless` *is*, which the
guide never said: a lean alternative entry added so custom-UI consumers
skip the chat and attachment native deps — **not** a replacement for the
root barrel, which remains the package's default full surface and
re-exports everything in `/headless`.
### The fallback advice is deliberately not just "import from the root"
On 1.62.2 and 1.63.2 the root barrel does export all three names
(checked in the shipped tarballs, not assumed). But
`package/dist/index.mjs` on those versions statically imports
`expo-document-picker` and `expo-file-system`. A reader who switches to
the root therefore inherits exactly the peer-dependency resolution
failure that this guide's `/headless` choice exists to avoid:
```
Unable to resolve module expo-document-picker
```
So the callout says to prefer upgrading to 1.64.0+, and if you cannot
(because you are matching a pinned `@copilotkit/runtime`), names the
install-or-stub requirement that comes with the root import rather than
presenting it as a free swap.
### Why not backport `./headless` to 1.62.x
That was the alternative the issue floated, and it looks unnecessary.
`/headless` was never intended as the canonical entry that supersedes
the root — #6142's message, `src/index.ts` (`export * from
"./headless"`, with a root quick-start that uses the root), and
`src/headless.ts` ("existing imports from `@copilotkit/react-native` are
unchanged") all agree it is a lean *alternative*. Adding an entry point
in a patch of an older line would also change what "the pinned set"
means for anyone matching client to runtime.
## Related PRs and Issues
- Closes OSS-956
- #6142 — added the `/headless` subpath in 1.64.0
- #5883 — the `@copilotkit/react-core/v2/headless` entry that #6142
mirrors
## Verification
- MDX compiles via `@mdx-js/mdx` + `remark-gfm`, edited and baseline
both
- 4-column Import surfaces table parses; 19/19 `<Callout>` tags balanced
- `commitlint` exit 0
- `pnpm check:intelligence-env-names` passes
- `.mdx` is not in lefthook's `lint-fix` glob, so oxfmt/oxlint never
applied to this file
- This page carries no `doctest=` fences, and no fence in it changed, so
the guide's snippets stay runnable as they were
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Enable the browser Inspector by default for React v2, Vue, and Angular
development builds.
- Add `enableInspector?: boolean` as the shared opt-out API; `false`
disables it.
- Keep production and SSR hard-disabled, even when `enableInspector` is
`true`.
- Keep React Native unchanged because the Lit Inspector requires a DOM.
## Why
The Inspector already consumes the same CopilotKit core used by each
browser framework, but Vue and Angular required manual setup and React's
behavior was tied to the legacy `showDevConsole` prop. Developers should
get the same debugging entry point in every browser framework without
exposing it in production.
## How
- Centralize visibility in `@copilotkit/shared`: browser + development +
not explicitly disabled.
- Mount the custom element after hydration/rendering and bind the exact
framework core before connection.
- Reuse Angular's existing Inspector/Event Snippets service and track
element ownership for cleanup.
- Keep Vue's wrapper reactive and client-only.
- Remove the dead Inspector anchor configuration and obsolete example
workarounds.
- Leave `showDevConsole` available for legacy error UI, but it no longer
controls the v2 Inspector.
## Verification
Passing:
- Nx tests: React Core (1,534), Vue, Angular (316), Shared, and Web
Inspector.
- Nx typechecks: React Core, Vue, Shared, and Web Inspector.
- Nx builds: React Core, Vue, Shared, and Web Inspector.
- Shell docs: typecheck and production build.
- Pre-commit lint: no errors.
Unrelated current `main` baseline failures:
- Angular typecheck/build: `ChangeDetectionStrategy.Eager` is not
present in the installed Angular version; the failing files are
unchanged by this PR.
- Shell docs tests: three Git LFS image fixtures are pointer files in
this worktree, plus one unrelated Mastra fixture expectation.
## Summary
- publish the current Python SDK lifecycle repair as
`copilotkit==0.1.96`
- unblock the LangGraph Python starter from consuming the fixed
intercepted frontend/MCP tool-call bridge
- keep the starter pin unchanged until PyPI serves this exact version
## Why this is separate
FAC-124 requires an ordered delivery sequence. The current lifecycle fix
and its streaming/non-streaming regression tests are already on `main`,
but PyPI still serves `0.1.95` and the starter is pinned to `0.1.94`.
Merging this release gate publishes `0.1.96`; the follow-up PR can then
safely update the starter lock, add the bounded Excalidraw labeling
contract, and add starter-level deterministic coverage.
Refs FAC-124.
## Validation
- `./scripts/release/detect-py-version-changes.sh` →
`should_publish=true` for `0.1.96`
- `poetry check --lock`
- `poetry build` produced the `0.1.96` wheel and sdist
- `poetry run python -m pytest -q
tests/test_intercepted_tool_call_events.py` → 8 passed
- `git diff --check`
## Summary
- configure the Agno showcase agent with writable SQLite session storage
so paused frontend-tool and HITL runs can resume
- document the database requirement on all four affected Agno routes
without changing shared framework-neutral content
- add runtime-agent, rendered visual docs, LLM-text, dependency, and
framework-isolation regression coverage
## Validation
- Agno Python tests: 33 passed
- Agno persistence regression: passed
- shell-docs persistence tests: 5 passed
- shell-docs typecheck and production build: passed
- Ruff and git diff checks: passed
- Agno 2.7.2 characterization: the configured database passes the
missing-database guard and reaches session lookup
## Probe note
The direct D6 probe was blocked before Agno started by the existing
showcase shared-module staging error at showcase/integrations/ag2/tools.
No Agno service or changed code was reached.
Fixes
[FAC-125](https://linear.app/copilotkit/issue/FAC-125/agno-frontend-tools-fail-without-database-configured).
## Bug
[Sam
reported](https://copilotkit.slack.com/archives/C0BR0V2P4QJ/p1787777419496249)
two live Docs CTA failures:
- The navbar signup CTA returned users to Docs after authentication.
- Other Intelligence CTAs could send signed-in users to Pricing.
## Fix
- Send every Docs Intelligence signup CTA to the matching environment at
`/sign-in?post_auth_redirect=ready`.
- Remove the Docs `redirect_url` that explicitly caused the return to
Docs.
- Share the URL builder across the navbar and in-page CTAs so their
destinations stay aligned.
- Preserve existing UTM attribution and custom Talk to an Engineer
destinations.
## Validation
- 18 focused tests passed.
- Shell Docs TypeScript check passed.
- Focused lint and formatting checks passed.
No visual changes.
The React Native guide imports `CopilotKitProvider`, `useAgent` and
`useCopilotKit` from `@copilotkit/react-native/headless` and names no version.
That subpath first ships in 1.64.0 (#6142), so a project pinned to 1.63.x or
earlier fails every one of those imports with `Unable to resolve module
@copilotkit/react-native/headless` — which on Metro reads like a broken install
rather than a version skew, and sends the reader into the package-exports and
polyfill debugging the same guide warns about. The actual fix is one word in an
import path.
Verified against the registry rather than inferred: `./headless` is absent from
1.62.0, 1.62.2, 1.62.3, 1.63.0, 1.63.1 and 1.63.2, and present from 1.64.0
onward. `./components` and the polyfill subpaths exist across all of them, so
`/headless` is the only path in the guide with a version boundary.
State the boundary in the three places a reader meets the subpath: the intro
line that introduces the surfaces, a callout in the install step (where the
resolved version is actually decided, with `npm ls` to check it), and an
"Available since" column in the Import surfaces table.
The fallback advice is deliberately not just "import from the root". On 1.62.2
and 1.63.2 the root barrel does export all three names — but its `dist/index.mjs`
statically imports `expo-document-picker` and `expo-file-system`, so a reader who
switches to the root inherits exactly the peer-dependency resolution failure that
this guide's `/headless` choice exists to avoid. The note says so and points at
the stub/install requirement.
Also records what `/headless` is, since the guide never said: a lean alternative
entry added so custom-UI consumers skip the chat and attachment native deps, not
a replacement for the root barrel, which remains the package's default full
surface and re-exports everything in `/headless`.
Prose only — no snippet in this page changed, and the page carries no doctest
fences, so the guide's snippets stay runnable as they were.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary
- document the Google ADK `stop_on_terminal_text` registration required
for state streaming
- keep internal callback commentary out of generated public docs and
link the canonical implementation instead
- cover both visual and LLM documentation output, including regression
checks for leaked internals
## Validation
- `npx vitest run src/lib/__tests__/setup-content.test.ts
src/lib/__tests__/setup-concept.test.ts`
- `npx vitest run src/lib/__tests__/llm-text.test.ts -t "renders the
Google ADK termination setup"`
- `npx oxfmt --check src/lib/__tests__/setup-content.test.ts
src/lib/__tests__/setup-concept.test.ts
src/lib/__tests__/llm-text.test.ts`
- `npx oxlint src/lib/__tests__/setup-content.test.ts
src/lib/__tests__/setup-concept.test.ts
src/lib/__tests__/llm-text.test.ts`
- `npm run typecheck`
- `git diff --check`
## Why
An Intelligence integration lost a request-to-row correlation map
partway through a user interaction — no error, no warning. It surfaced
as "our response routing is flaky". OSS-979 filed it as
`CopilotKitProvider` remounting its children.
The provider does nothing of the kind. It renders `{children}`
unconditionally at `CopilotKitProvider.tsx:952` — unkeyed, no early
return, and there is no `Suspense` boundary anywhere in `v2`. Nothing in
the SDK silently re-points the active thread either; every mutation path
(`setActiveThreadId`, `startNewThread`, the drawer row click, the
inspector override) is caller-driven.
The remount was app-side, and it was app-side because this skill told it
to be:
- `references/threads.md:98` teaches `useThreads()` → select →
`<CopilotChat key={activeId}>`, and that recipe is only reachable once
Intelligence is wired.
- `references/switching-agents.md:123` teaches "`key={activeAgent}`
forces remount so thread state doesn't leak" without saying what else
that discards.
- `examples/showcases/reskinnable-demo/src/app/[skin]/layout.tsx:223`
models `<SubagentActivityProvider key={threadId}>` above `{children}`,
commented "Remounting is deliberate".
Follow all three and you key a layout-level provider on a thread id that
changes asynchronously after mount. Everything below it dies
mid-interaction.
Two properties made it invisible:
- Durable threads exist only in Intelligence mode, so with a plain SSE
runtime `useThreads` returns nothing, the selected thread never changes,
and the remount never fires. It appears the moment Intelligence is
wired.
- Whether state survives depends on whether the user acted before the
thread list resolved.
## What changed
Docs only — no library change. Both traps now carry their blast radius,
in the four places an agent actually reads:
| File | Change |
|---|---|
| `SKILL.md` | Two invariants in the load-once section, so they land
before any reference is opened |
| `references/threads.md` | New HIGH entry on keying above app state;
note that `activeId` in the switcher recipe settles asynchronously |
| `references/switching-agents.md` | Existing HIGH entry now states the
blast radius and cross-links the threads trap |
| `references/switching-agents-recipes.md` | Key rule amended — keep it
on `<CopilotChat>`, nowhere higher |
| `references/agent-access.md` | The second route to the same symptom:
`useAgent` swaps a provisional stand-in for the real agent when `/info`
resolves, so an effect keyed on `agent` re-runs once, mid-interaction.
Adds an `isReady` pattern and a HIGH entry |
`isReady` appeared in **zero** shipped skills before this — it was
documented only in `showcase/shell-docs/.../useAgent.mdx` and in JSDoc.
Same shape as OSS-888, where the root cause was the shipped skill rather
than the library.
Also corrects a factual error: the skill claimed `useAgent` returns `{
agent }` only. It returns `{ agent, isReady }`.
The 10-file diff is 5 source files under `packages/react-core/skills/`
plus their 5 mirrors under `skills/`, regenerated with `pnpm
sync:plugin-skills`.
## Verification
- `pnpm check:plugin-skills` — mirror in sync
- `pnpm exec vitest run scripts/__tests__/sync-plugin-skills.test.ts` —
12 passed
- `oxfmt --check` — clean over both skill trees
- Full pre-commit suite green, including `test-and-check-packages`
(`test`, `publint`, `attw` across 2 projects and 20 dependent tasks)
## Not in scope
Whether the run's app keyed on `threadId` or on `agent` is not
settleable from the repo — its source is not in any checkout, and there
is no `2026-08-25` strands run report under
`tools/one-prompt-development/evaluation/runs` on any branch. Both
variants produce the reported symptom and this covers both, so a
first-hand repro is a separate task. The `reskinnable-demo` layout is
left as-is deliberately: it is a legitimate use of the pattern, and it
is now the worked example the guidance warns about.
Scoping detail in the OSS-979 comment.
refs OSS-979
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## What
Converts `examples/integrations/agentcore` from unlocked
`requirements.txt` files to uv projects (`pyproject.toml` + `uv.lock`),
matching every other Python integration example in this repo.
## Why
The example already used uv as an *installer* — the agent images are
built from `ghcr.io/astral-sh/uv` and ran `uv pip install -r
requirements.txt` — but nothing was locked, so each image build
re-resolved transitive dependencies against whatever PyPI had that day.
That had already drifted into a hard break. `langgraph==1.0.10rc1`
resolved alongside a `langgraph-prebuilt` that reads `ExecutionInfo` off
`langgraph.runtime`, which 1.0.x does not export, so the LangGraph agent
raised `ImportError` at container start.
## The migration
- Both agents get a `pyproject.toml` + `uv.lock`; the Dockerfiles
install with `uv sync --locked` and run out of `/app/.venv`.
- LangGraph agent moves to `langgraph==1.1.6` / `langchain==1.2.15`, the
pair used by `examples/integrations/langgraph-python`, which resolves
the import failure. `langchain` was previously `>=0.3.0` while the code
uses the 1.x `create_agent` API.
- Four packages that shipped code imports directly were declared nowhere
and survived only as transitives: `boto3` (both agents, via
`agents/utils/ssm.py`), `PyJWT` (langgraph, via `agents/utils/auth.py`),
`langchain-core` (langgraph, via `tools/todos.py`), `botocore` (root,
via `scripts/utils.py`). All now declared; no resolved version changed.
- `aws-opentelemetry-distro` moves from a loose second `uv pip install`
into the locked set.
- The example root gets a project for the `scripts/` helpers. Their
`requirements.txt` listed the dependencies but nothing installed it, so
`uv run scripts/test-agent.py` — the command that script's own docstring
gives — failed on a missing `requests`.
- Deploy and local-dev scripts call `uv run` instead of bare `python3`;
preflight checks for `uv` rather than `python3`.
## Terraform
The docker-mode image hash read `patterns/<pattern>/requirements.txt`,
`patterns/utils`, a root-level `gateway/` and `tools/`, and a root
`pyproject.toml` — none of which exist here. `filesha256` on a missing
file is a plan-time error. Repointed at `agents/<pattern>` and
`agents/utils`, now hashing `pyproject.toml` and `uv.lock`.
The hash then had to exclude the virtualenv the migration creates:
`fileset(pattern_dir, "**/*.py")` saw 3114 files instead of 5, and the
computed hash differed depending on whether a developer had ever run the
agent locally — feeding `replace_triggered_by` and forcing a runtime
replacement. Measured: `5f9a98ef…` with a venv present vs `bacab1e1…`
without, on identical committed sources. The fix filters `.venv/` and
`venv/` and produces the clean-checkout digest in both cases.
## Review
Five review rounds plus a promotion audit, and nine defects this
migration introduced were found and fixed before merge. Each fix was
verified by running the thing, not by reading it:
- The image hash sweeping the local virtualenv (above).
- The undeclared direct dependencies (above).
- `scripts/test-agent.py` piped the child's output and never drained it,
deadlocking the agent, while the startup-timeout branch blocked forever
on a read — the only channel carrying uv's lockfile-drift error.
- `--local` treated any listener on port 8080 as the agent, so a foreign
process produced "Agent started successfully" while the real child died.
Adopting a running process is now explicit opt-in.
- The documented invocation contract had drifted across the two READMEs
and both scripts' usage text; a single reconciliation pass now owns all
of them, and every documented command was executed to confirm it works.
- The Terraform README named the CloudFormation deploy script, which
cannot read a Terraform deployment.
- Two `.env.example` entries parsed as their own trailing comment text
under Docker Compose.
- The build context shipped 898 MB of local-only artifacts (measured);
now 11 kB.
- Assorted comment and message inaccuracies, including a container env
block whose comment attributed a runtime-critical variable to uv.
## Repository hygiene
Two fixes the pre-merge gates surfaced rather than the review rounds:
- `oxfmt` on the example README and `tofu fmt` on the Terraform locals
file. The README break was introduced by this branch; the Terraform one
predates it but sits in a file this branch edits.
- The Terraform ignore rules were anchored to the top level, so the
provider cache `terraform init` writes beside every *nested* module was
fully stageable — a measured 834 MB one `git add -A` from being
committed. Switched to unanchored patterns, matching what this branch
already did for the virtualenv layout. Verified with `git check-ignore`
that the nested cache and lock file are now covered, tfstate and tfvars
still are, and no tracked file is caught by the wider patterns.
## Not addressed
Roughly 90 further findings are real but pre-existing and belong to four
follow-up subjects, listed in full in the review ledger:
- **Terraform module repair** — `terraform validate` fails on five
undeclared resources, so this module cannot plan at all. Zip mode
references an entry point and a packager directory that do not exist.
- **test-agent AG-UI correctness** — the request body fails
`RunAgentInput.model_validate` on both agents, and the response decoder
parses a pre-AG-UI format, so failures print nothing and exit 0.
- **README accuracy** — a `docs/` directory that does not exist, a wrong
Node floor, a teardown block whose second command never runs.
- **Deploy-script hardening** —
`infra-terraform/scripts/deploy-frontend.py` requires a Terraform output
nothing declares, so it exits 1 every run; the README now says so rather
than presenting it as a working path.
## Verification
Both images built for `linux/arm64` and the agent module imported inside
each, after every fix cycle. `uv lock --check` clean on all three
projects. Every documented command executed from its documented
directory. The virtualenv hash filter, the build-context reduction, the
pipe deadlock, the port-adoption fix and the env-file parsing were each
verified by measurement with before/after output. No repo CI job builds
or lints this example, so these local runs are the only coverage that
exists.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
**Merge order: #6711 → #6718 → this PR.**
#6718 rewrites `identifyUser` in the same 22 blocks and is a sibling of
this branch, not stacked on it, so the two overlap on the same lines.
Landing the gate last means it ratchets on the finished shape and avoids
a conflict. If this PR goes first instead, #6718 goes red until it moves
all 22 sites together — which is the gate working, but noisier.
Stacked on #6711 — merge that first. This branch descends from it, so
the diff below carries its commit too; GitHub drops those once #6711
lands.
Basing this PR on `main` rather than on #6711's branch is deliberate: 17
of 35 workflows filter `pull_request: branches: [main]`, including the
one this PR extends, so a PR based on the 981 branch would not run the
check it adds.
## Problem
The marked block that wires managed Intelligence is the region a hosted
reader copies verbatim, and nothing checked it. Both gaps are
deliberate, not accidental:
- `examples/integrations/_parity/manifest.json` lists
`src/app/api/copilotkit/**` under `allowedDivergence` for every instance
it tracks. What parity does hold byte-identical is the demo frontend: 54
verbatim paths of example canvas, todo columns and charts.
- No `examples/integrations/*/docker-compose.test.yml` sets
`COPILOTKIT_LICENSE_TOKEN`. The wiring is a ternary on that variable, so
all 13 smoke-tested starters take the else arm. The `intelligence:` arm
has never executed in CI, in any starter.
The cost was already visible. The block's code was byte-identical in 21
of 22 starters, but its warning comment had drifted into five variants
and the two `ms-agent-framework-*` starters shipped the `demo-user` stub
with no warning at all. Comment drift is harmless by itself; it is the
tracer showing nothing held the region still, and it is how the
localhost default of #6711 survived in all 22 copies at once.
## Change
`scripts/validate-intelligence-wiring-block.ts` greps the opening
marker, compares every site against the north-star starter, and fails on
the first line that differs. Two normalisations keep it usable:
- The block is dedented, because `agentcore` nests it two levels deeper
— its runtime is a Lambda handler, not a Next.js route.
- The else arm's runner name is masked, because `agentcore` runs
`AgentCoreRunner` in front of a Bedrock AgentCore session where an
in-process runner has nothing to run. `EXPECTED_RUNNER` holds that one
exception, so a runner swapped in by accident still fails.
Everything else, comment text included, must match to the byte. Then the
warning is unified at all 22 sites on the fullest existing wording,
which also says the id must exist in Intelligence or thread operations
can fail.
It compares against the north star rather than a literal kept in the
script, so improving the block means editing `langgraph-python` and
running the other 21 to match.
## What it does and does not guarantee
It is a shape gate, not a content gate: 22 identically wrong copies
still pass. What it guarantees is that a fix reaches all of them or
none. The check passes on day one — 21 of 22 already matched on code —
so it is a ratchet, not a migration.
## Verification
Mutating a real starter three ways, each caught:
| Mutation | Reported as |
| --- | --- |
| Dropped one comment line | `line 15 differs from the north star`, exit
1 |
| \`InMemoryAgentRunner\` → \`SomeOtherRunner\` | `else arm uses
SomeOtherRunner; expected InMemoryAgentRunner` |
| Reintroduced \`?? \"http://localhost:4201\"\` | `line 6 differs`, both
sides shown |
The third matters: the #6711 regression is now caught at a second site,
independent of the env-name validator.
Commands run, all exit 0:
- `pnpm exec vitest run
scripts/__tests__/validate-intelligence-wiring-block.test.ts
scripts/__tests__/validate-intelligence-env-names.test.ts` — 30 passed
- `pnpm check:intelligence-wiring-block` — `All 22 Intelligence wiring
sites match langgraph-python.`
- `pnpm check:intelligence-env-names` — unaffected, still canonical
- `pnpm parity:verify`
- `oxfmt --check`, `oxlint`, and `tsc --noEmit --strict` on the new pair
Two tests guard the gate against going vacuous: one asserts at least 22
marker files are discovered, so an empty violation list cannot pass on
an empty file list.
Not run locally: the lefthook pre-commit suite, which fails
environmentally in a worktree without per-package installs (`sh: vite:
command not found`). This diff touches no package source.
## Not covered
Enrolling the `intelligence:` arm in the smoke path. It needs a license
token in CI secrets and an endpoint reachable from the compose network —
a different size of job, tracked separately.
## What does this PR do?
`CopilotKitIntelligence` is built to be correct when the caller says
nothing: omitting `apiUrl`/`wsUrl` resolves to
`https://api.intelligence.copilotkit.ai` and
`wss://realtime.intelligence.copilotkit.ai`, and its docstring says so
outright — *"leaving both unset is always correct against it."*
Every starter's runtime route defeated that default:
```ts
apiUrl: process.env.INTELLIGENCE_API_URL ?? "http://localhost:4201",
wsUrl:
process.env.INTELLIGENCE_GATEWAY_WS_URL ?? "ws://localhost:4401",
```
With the variables unset — the correct configuration for a managed user
— the `??` supplies localhost and the runtime aims at a local stack that
is not running. This is the artifact `copilotkit init` clones, so it is
the first thing a new managed user runs.
The starter's own `.env.example` already warns about exactly this
failure, two files away:
> `INTELLIGENCE_API_URL` and `INTELLIGENCE_GATEWAY_WS_URL` point at a
self-hosted or local Intelligence deployment only — leave them unset
when using managed Intelligence, or the channel host and runtime will
try to reach a local stack that usually is not running.
So the documentation was right and the code contradicted it.
`channel-host.mts`, in the same directories, already had the correct
shape.
### The fix
**22 runtime wiring sites** (20 route handlers, `adk-angular/server.ts`,
and the AgentCore Lambda) now use the conditional spread these starters
already use in `channel-host.mts`, so a self-hosted override still works
and the managed default applies when absent:
```ts
...(process.env.INTELLIGENCE_API_URL
? { apiUrl: process.env.INTELLIGENCE_API_URL }
: {}),
```
No hosted URL is written into the examples — the library already owns
them, so this is a deletion.
**3 `.env.example` files** (`agent-spec`, `llamaindex`, `mcp-apps`) set
the same values *uncommented*. Two do it directly beneath a comment
telling the reader to leave them unset, and an `.env.example` is copied
to `.env`, so these were the remaining route to a localhost value once
the code default was gone. Commented out to match the other nineteen
starters; `agent-spec` had no explanation at all and gets the standard
one.
**A guard**, added to the existing
`scripts/validate-intelligence-env-names.ts` rather than a new script —
it already polices the canonical Intelligence key name and the two dead
hosts, and its workflow is deliberately unfiltered so it sees every
README, example and skill. Two rules: `managedUrlFallback` (a `??`/`||`
default on either variable) and `managedUrlEnvFileAssignment` (an
uncommented env-example assignment). The rule is the *pattern*, not the
literal, so a staging host substituted for localhost fails the same way.
Five files legitimately want a local target and are allowlisted with
their reasons: the `playwright.config.ts` and `.env.example` of the
banking and reskinnable-demo showcases (own vendored compose ports
7050/7053 and 7250/7253, own seeded org keys) and
`agentcore/docker/.env.example` (the documented local development
stack). Resolving those to the managed hosts would aim an offline test
suite at production.
`scripts/__tests__` has no general runner, so the workflow runs this
test file explicitly, following the `plugin-skills-check.yml` precedent
— otherwise a rule that silently stopped matching would leave the check
passing on an empty result.
## Related PRs and Issues
- Closes OSS-981.
- Supersedes the canceled ENT-922, whose blocker ("do not invent hosted
URLs; rewrite once the managed env contract is final") no longer
applies: the contract shipped as `MANAGED_INTELLIGENCE_API_URL` /
`MANAGED_INTELLIGENCE_WS_URL`, and the fix removes a fallback rather
than adding a URL.
- ENT-949 shipped a warning for this class of mistake, but
`warnOnPartialHostOverride` only fires on a *partial* override — both
values defaulting to localhost together is not partial, so nothing
warned.
## Verification
- `pnpm exec vitest run
scripts/__tests__/validate-intelligence-env-names.test.ts` — 13 passed.
Written first: the rules were red before they existed, then reported
**48 violations across 24 files** for the code rule and **10 across 5**
for the env-file rule; the fixes took both to green.
- `pnpm check:intelligence-env-names` — exit 0.
- `oxfmt --check` and `oxlint` over all 25 touched files — clean.
- The spread typechecks under `strict` + `exactOptionalPropertyTypes`,
the setting that would reject `apiUrl: string | undefined`.
- The marked wiring block stays byte-identical across 21 of 22 starters
(`agentcore` differs only in its runner), and no `localhost` remains
inside any marked block.
- Not run locally: the 13 starter Next builds. `test_smoke-starter.yml`
typechecks the route handlers in CI on this PR.
### Out of scope
`agentcore/docker/docker-compose.yml` keeps its
`${INTELLIGENCE_API_URL:-http://localhost:4201}`: it is compose
substitution in the documented local-dev stack, not shipped runtime
code. Separately that default cannot work anyway — inside the bridge
container `localhost` is the container's own loopback — but that is a
different bug.
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## What
Sunsets `examples/showcases/banking`. It is superseded by
`examples/showcases/reskinnable-demo`, which ships the same banking
experience as one of its runtime-swappable skins (alongside airline) on
top of a shared shell. Keeping both means maintaining two copies of the
same demo.
160 files deleted, plus the five places that pointed at the app:
| File | Change |
| --- | --- |
| `pnpm-workspace.yaml` | Drops the workspace entry. Also fixes the
adjacent NOTE, which attributed the canary AG-UI pin to "banking's
agent" when it is reskinnable-demo's own Python deep agent that needs
it. |
| `pnpm-lock.yaml` | Regenerated (−919/+12). The 12 additions are
peer-suffix re-keying caused by removing the importer — banking pinned a
different `eslint`/`vitest` peer combination. No dependency version
changes. |
| `examples/README.md` | The banking row becomes a reskinnable-demo row,
so the successor is listed and the showcase count is unchanged. |
| `showcase/shell-docs/src/content/docs/faq.mdx` | The "Banking
Assistant" link retargets to reskinnable-demo instead of 404ing. |
| `.github/config-allowlist.txt` | Drops the deleted `next.config.mjs`.
|
Note that banking was a real pnpm workspace member using `workspace:*`
deps, unlike reskinnable-demo, which sits deliberately outside the
workspace with its own lockfile. That is why the root lockfile has to be
regenerated here.
## Deliberately not changed
- `scripts/migrate-demos.sh` and `scripts/archive-demo-repos.sh` still
name `examples/showcases/banking`. Those are the already-executed
one-shot manifests for the repo consolidation; the path is a historical
record in them, not a live reference.
- `reskinnable-demo`'s `.env.example` and `docker-compose.yml` still
explain their +200 port offset in terms of banking's stack. The offset
stays real, and "was cloned from banking" stays true.
- The `banking` mentions in `test_reskinnable-demo.yml` refer to
reskinnable-demo's **banking skin**, not this app.
## Verification
- `.github/scripts/check-config-allowlist.sh` passes.
- A full `pnpm install` agrees with the regenerated lockfile (no further
diff).
- The lockfile-only regen and the full install produce identical output.
The `test-and-check-packages` pre-commit hook fires on any
`pnpm-lock.yaml` change, so it ran `test,publint,attw` across all 25
packages. Four suites failed locally — `sqlite-runner`, `web-inspector`,
`vue`, `react-core` — in a worktree installed with `--ignore-scripts`,
which skips `better-sqlite3`'s native build. This change touches no
package source, so CI is the gate on those; please confirm they are
green here before merging.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- place the thread-title task after the embedded conversation transcript
- explicitly tell the reused agent not to answer the conversation
- add a regression test that locks the prompt ordering
## Why
LangGraph starter agents can interpret the final embedded `user:` line
as the active request when the transcript comes last. They answer the
conversation instead of returning title JSON, causing retries and
eventual `Untitled` thread names.
## Validation
- reproduced with the latest LangGraph starter and
`@copilotkit/runtime@1.69.2`
- original prompt: 0/12 direct calls returned title JSON; 4/4 targeted
threads fell back to `Untitled`
- reordered prompt: 12/12 direct calls returned title JSON; 4/4 targeted
threads received generated titles
- runtime thread-name unit suite: 28/28 passing
- pre-commit affected package checks passing under the repository Node
22 toolchain
## Summary
- honor the predictive-state `tool_argument` source field instead of
indexing tool arguments by destination `state_key`
- preserve the agent's existing state and combine every matching
same-tool configuration into one replacement-safe `setState` call
- preserve the SDK contract by storing the complete tool arguments when
`tool_argument` is omitted
- tolerate incremental JSON strings while ignoring unrelated tools and
malformed `PredictState` configuration
- merge current `main` and keep the final diff limited to the
PredictState implementation and regression tests in React Core's
`v1-deprecated` source boundary
## Validation
- predictive-state regression tests: 8/8 passed
- full React Core suite: 1,532 passed, 2 skipped; script tests: 47/47
passed
- dependency-aware React Core type-check: passed
- Web Inspector type-check from current `main`: passed
- React Core build: passed
- Python predictive-state control tests: 5/5 passed
- focused lint: 0 errors; formatting and `git diff --check`: passed
The final PR diff is limited to two React Core files.
FAC-122 is the canonical report; this also covers its existing
duplicates FAC-90 and FAC-107.
Linear: FAC-122