## Release angular v0.4.0
**Scope:** `angular` | **Bump:** `minor`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `angular` packages to `0.4.0`
and generated AI-enhanced release notes.
2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
must pass before merging. This is the review gate.
3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.
4. **When this PR is merged**, the `release / publish` workflow
automatically:
- Builds all packages
- Publishes the `angular` packages to npm at version `0.4.0`
- Creates git tag `angular/v0.4.0`
- Creates a GitHub Release with the final release notes
### Before merging
- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)
---
> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
Inspector Threads now has **Try from here**. One click copies a stored
thread into a Playground scratch session. The stored thread does not
change.
If the copy fails, Inspector stays on Threads and keeps the current
Playground scratch. Example tour threads and locked Threads do not show
the button.
## What does this PR do?
Adds **Try from here** on a real stored thread in Inspector Threads. One
click copies messages and thread state into a Playground scratch
session. The stored thread does not change.
If the copy fails, Inspector stays on Threads and keeps the current
Playground scratch. Example tour threads and locked Threads do not show
the button.
## Related PRs and Issues
- Linear: OSS-873
- Playground base: https://github.com/CopilotKit/CopilotKit/pull/6580
(merged)
## 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
## Testing
**Commands run**
1. Rebased `feat/oss-873-try-from-here` onto `origin/main` and resolved
6 conflict files.
2. `npx nx run @copilotkit/web-inspector:test` — 626 tests passed (after
the stale-result guard).
3. `npx nx run @copilotkit/web-inspector:check-types` — passed.
**Manual test**
1. Open Inspector on localhost with Intelligence on, so a real stored
thread exists.
2. Open that thread. Confirm **Try from here** is in the thread header.
3. Click **Try from here**. Confirm Inspector opens Playground with the
copied messages and the stored thread is unchanged.
4. Open an example tour thread. Confirm **Try from here** is not shown.
5. Force a copy failure (disconnect runtime). Confirm Inspector stays on
Threads and the prior Playground scratch is unchanged.
**How this PR makes testing easy**
- `packages/web-inspector/src/__tests__/inspector-navigation.spec.ts`
covers the button, copy path, failure path, and a stale click that must
not overwrite Playground.
- `packages/web-inspector/src/lib/__tests__/telemetry.test.ts` covers
`oss.inspector.threads_try_from_here_clicked`.
## Risk / rollback
Risk is limited to Inspector Threads and Playground. A revert of this PR
removes the button and the new telemetry event. No runtime protocol
change.
## Public API change
New Inspector telemetry export and event name:
**Before**
```ts
trackThreadsTabClicked(props);
```
**After**
```ts
trackThreadsTabClicked(props);
trackThreadsTryFromHereClicked({ ...props, outcome: "success" });
```
`CpkThreadInspector` also emits a `tryFromHere` custom event when the
user clicks the button.
Closes OSS-904.
## The problem
`CopilotKitCore.runtimeConnectionStatus` was set only by the `/info`
handshake, which runs **once on connect**. If the runtime became
unreachable after that, the status stayed `connected` indefinitely —
measured two independent ways against `examples/v2/react/demo` and
recorded in the ticket.
The failure was not lost, it was filed in the wrong drawer: it arrived
as `agent_run_failed`, indistinguishable from an agent bug. So
everything downstream inherited the wrong answer — System Health
reported healthy, the launcher error signal could not fire for the most
common real symptom ("it worked a minute ago"), and a customer `onError`
handler written to separate wiring problems from agent problems got the
wrong classification.
## What this changes
The status now reports **the outcome of the last actual contact with the
runtime**.
A failed runtime request — or silence past a per-request watchdog —
triggers **one** bounded confirmation request. If nothing answers, the
status moves to `error` and the failure is emitted through the existing
wiring error code, so customers already handling startup wiring failures
pick up the mid-session case without changing a line. A subsequent
successful request re-syncs and clears it.
Crucially, **the conversation survives**. The transition does not
discard runtime knowledge, so the agent backing an open chat is the same
instance, its messages stay on screen, and submitting stays possible —
which matters because submitting is what restores the status.
No polling, no heartbeat, no retry loop. Every timer is bound to one
request and dies with it.
## Decisions worth knowing when reviewing
- **Reactive in both directions.** A heartbeat would put permanent
background traffic into every embedding application; a retry loop mostly
races a user who is about to retry anyway. The cost is stated rather
than hidden: while nothing is happening, nothing is detected.
- **Status change is separated from discarding knowledge.** The only
pre-existing code that set `error` also cleared `remoteAgents`. That is
right at startup and destructive mid-session, because conversation state
lives on the agent instance. Four sites now hold this invariant up
together; each carries a comment saying so.
- **The trigger is deliberately permissive, and the check is the
arbiter.** A request that received a successful response never triggers
a check; user cancellation never does; everything else may. Defining the
trigger precisely would mean maintaining a status-code list that is
complete only for the deployment topologies someone thought of.
- **Silence counts.** A server can refuse (fails fast) or hang (accepts
and never answers). A stopped dev server refuses; a container
mid-rollout, a half-switched deploy and a dropped tunnel hang. Only
bounding the check does not help, because no check starts — hence the
per-request watchdog. It observes only and never cancels the request.
- **The rule is stated by destination, not by call site**, so a runtime
route added later inherits the behaviour. Excluded: the Intelligence
realtime endpoint (a different service — reporting its outage as
"runtime unreachable" would be a false diagnosis), endpoints belonging
to the customer, and the stop request.
- **Recovery may prune, under two conditions**: the runtime must have
reported at least one agent, and the agent must carry no conversation
state. An empty list is the signature of a runtime that has not finished
registering.
- **"Answered but refused" keeps the error status and gets a different
message.** An expired token means the app cannot work, so red is right;
telling the reader "unreachable" would send them to check ports and
containers.
## Deliberately not delivered
- Detecting an outage, or a recovery, while the application is idle.
- Recovery by opening the Threads view: every binding withholds thread
requests until the status is already connected, so nothing is sent while
it is red. The thread plumbing still earns its place for *detection*.
- A signal for the Intelligence realtime endpoint failing while the
runtime is healthy — a real gap, and its own ticket.
- Memory and suggestion routes adopting the instrumented fetch.
- A new status value or a new error code.
## Costs this introduces
`error` now means two things — "never connected, no agents" and "lost
mid-session, agents intact". Documented on the enum. And because the
status can now change mid-session at all, an outage costs some churn
that did not exist before: the memory list and the Inspector's thread
list are cleared and refetched, and where the chat owns its run-activity
store it is stopped and restarted. All of it is paid on a user-caused
transition, never while idle.
## Testing
Four independent reviewers audited an earlier revision of this branch;
the ten defects they reproduced are fixed and each is pinned by a test
that was red first. A mutation audit of 110 mutants killed 90; the
surviving holes were closed in the round after.
The connection-health suites carry 72 tests. Request counting is a
first-class assertion throughout, because several decisions are
*absences* — no polling, no retry loop, one check per burst, no traffic
while red — and an absence is only testable by counting. Those tests use
fake timers advancing ten minutes; that boundary is documented where it
lives, since anything slower is invisible to them.
Verified by hand in a browser with the runtime running as its own
process, so the page outlives it: a refusing runtime, a hanging runtime,
recovery, an agent added during an outage, an agent deleted during an
outage. `performance.timeOrigin` was checked throughout to prove the
page never reloaded and the result was not an artefact of a fresh
handshake.
## Follow-ups this leaves behind
Three of these deserve their own ticket. None blocks this PR; all three
are consequences of where its scope was drawn, and they are listed here
so the boundary is explicit rather than implied.
### 1. A signal for the Intelligence realtime endpoint
In Intelligence mode the browser gets its chat events from a **second
service** at its own address; the runtime is only asked for the
credentials. If that service fails while the runtime is healthy, this
change correctly reports the runtime as reachable — and the user
experiences exactly the silence this ticket exists to remove.
It is excluded here on purpose: folding it into the runtime status would
report "runtime unreachable" about a healthy runtime, and a false
diagnosis costs more debugging time than no signal. It needs its own
signal, which is a presentation decision as much as a detection one.
### 2. Memory routes onto the instrumented fetch
The memory store still builds with the global fetch, so its
runtime-bound requests are invisible to connection health. Two costs: a
genuine failure there is a signal we discard, and a success there cannot
restore the status.
The asymmetry is what makes this worth fixing rather than leaving:
memory is the surface most disrupted by a status transition (its list is
cleared and refetched) and currently the one least able to contribute.
The change itself is small — that module already takes its request
function as an injected dependency.
### 3. Consumers should key on what they need, not on the status value
Several consumers treat "status is not connected" as "discard
everything": the memory list, the Inspector's thread list, and the
chat's run-activity store. That was harmless while the status could not
change after page load. It can now, so every outage costs churn that did
not exist before.
This is the same mistake this PR fixes three times *inside* core — a
guard bound to a state instead of to the thing it protects. The
principle was applied internally and not to these consumers. That makes
the churn listed under "Costs" above **deferred rather than inherent**,
and it is the largest of the three follow-ups: three consumers in three
packages, each with its own risk, which is why it was kept out of this
PR.
### Two smaller items
- The launcher error signal on `main` carries a comment stating the
limitation this change removes ("a runtime that dies after the page
loaded … raises nothing … closing that gap means a re-probe in the
core"). It becomes false when this lands and should be corrected then.
- `packages/web-inspector/src/styles/generated.css` is build output
under version control and re-dirties the tree on every build. Unrelated
to this PR, but the Tailwind source glob scans test files, so any prose
comment containing a utility word (`fixed`, `hidden`, `visible`,
`block`) silently changes the committed CSS. Narrowing the glob would
remove the class of problem.
Full specification, including the interview decisions and every
revision: `OSS-904-PRD.md`.
## Why
The Inspector said what Intelligence *is* and linked out to a signup
page. Of ~1,655 Inspector opens in 90 days, **under 100 clicked any
CTA**. This replaces the feature list with an argument, and the outbound
link with an install that happens in the editor the developer is already
in.
This is the unfinished half of OSS-867, whose body asks for exactly
this: *"If a capability requires Intelligence, detail why and include a
video demonstrating that feature working end-to-end."*
## What changed
**A four-slide argument, paired to the picture beside it.** Each slide
carries two sentences and the visual they describe: your users' threads
→ the pattern inside them → the skill file → what it compounds into. An
earlier draft sold Threads in prose while animating Learning; bound
together they read as one chain, and `meeting-scheduling/SKILL.md`
recurs through all four so the closing diagram is checkable rather than
decorative.
Condensed from the six-phase animation on the Intelligence home page —
not screen-recorded. A ported version is themeable, stays sharp, and
costs no asset weight; the original also runs 21.4s and opens on the
agent booking the wrong meeting, a poor first frame for a card arguing
for the product.
**A copy-prompt button instead of a link out.** It hands the CLI's own
onboarding prompt to a coding agent. Every previous Intelligence CTA
opened a new tab into a signup form, which is where developers drop out.
It carries the CLI's `onboarding_run_id`, so
`oss.inspector.home_prompt_copied` can be joined to
`cli.onboarding.completed` on the Intelligence side. `home_cta_clicked`
only ever proved that someone clicked a link — this is the first event
that can show whether an install followed.
**Section anatomy mirrors System Health** (header band, rule, content),
so the action sits in the same top-right slot the status pill and renew
link already use, and the panel keeps one section shape throughout.
## Correctness of the claims
The copy was checked against the product's own surfaces, and two claims
did not survive:
- **Skills are not applied at run time.** Candidates land at
`pending_review`, a human approves, and the published set is pulled down
with `copilotkit skills download`. Nothing reads published skills during
a run. The slide says approve → pull in → the next run starts from what
worked.
- **Insights were missing**, and with them the evidence link that makes
Learning credible: every Insight cites the Threads and messages behind
it.
Also: *Rich Threads* is the product's name for the durable ones, and the
distinction is the whole sale next to a Threads tab full of local ones
that die on reload. "Your users" means the app's end users — which is
what the platform means too (`identifyUser` resolves one user per
request; a thread carries `end_user_id`, renamed from `user_id` because
the old name *"caused repeated misdiagnosis"*).
## Behaviour worth reviewing
- The story advances **only while Home is visible and the document is
not hidden**. A debugging tool should not hold a repeating timer behind
a closed panel.
- Slide motion is horizontal and derived from each slide's index
relative to the active one, so clicking a tab backwards animates
backwards with no stored direction to fall out of sync.
- Copied state **expires after 4s** so the button invites a second
press; a failed copy **does not**, because that state is the only place
the prompt is selectable by hand.
- Three modes, not two: a lapsed plan keeps the renew link and never
sees an install prompt.
- The rotating copy is hidden from assistive tech (it would announce
four times a loop); one stable sentence is exposed in its place and is
test-covered so it cannot quietly rot.
## Deliberate omissions
**No third-party coding-agent logos on the button**, unlike the
Intelligence app. That app is a private hosted surface; this package
ships inside other people's sites, and vendoring Anthropic's and
OpenAI's marks is not a call to make quietly. The helper line names the
agents in text.
Deferred and worth discussing separately: ordering the Home sections by
state (health first when something is broken, Intelligence first when
nothing is), and putting the same button in the locked Learning and
Threads tabs, where intent is highest.
## Verification
617 tests pass, `check-types` clean, oxlint 0 errors. Verified live in
both themes: all four slides, uniform 16px padding on every slide, card
height stable across slides, copy success **and** failure paths, and the
header band unchanged at 76px when the copied hint appears. The reset
behaviour is mutation-checked — the file records which mutation each
test does and does not catch.
Five packages publish to npm with no `license` field, so registry
metadata and automated license scanners report them as **Unknown**:
```
@copilotkit/agentcore-runner published=1.68.1 license=<NONE>
@copilotkit/core published=1.68.1 license=<NONE>
@copilotkit/sqlite-runner published=1.68.1 license=<NONE>
@copilotkit/voice published=1.68.1 license=<NONE>
@copilotkit/web-inspector published=1.68.1 license=<NONE>
```
The repo is MIT (see `LICENSE`) and every other published
`@copilotkit/*` package already declares it — these five were simply
missed. This adds `"license": "MIT"` to each, positioned before
`"repository"` to match the sibling packages.
## Why
Reported downstream in #2860, where a corporate procurement scan refused
packages whose license it could not resolve. That class of scanner reads
the `license` field from registry metadata; a `LICENSE` file in the repo
is not enough, and these packages ship no `LICENSE` file either.
**Correcting the record on that issue while I am here:** the `@ag-ui/*`
packages named in the original report are *not* affected. Every version
the reporter’s scanner flagged already carries `"license": "MIT"`:
```
@ag-ui/client@0.0.42 MIT
@ag-ui/core@0.0.37 MIT
@ag-ui/core@0.0.42 MIT
@ag-ui/encoder@0.0.42 MIT
@ag-ui/langgraph@0.0.20 MIT
@ag-ui/proto@0.0.42 MIT
```
`@ag-ui/core` has declared MIT since at least 0.0.35. An earlier triage
note on #2860 attributed the failure to a missing SPDX field upstream;
that was wrong, and why their scanner reported `Unknown` for `@ag-ui/*`
is still unexplained. This PR fixes the part that is genuinely defective
on our side.
## Testing
Metadata-only; no source, build, or runtime change.
- Confirmed the five missing fields against the live registry with `npm
view <pkg> license` (output above), and confirmed the other published
`@copilotkit/*` packages (`runtime`, `react-core`, `react-ui`, `shared`,
`sdk-js`, `angular`, `channels`, `channels-core`) already report `MIT`.
- Enumerated every non-private `packages/*/package.json` on
`origin/main` to confirm these five are the complete set missing the
field.
- Each edited file re-parsed with `json.load` and reports `MIT`.
- The `sync-lockfile` pre-commit hook resolved all 71 workspace projects
against the edited manifests without error.
Placement matches `packages/shared/package.json`, where `"license"`
immediately precedes `"repository"`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
When an agent is asked to fix Inspector UI, it must start the standalone
lab and take screenshots.
This PR adds `skills/inspector-workbench/SKILL.md` next to
`inspector-docs`. `AGENTS.md` and `CLAUDE.md` point at it, so CopilotKit
employee sessions load it by default.
## What does this PR do?
- Adds the `inspector-workbench` skill. The default host is `nx run
@copilotkit/web-inspector:dev:standalone` at `http://127.0.0.1:5177`.
- Requires a screenshot after each visual change. Screenshot files go in
`.inspector-workbench/` (gitignored), not the repo root.
- Cross-links `inspector-docs` when a pane is added, renamed, or
removed.
- Registers the slug in `RESERVED_LIFECYCLE_SLUGS` so `pnpm
sync:plugin-skills` does not delete the skill.
## Related PRs and Issues
None.
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] "Allow edits by maintainers" is checked (lets us help iterate on
your PR directly — faster turnaround for everyone)
## Testing
1. Commands run:
- `pnpm check:plugin-skills` passed (`plugin skill mirror in sync`).
- `pnpm exec vitest run scripts/__tests__/sync-plugin-skills.test.ts`
passed.
- Full package tests were not run. This change is agent instructions
plus the reserved-slug list.
2. Manual test:
1. Open `skills/inspector-workbench/SKILL.md`.
2. Confirm the default command is `nx run
@copilotkit/web-inspector:dev:standalone`.
3. Ask an agent to fix Inspector UI. Confirm it starts the lab and takes
a screenshot before it claims the UI is done.
3. How this PR makes testing easy: the reserved-slug unit test now
includes `inspector-workbench`. CI `plugin-skills-check` will run on
this path.
## Risk / rollback
Low. This is agent instruction plus a gitignore folder. Revert the PR to
undo.
The assertion searched footer.outerHTML for "241" to prove the unclamped
thread count never reaches the user. outerHTML also carries lit's part
markers, which lit builds as `lit$` + nine digits from Math.random(),
regenerated per process. Roughly one process in a hundred rolls a marker
containing those digits and fails the assertion with no relation to what the
footer rendered — this run drew lit$924125892$.
Comments are stripped before the check. Visible text and attributes still
count, so a genuine leak in an aria-label is caught exactly as before, and the
stripping is asserted so it cannot silently stop working.
Not introduced here: the same dice roll could hit any change to this package.
The other outerHTML assertions in the suite compare two strings from the same
process and share a marker, so they were never exposed.
The rail's four tabs reported nothing. Adds
oss.inspector.home_story_beat_selected, carrying the step as a property rather
than one event per label: the labels are expected to move as the story is
iterated, and per-label events would retire with them. beat_index rides along
so a reorder can be judged against where people actually click.
Only a press reports. The story also advances on its own every few seconds,
and reporting that would emit one event per idle developer per beat and bury
the handful of real interactions under a metronome — asserted, not assumed.
## Summary
- follow up on the post-merge review of #6747: LangChain projected each
`wrapModelCall` request through that middleware's own schema, which
stripped `agentName` owned by the sibling Deep Agents middleware
- preserve sibling-owned fields at the shared CopilotKit middleware
boundary while retaining the existing `exposeState` default-off and
allowlist behavior
- add a real `createAgent` regression that proves `agentName: "Mochi"`
reaches the model system prompt
The docs merged in #6747 need no further copy change; this fixes the
shared runtime boundary they exercise.
## Validation
- reproduced RED on merged `main`: the real model received only the
original system prompt
- focused regression GREEN
- `pnpm nx test @copilotkit/sdk-js` (118 passed)
- `pnpm nx check-types @copilotkit/sdk-js`
- `pnpm nx build @copilotkit/sdk-js`
- pre-commit package, publint, attw, binary, formatting, and
environment-name checks
Fixes
[FAC-66](https://linear.app/copilotkit/issue/FAC-66/deep-agents-ts-interrupt-based-docs-do-not-persistuse-agent-name).
## Summary
- complete the Python and TypeScript Deep Agents interrupt setup so the
chosen name persists in graph state
- expose only the name field to the model and explain how it should use
that state after resume
- keep both public interrupt guides in parity and test authored,
rendered, and LLM-text output
## Validation
- `npx vitest run src/lib/__tests__/deepagents-interrupt-docs.test.ts`
(3 passed)
- `npm run typecheck`
- `npm run build`
- full shell-docs suite: 478/479 passed; the remaining Mastra
tool-rendering import assertion is pre-existing and unrelated
Fixes
[FAC-66](https://linear.app/copilotkit/issue/FAC-66/deep-agents-ts-interrupt-based-docs-do-not-persistuse-agent-name).
Also covers FAC-67, which already duplicates FAC-66.
## Problem
Vue 3 is a documented frontend and generative UI is the capability that
turns a chat box into the product — every completing showcase cell's
proof is a *card*, not a paragraph. But no page described how a tool
result becomes a rendered surface in Vue.
A showcase run pairing AWS Strands with Vue reached exactly that point,
correctly refused to invent a rendering path, and said so:
> "The official Vue documentation also does not document Strands
generative-UI rendering, so none was invented or claimed."
It shipped a text answer. Vue has 2 recorded runs against Next.js's 42 —
the least-covered frontend is also the one where the most valuable
capability was undocumented, and those reinforce each other.
## The capability was never missing
`packages/vue` already ships the whole surface: `useRenderTool`,
`useDefaultRenderTool`, `A2UIMessageRenderer`,
`A2UISurfaceActivityRenderer`, `OpenGenerativeUIRenderer`,
`MCPAppsActivityRenderer`, a full `src/v2/components/a2ui/` catalog and
adapter, e2e coverage, and two working demo pages under
`examples/v2/vue/demo/`. Notably it is React-free *by design* — the A2UI
code carries comments explaining it duplicates small helpers
specifically to avoid pulling `@copilotkit/a2ui-renderer`'s React
dependencies.
So this is a docs task, not an SDK one.
## But the gap was structural, not editorial
This is the part worth reviewing carefully, because it's why a guide
file alone would not have fixed anything.
**Sidebar.** `getFrontendQuickstartNavTree()` gated its guides branch on
`id === "angular"`. Angular gets its 8 guides; every other frontend got
an empty array plus a "Guides coming soon" placeholder. The new test's
red-check shows Vue's entire sidebar:
```
AssertionError: expected [ '/vue', …(2) ] to include '/vue/guides/generative-ui'
```
Three URLs.
**Routing.** `resolveFrontendDocPage()` serves `/<frontend>/<slug>` only
from a `frontends/<frontend>/<slug>` variant file, or from a doc whose
nearest `meta.json` declares `frontend: universal`.
`generative-ui/meta.json` declares no policy at all, so
`/vue/generative-ui/*` resolves **not-found**. Those pages weren't
merely React-flavored for a Vue reader — they were unreachable in the
Vue namespace.
The irony: `concepts/meta.json` **is** universal, and it holds
`generative-ui-overview`. A Vue developer could reach the page
explaining *what* generative UI is, and no page showing *how*.
## Changes
| File | Change |
| --- | --- |
| `docs/frontends/vue/guides/generative-ui.mdx` | New. The guide. |
| `lib/frontend-page-content.ts` | `VUE_GUIDE_PAGES` + a
`FRONTEND_GUIDE_PAGES` lookup replacing the `id === "angular"` branch,
so a frontend's guides are data rather than a conditional. Angular's
tree is unchanged. |
| `docs/frontends/vue.mdx` | The missing "Where to go next" pointer. |
| `lib/__tests__/frontend-options.test.ts` | Three tests. |
The guide covers `useRenderTool`, `useDefaultRenderTool`,
`useFrontendTool` with a renderer, A2UI (provider-level and
catalog-on-provider), Open Generative UI, and MCP Apps — written from
`packages/vue` source and the in-repo demos, not translated from the
React docs.
Two things it states deliberately:
- **It does not depend on the agent framework.** The reporting run read
the absence as Strands-specific. Generative UI reads AG-UI tool calls;
nothing changes when you swap the agent. The guide says so up front.
- **`useRenderTool` and `useFrontendTool` do not hand their renderers
the same props.** The former normalizes to `parameters` + a string-union
status; the latter passes through to core with `args` + the
`ToolCallStatus` enum. A renderer written for one silently draws nothing
in the other. Verified in source, not inferred.
### One note on the link form
The quickstart links the guide as `/vue/guides/generative-ui`, not the
relative `guides/generative-ui` that `angular.mdx` uses.
`resolveDocsHref` returns any non-root-relative href untouched, and
`next.config.ts` sets no `trailingSlash` — so the relative form would
resolve against `/vue` and land on `/guides/generative-ui`, which
doesn't exist. A test pins the authored href and asserts it both
survives rewriting and resolves. (`angular.mdx:241` uses the relative
form and looks like it has the same problem; not touched here.)
## Verification
- `frontend-options.test.ts` — 25/25. **Red-checked twice**: commenting
out the single nav wiring line fails the sidebar test; reverting the
href to the relative form fails the link test. Both can actually fail.
- Full `shell-docs` suite — 475/476. The one failure
(`llm-text.test.ts`, mastra tool-rendering) **reproduces on unmodified
`origin/main`** with these changes reverted. Pre-existing, unrelated.
- `tsc --noEmit` clean. `oxfmt --check` and `oxlint` clean.
- Search index regenerated: the page parses and is indexed at `href:
"/vue/guides/generative-ui"`, section "Frontends".
## Deliberately out of scope
1. **No Vue redirect map.** Angular's `ANGULAR_DOC_REDIRECTS` maps ~20
`generative-ui/*` slugs onto its guides, so
`/angular/generative-ui/tool-rendering` lands somewhere useful.
`/vue/generative-ui/tool-rendering` still 404s. That's a policy decision
about how much React IA to mirror into Vue.
2. **Backend-scoped variant.** On `/vue/<backend>`, `resolveDocsHref`
rewrites cross-section links — `/generative-ui/a2ui`,
`/generative-ui/mcp-apps`, `/inspector` — into that prefix, where they
resolve not-found. This follows from those sections having no
`frontend:` policy, is the same for every non-Angular frontend page
today, and is not introduced here. The sidebar link to the guide is
correct in both contexts.
3. **`FRONTEND_REFERENCE_SLUGS.vue` left alone — but please look at
it.** Vue's sidebar "Reference docs" link points at `"reference"`, the
**React** reference, despite a complete 25-page `/reference/vue` tree
existing and registered in `reference-items.ts`. It's pinned by an
assertion at `frontend-options.test.ts:538`, so it looks deliberate. If
it's an oversight it compounds this exact bug — a Vue developer sent to
the React reference cannot find `useRenderTool`'s Vue signature.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Problem
`useAgentContext` stringifies any non-string value before it leaves the
browser, and the AG-UI protocol types `Context.value` as a string on
both ends. An agent therefore always reads a **JSON string** — never the
object or array that was registered.
None of the four reference pages said so. They stopped at the browser
half:
- `/reference/v2/hooks/useAgentContext` — "Object values are serialized
automatically."
- `/reference/react-native/hooks/useAgentContext` — the same sentence.
- `/reference/vue/hooks/useAgentContext` — "Non-string values are
serialized with `JSON.stringify` automatically."
- `examples/v2/docs/reference/use-agent-context.mdx` — "Can be any
serializable value."
"Serialized automatically" reads as *the framework handles it*, not
*your agent gets a string and must parse it*. No page mentioned
`json.loads`, `JSON.parse`, or what the agent side receives.
## Why it matters
An author who believes that writes an agent that reads the object. When
the shape check fails, the agent cannot distinguish "context arrived
JSON-encoded" from "no context was sent" — the two are byte-identical —
so it refuses every request while the browser is registering context
perfectly.
That is what happened on the `both-oss::langgraph-python::nextjs`
conversion journey (OSS-1003). The agent guarded on
`isinstance(entry["value"], list)`, which the protocol can never
satisfy, so its success path was unreachable on every real run and the
journey was dead on arrival. Reproduced directly against that graph:
```
A wire shape (value = JSON string) -> "...context is missing."
B object shape (value = list) -> resolved correctly
C no context at all -> identical to A
```
## Change
Each of the four pages gains a `## What the agent receives` section:
- the wire shape shown as literal JSON
- `json.loads` (Python) and `JSON.parse` (TypeScript) agent-side
examples
- a callout naming the shape check as the trap, and why a failed check
is indistinguishable from absent context
The `value` parameter description and the `Serialization` behavior
bullet on each page now name the consequence for the agent author and
link to that section, instead of stopping at the browser half.
Docs only — no source or runtime behavior changes.
## Verification
- All four files parse-check clean: balanced code fences, balanced JSX,
anchor targets present and linked.
- `Callout` is globally registered
(`showcase/shell-docs/src/lib/mdx-registry.tsx:263`), so no per-file
import is needed.
- `shell-docs` `llm-text.test.ts`: **10 failed | 32 passed** both with
and without these edits — identical, so this change introduces nothing.
Those 10 are pre-existing `claude-sdk`/`google-adk` content assertions
that fail on stale generated data in a fresh worktree.
- `.mdx` is outside lefthook's `lint-fix` glob, so oxfmt/oxlint do not
reformat these files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## 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.
Vue 3 is a documented frontend and generative UI is the capability that turns a chat box
into the product, but no page described how a tool result becomes a rendered surface in
Vue. A run pairing AWS Strands with Vue reached that point, correctly refused to invent a
rendering path, and shipped a text answer instead.
The capability was never missing. packages/vue ships useRenderTool, useDefaultRenderTool,
A2UIMessageRenderer, A2UISurfaceActivityRenderer, OpenGenerativeUIRenderer, a full a2ui/
catalog and adapter, e2e coverage, and two working demo pages. Only the docs were absent.
They were absent structurally, not editorially. getFrontendQuickstartNavTree gated its
guides branch on `id === "angular"`, so every other frontend got an empty list plus a
"Guides coming soon" placeholder -- Vue's whole sidebar was three URLs. Routing matched:
resolveFrontendDocPage serves /<frontend>/<slug> only from a frontends/<frontend>/ variant
or a section marked `frontend: universal`, and generative-ui/meta.json declares no policy,
so /vue/generative-ui/* resolved not-found. concepts/meta.json IS universal, so a Vue
reader could reach the page explaining what generative UI is and no page showing how.
Add the guide, and replace the Angular identity check with a FRONTEND_GUIDE_PAGES lookup
so a frontend's guides are data rather than a branch. Angular's tree is unchanged.
The guide is written from packages/vue source and the in-repo demos rather than translated
from React, states up front that none of this depends on the agent framework, and documents
that useRenderTool and useFrontendTool do not hand their renderers the same props --
`parameters` plus a string-union status versus `args` plus the ToolCallStatus enum -- so a
renderer written for one silently draws nothing in the other.
The quickstart's link to the guide is the fully-qualified /vue/guides/generative-ui rather
than the relative form angular.mdx uses. resolveDocsHref returns non-root-relative hrefs
untouched and next.config sets no trailingSlash, so `guides/generative-ui` would resolve
against /vue and land on /guides/generative-ui. A test pins the authored href and asserts
it both survives rewriting and resolves.
Not addressed here: Vue has no equivalent of ANGULAR_DOC_REDIRECTS, so /vue/generative-ui/*
still 404s rather than landing on this guide, and on the backend-scoped variant of the page
(/vue/<backend>) resolveDocsHref rewrites cross-section links like /generative-ui/a2ui and
/inspector into that prefix, where they do not resolve. Both follow from the missing
`frontend:` policy rather than from this guide. Separately, FRONTEND_REFERENCE_SLUGS.vue
points at the React reference despite a complete /reference/vue tree; it is pinned by a
test assertion, so it is left alone here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
useAgentContext stringifies any non-string value before it leaves the
browser, and the AG-UI protocol types Context.value as a string on both
ends. An agent therefore always reads a JSON string, never the object or
array that was registered. None of the four reference pages said so; they
stopped at "serialized automatically", which reads as "the framework
handles it".
An author who believes that writes an agent that reads the object. When
the resulting shape check fails, the agent cannot distinguish "context
arrived JSON-encoded" from "no context was sent" -- the two are
identical -- so it refuses every request while the browser is registering
context correctly. That is what happened on the both-oss
langgraph-python conversion journey, where the agent's
isinstance(value, list) guard could never pass and the journey was dead
on arrival.
Each page now carries a "What the agent receives" section: the wire shape
as literal JSON, json.loads and JSON.parse examples, and a callout naming
the shape check as the trap. The value parameter description and the
Serialization behavior bullet now name the consequence for the agent
author instead of stopping at the browser half.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## 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.
The spec replaced @copilotkit/core wholesale with a two-export factory. That
held until the Inspector started mounting in these tests — it is enabled by
default in browser frameworks now, and its connectedCallback calls
isInspectorThreadBridgeEnabled, one of seventeen value exports it imports from
core. A missing one throws an uncaught exception, so the run fails while all
49 test files still report passing, which is a confusing way to find out.
The factory now spreads the real module and overrides only CopilotKitCore and
the connection-status enum, which is what these tests actually drive. Listing
the seventeen would have postponed the next occurrence rather than removed it.
Surfaced by the web-inspector work on this branch: angular only runs when
affected, and it becomes affected the moment web-inspector changes — so the
first PR to touch web-inspector after the default-on change was going to hit
this regardless of what it changed.
## 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)
The checked-in artifact was produced mid-rebase, so its Tailwind token block
reflects the utilities in use at that point rather than on today's main. A
fresh build:css on the rebased tree differs; this is that output, so the
committed file matches what the build produces.
"What Intelligence does" promised an explanation and pointed at
intelligence.copilotkit.ai — the product and signup page. Mis-promising a
destination is a poor trade at the moment the card is asking to be trusted,
and the label was doing no work besides.
The two actions are two routes to one outcome: let the coding agent wire it
up, or go and do it in the browser. The secondary now says so — "Set it up
yourself" — which is honest about where it lands and quietly argues for the
primary, because "yourself" implies the other route is not.
The confirmation used to stand for the rest of the session. The reasoning was
that a developer leaves for their editor and comes back and should still find
the instruction — but whoever comes back has already pasted. The likelier
reason to return is to copy again, and a button wearing a checkmark and
"Prompt copied" reads as spent even though it still works.
Copied now reverts after 4s: button label and secondary line both return, so
the "What Intelligence does" link comes back too. Longer than the 2s the
Threads setup prompt uses, because this state carries an instruction to read
and not just an acknowledgement.
A failed copy does not expire. That state is the only place the prompt text is
selectable by hand, and pulling it away mid-selection would be worse than the
clipboard failing.
Three tests, mutation-checked: disabling the reset kills the two copied-state
tests. The failed-state test is blunter and only fails when both safeguards go
(the scheduling condition and the timer's own state re-check) — recorded in the
file so nobody reads more into it than it proves.
The instruction was a second row under the button, which pushed the action
column past the band's 76px min-height and shoved the whole story down at the
moment the developer had just acted — the worst possible time for the layout
to move.
It now takes over the secondary slot instead of adding to it. Before the press
the useful aside is "what is this"; after it, "where to put it". Both are
single lines in the same flex slot, so the swap cannot change the height:
measured 76px and an unmoved story in both states.
Dropping the line was the alternative, but it is the one instruction that
cannot go: a prompt on the clipboard with no idea what to do with it converts
nobody. It is also vendor-neutral now — naming three editors was brittle and
the prompt identifies the agent itself.
- "Your users had thousands" carried a count a developer wiring this up
locally does not have yet. "Your users have all the others" holds on day one
and at scale. Whose users: the app's end users, which is what the platform
means too — `identifyUser` resolves one user per request from the app, and a
thread carries `end_user_id`, a column renamed away from `user_id` because
the old name "caused repeated misdiagnosis" against control-plane users.
- The Skills line defended instead of selling. "Nothing reaches your agent
until you approve it" answers a fear the reader has not voiced and plants
the worry it deflects. Same fact as ownership: a SKILL.md that is yours to
review, edit and ship.
- The last tab is "Intelligence", not "Better agents". The other three are
the parts; this one is the whole, so the rail reads Threads · Learning ·
Skills · Intelligence. Beat id renamed to match, selectors included.
Checked the four slides against the product's own copy instead of against
intuition, and two claims did not survive.
- "Skills apply it — without you writing another prompt" said the platform
applies skills at run time. It does not: candidates land at pending_review,
a human approves, and the published set is pulled down with
`copilotkit skills download`. Nothing reads published skills during a run.
The slide now says approve, pull in, and the next run starts from what
worked — which is also the stronger pitch, because a developer does not
want a platform silently changing how their agent behaves.
- Insights were missing entirely, and with them the evidence link that makes
Learning credible: every Insight cites the Threads and messages behind it.
Learning's own onboarding leads with "46 evidence refs" for that reason.
Also: "Rich Threads" is the product's name for the durable ones, and the
distinction is the whole sale next to a Threads tab full of local ones that
die on reload. A skill is a directory holding a SKILL.md, so the card shows
`meeting-scheduling/SKILL.md` and is marked Pending review.
- The rail's last step was "Reuse", which named neither a product surface nor
an outcome. It is "Better agents" — the promise, in the customer's words.
- Secondary link before the primary button: with the filled button in the
middle it read as a block wedged between the heading and the link instead
of the one thing to press.
- The heading gets the brand mark and its band a wash along the brand's hue
path, reusing the account strip's existing device. Not gradient text: at
18px it renders muddy, costs contrast, and is the most over-used premium
tell going.
The Inspector said what Intelligence is and linked out to a signup page. Of
~1,655 opens in 90 days, under 100 clicked any CTA. This replaces the feature
list with an argument, and the outbound link with an install that happens in
the editor the developer is already in.
- Copy: the heading names the product; a four-slide argument runs underneath,
each slide pairing two sentences with the picture beside it (threads →
the pattern in them → the skill file → it applies itself). An earlier draft
sold Threads in prose while animating Learning; bound together they read as
one chain.
- Copy prompt: hands the CLI's own onboarding prompt to a coding agent instead
of opening a signup form. Carries the CLI's onboarding_run_id, so
oss.inspector.home_prompt_copied can finally be joined to
cli.onboarding.completed — home_cta_clicked only ever proved a click.
A refused clipboard reveals the prompt instead of swallowing it.
- Three modes, not two: a lapsed plan keeps the renew link and never sees an
install prompt.
- Section anatomy mirrors System Health (header band, rule, content), so the
action sits in the same top-right slot the status pill and renew link use.
- The story only advances while Home is visible and the document is not
hidden; a debugging tool should not hold a repeating timer behind a closed
panel.
No third-party coding-agent logos on the button, unlike the Intelligence app:
this package ships inside other people's sites, and vendoring those marks is
not a call to make quietly. The helper line names the agents in text.
## 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.