Two issues found after the example merged:
1. The closed chat panel is only translated off-screen, so its inputs/buttons
stayed in the tab order and the a11y tree. Mark the <aside> `inert` while
closed (`[attr.inert]`), so focus/AT skip it; removed when open.
2. The dev web inspector (and its @copilotkit/web-inspector dep, ~660 kB) shipped
in the production initial bundle. Gate it behind `@defer (when isDev)` so it's
split into a lazy chunk that a prod build (isDev=false) never loads.
Verified: prod `ng build` initial bundle 4.59 MB -> 3.93 MB with web-inspector now
a lazy chunk; dev serve still mounts the inspector; closed-panel focus is blocked
(activeElement falls back to body), open panel unaffected.
## What & why
Adds the first **Angular** scaffoldable integration example:
`examples/integrations/adk-angular/` — an Angular 21 SPA wired to a
Google ADK agent over AG-UI, a peer to the React
`examples/integrations/adk`. Angular support is selective (ADK first),
so this surfaces as a new **"Angular + ADK"** entry in `copilotkit init`
(CLI wiring is a companion PR in the Intelligence repo).
## Architecture — 3 processes
Angular is a SPA, so (unlike the Next.js React example, which hosts the
runtime in an API route) the runtime lives in a standalone Node server —
the documented `@copilotkit/angular` pattern.
| Process | Port | Source |
| --- | --- | --- |
| Angular app (`ng serve`) | 4200 | `src/` |
| Copilot Runtime (`tsx server.ts`) | 8200 | `server.ts`
(`createCopilotNodeListener`) |
| Python ADK agent (`uv`) | 8000 | `agent/` — copied from
`examples/integrations/adk` |
One `npm run dev` runs all three via `concurrently`.
## Feature parity with the React ADK example
- Shared agent state (proverbs) via `injectAgentStore` +
`agent.setState`
- Generative UI (weather card) via `renderToolCalls`
- Frontend tool (`setThemeColor`) via `registerFrontendTool`
- Static suggestions (the same 5)
- Threads drawer + env-gated managed Intelligence
(`CopilotKitIntelligence` when `COPILOTKIT_LICENSE_TOKEN` is set, else
`InMemoryAgentRunner` — matching the React `route.ts` gating)
- Dev web inspector (`cpk-web-inspector`), matching the richer React
examples
### Two behaviors that required real fixes (found via live testing, not
static review)
1. **Frontend tools now reach the ADK agent.** `setThemeColor` silently
failed because the ADK agent's tools were static and `ag_ui_adk` only
forwards client tools when the agent includes an `AGUIToolset()`
placeholder. Added it (this affected the React `adk` example identically
— fixed there in a companion PR).
2. **The weather generative-UI card now renders.** A `renderToolCalls`
entry scoped with `agentId` filtered the server-side tool call out;
removed it.
## Chat UI — floating, React-`CopilotSidebar`-style
Angular has no `CopilotSidebar`, so the chat is a hand-rolled
collapsible panel that mirrors React's behavior:
- **Docks + pushes** the content when there's room (viewport ≥ 1200px:
starts expanded, content reflows beside the chat via a right margin),
and **overlays** on narrower screens so the content isn't smushed by the
threads drawer's push.
- Toggle/close match React's `CopilotChatToggleButton` /
`CopilotModalHeader`: lucide `MessageCircle` FAB (bottom-right) to open;
lucide `X` in the panel header to close; no bottom-right "X" (the open
panel covers the FAB, exactly like React).
- Layout uses a global `box-sizing: border-box` reset (React/Next get
this from Tailwind's preflight) so cards don't clip on small screens.
## Dependency
Requires **`@copilotkit/angular@0.2.0`** (published; it carries the
threads drawer + `provideCopilotChatConfiguration`).
`runtime`/`core`/`shared` pinned to `1.63.1` — matches
`@copilotkit/angular@0.2.0`'s own deps (verified: no duplicate
`@copilotkit/core`).
## Testing
`ng build` green; `oxlint` 0/0; a 4-round 7-agent CR loop converged to
zero findings.
Verified **live against the hosted Intelligence platform + a real Gemini
key**:
- **Managed Intelligence connects:** `/api/copilotkit/info` → `mode:
intelligence`, `licenseStatus: valid`, full `threadEndpoints`
(list/inspect/mutations/realtimeMetadata), realtime `wsUrl`.
- **Threads render:** the drawer loads the org's persisted threads with
pagination.
- **Shared state:** proverbs seed on load; add / update / read via chat
all round-trip and update the card.
- **Frontend tool + generative UI:** "set the theme to teal, then get
the weather in Paris" recolors the panel **and** renders a teal-themed
weather card in the chat.
- **Responsive:** dock-and-push at ≥1200px (content beside the chat, 0
overflow); overlay + starts-closed below; no clipping at 580 / 1050 /
1200 / 1400.
Not exercised: nothing outstanding — the previously-untested agent/tool
paths are now confirmed live. (`GOOGLE_API_KEY` is required for live
agent runs; the scaffold + build don't need it.)
## Follow-ups (not in this PR)
- Companion: **CopilotKit/Intelligence** CLI PR adds the `adk-angular`
framework; **CopilotKit/CopilotKit#6107** ports the `AGUIToolset` fix to
the React `adk` example.
- Inherited-from-React demo-quality items (fail-loud on missing
`INTELLIGENCE_API_KEY`, the `.sh || .bat` / `dev:debug` Windows /
`postinstall`-uv script patterns, the agent's broad `except` / dead
`ProverbsState` / prompt typos) — cross-example cleanup.
- Docs: the ADK quickstart/cookbook should note `AGUIToolset()` is
required for frontend tools.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
When the viewport is wide enough (>=1200px) the chat starts expanded and DOCKS:
.layout--pushed adds a right margin equal to the chat width so the proverbs
content reflows beside the chat instead of being covered (mirrors React's
CopilotSidebar margin-inline-end push). Below the breakpoint the chat starts
closed and OVERLAYS on open, so narrow screens aren't smushed.
- DOCK_BREAKPOINT_PX (1200) gates the initial open state; a matching
@media (min-width:1200px) rule applies the push margin
- --chat-width var keeps the panel width and push margin in sync
Verified live: 1400px starts expanded, content pushed beside the chat (right
929 <= chat left 960), 0 overflow; 1000px starts closed, opening overlays with
no push and no overflow.
Previously the FAB swapped to an X when open and sat above the panel, so an open
chat showed TWO X's (header + bottom-right). React never does that: its toggle
FAB is always a MessageCircle, and the sidebar (z-1200) covers the FAB (z-1100)
when open, so the only visible close is the header X.
- FAB icon is always MessageCircle (no X swap)
- chat panel z-index 1200 > FAB 1100, so an open panel covers the FAB
Verified live: OPEN shows only the top-right header X (FAB covered); CLOSED shows
the bottom-right MessageCircle FAB to reopen. No bottom-right X in either state.
Replace the hand-rolled unicode/emoji controls with lucide icons matching React:
- header close = lucide X (in a title-left/close-right, border-b bar like CopilotModalHeader)
- toggle FAB = lucide MessageCircle (open) / X (close), ALWAYS visible, fixed
bottom-6 right-6, h-14 w-14, rounded-full, primary-dark — matches CopilotChatToggleButton
Verified live: FAB shows X when open / MessageCircle when closed and stays visible
(React's toggle behavior); close + FAB both drive chatOpen; 0 console errors.
Replaces the permanent 420px chat column with a collapsible slide-over panel
(toggle FAB + close button), mirroring React's CopilotSidebar. The chat now
OVERLAYS rather than reserving a grid track, so the threads drawer's desktop
push no longer smushes the content and the medium-width layout has room.
- app.ts: 2-track grid (drawer + content); fixed .chat slide-over + open FAB;
chatOpen signal (defaults open, like React defaultOpen). Theme var hoisted to
the component HOST so the weather card (now in the fixed chat) still inherits it.
- styles.css + web-inspector.ts: move the dev inspector FAB to bottom-left; its
default top-right anchor (max z-index, transform-positioned) sat over the chat's
close button and ate the click.
Verified live (real Gemini key): open/close/reopen slide-over; content 880px at
1200w (no smush); 0 horizontal overflow + no clipping at 580/1050/1200; inspector
clears both the close button and open-FAB; 'set theme to teal, then weather in
Paris' recolors the panel AND renders a teal-themed weather card in the floating chat.
The proverbs card is width:100% + padding:2rem; with the default content-box the
padding was added ON TOP of 100%, making the card wider than its column and
getting clipped by the layout's overflow:hidden (visible as cut-off content on
narrow/medium screens). React/Next examples get border-box from Tailwind's
preflight; the Angular app had no global reset.
Verified live at 580px (card 578px, no clip, 0 overflow) and 1050px (card fits
its grid track, no overlap with the chat). Matches the React adk's overflow-free
responsive behavior.
setThemeColor (and any frontend-registered tool) never reached the LLM: the ADK
agent's tools were static [set_proverbs, get_weather], and ag_ui_adk only injects
the run's forwarded client tools when the agent's tools include an AGUIToolset
placeholder (it swaps it for a ClientProxyToolset wired to input.tools). Added
AGUIToolset() so gemini-2.5-flash sees the forwarded setThemeColor and calls it.
Verified LIVE (real Gemini key): 'Set the theme to green' now recolors the panel
(--app-theme-color: green) — previously the agent declined ('I can only help with
proverbs or the weather'). Same root cause affects the React adk example (fixed
in a companion change).
## What
Fixes three broken **Built-in Agent** showcase demos by forwarding the
tools that runtime middleware injects into `input.tools`. The in-process
TanStack factories were dropping them with `tools: []`.
- **open-gen-ui** — agent now calls `generateSandboxedUi` (was emitting
the UI as a raw HTML code block)
- **open-gen-ui-advanced** — same factory/route; the sandbox → host
callbacks can now engage
- **mcp-apps** — agent now sees `create_view` and can render the
Excalidraw view (was replying with plain text)
## Why
`OpenGenerativeUIMiddleware` / `MCPAppsMiddleware` inject their tool
into `input.tools` at request time. The working main
`tanstack-factory.ts` forwards `input.tools` into `chat({ tools })`, but
`ogui-factory.ts` and `mcp-apps-factory.ts` hard-coded `tools: []` — so
the model never received the tool and fell back to prose/HTML. This is
exactly why the demos look broken on the **live Railway deploy** (real
LLM): given no tool, a real model just prints the UI as text.
## How
Declare the injected tools via `toolDefinition()` — the same pattern the
main factory already uses — and `export` its `jsonSchemaToZod` helper
for reuse (no duplication).
Note: `convertInputToTanStackAI(input).tools` would be simpler, but that
return field only exists in `@copilotkit/runtime >= 1.61.0`. This app
pins **1.60.2**, so the `toolDefinition()` route is the version-safe
fix.
## Verification
- ✅ `tsc --noEmit` against the pinned `@copilotkit/runtime@1.60.2` — the
three changed files are type-clean. (The app has pre-existing, unrelated
zod-version-skew tsc errors in the A2UI catalog files; those are on
`main` and untouched here.)
- ⚠️ **D5/D6 aimock replay was NOT run** — no Docker in my environment.
Needs a CI / Docker-capable run to confirm the demos render.
- ⚠️ **Fixtures may need re-recording.** The aimock fixtures for
`gen-ui-open`, `gen-ui-open-advanced`, and `mcp-apps` (built-in-agent)
may have been captured in the broken (no-tool-call) state; if so, replay
won't demonstrate the fix until they're re-recorded against a real LLM
(showcase Procedure 3). Please confirm the D5/D6 run and re-record if
needed.
## Follow-ups
- After merge, **redeploy the built-in-agent Railway service** so the
live showcase reflects the fix.
- P0 slice of the broader **OSS-594** parity effort; structural drift +
runtime-capability gaps are tracked in that ticket.
Refs OSS-594.
## What
The built-in-agent **prebuilt-sidebar** and **prebuilt-popup** demos
each rendered a single hard-coded "Say hi" suggestion pill, even though
a correct 3-pill hook (`usePrebuiltSidebarSuggestions` /
`usePrebuiltPopupSuggestions`, byte-identical to `langgraph-python`)
already lived in each demo's own `suggestions.ts` — just never imported.
This wires the existing hooks so the pills match canonical:
- **prebuilt-sidebar**: Say hi · Fun fact · Is 17 prime?
- **prebuilt-popup**: Say hi · Limerick · Is 17 prime?
## Why
Classic scaffold-then-drift: the flavor was copied from canonical
(bringing the `suggestions.ts` hook along) but each `page.tsx` was
hand-written with a degraded inline single-pill version and never wired
back. Part of the **OSS-594** P1a "orphaned suggestions" cleanup.
## How
Call the existing hook from each `page.tsx` and drop the now-unused
`useConfigureSuggestions` import. No new files; the only behavior change
is the suggestion set.
## Verification
- lefthook (`oxlint` + `oxfmt`) clean on commit.
- Leans on CI `build-check (built-in-agent…)` + `check-types` — both
green on the sibling P0 PR #6100 for the same app.
- 6 insertions / 18 deletions across 2 files.
Refs OSS-594.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Both demos hard-coded a single inline "Say hi" pill while their own
`usePrebuiltSidebarSuggestions` / `usePrebuiltPopupSuggestions` hooks (3 pills each,
byte-identical to langgraph-python) sat unused. Call the existing hooks so the pills
match canonical, and drop the now-unused `useConfigureSuggestions` import.
Refs OSS-594.
The OGUI and MCP-Apps factories built the in-process TanStack agent with `tools: []`,
discarding the tools the runtime middleware injects into `input.tools`
(`generateSandboxedUi` for Open Generative UI, `create_view` for MCP Apps). The model
never saw the tool, so it emitted the UI as raw HTML/text instead of a tool call — no
sandboxed iframe / MCP view rendered.
Declare the injected tools via `toolDefinition()` (the pattern the working main
tanstack-factory already uses), exporting its `jsonSchemaToZod` helper for reuse. The
converter's own `tools` return only exists in @copilotkit/runtime >= 1.61.0; this app
pins 1.60.2. Fixes open-gen-ui, open-gen-ui-advanced, mcp-apps.
Refs OSS-594.
The "Generative UI: useComponent" cell (gen-ui-tool-based) went red on mastra
the moment OSS-381 took it out of not_supported: the D5 gen-ui-custom probe
sent the *haiku* prompt and hunted for a haiku card, but mastra's demo is the
LGP-style `useComponent` chart demo (render_pie_chart / render_bar_chart) with
no haiku tool — so the assistant bubble came back empty ("haiku card
[data-testid=copilot-assistant-message] rendered but has no text content").
Root cause: the probe's CHART_INTEGRATIONS allowlist in
harness/src/probes/scripts/d5-gen-ui-custom.ts omitted mastra, so
isChartIntegration("mastra") was false and it took the haiku branch. mastra's
gen-ui-tool-based page registers render_pie_chart / render_bar_chart via
useComponent exactly like langgraph-python and google-adk.
- Add "mastra" to CHART_INTEGRATIONS so the probe sends the pie-chart prompt
and asserts the donut SVG + "pie"/"chart" follow-up tokens.
- Add aimock/d6/mastra/gen-ui-custom.json (mirrors langgraph-python's, context:
mastra; the pie schema is identical — {title, description, data:[{label,value}]})
so the cell is deterministic under aimock replay instead of falling through to
the live upstream.
- Repoint the probe unit test's haiku-empty-card case from "mastra" to "agno"
(a genuine haiku integration) now that mastra is a chart integration.
Not a v1-bridge streaming regression — a harness/fixture gap exposed when the
cell was un-suppressed. Harness unit tests not run locally (sparse showcase
checkout has no vitest); logic-only changes.
--no-verify: sparse showcase checkout has no monorepo lefthook/commitlint binaries.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "Generative UI: Agent State" cell (gen-ui-agent) was stuck at D4 — the
d5-single-pill e2e ("marks every step as completed") failed with only 2/3 steps
reaching "completed", which blocks D6.
Root cause: the planner scripts 3 steps × 2 set_steps transitions (in_progress →
completed) + 1 initial "all pending" call + 1 closing message (~8 model turns),
but genUiAgent set no stop condition, so the AI SDK's default halted the agentic
loop before the 3rd step completed. (LangGraph gold loops until the graph ends
and needs no equivalent; this is the AI-SDK step-cap analogue — cf.
toolRenderingAgent's d20 sequence.)
Add defaultOptions.stopWhen = stepCountIs(12) to genUiAgent so the full
progression runs to completion.
Verified on the faithful rig (Node 22 + next build/start + aimock 1.37.4 replay):
gen-ui-agent.spec 6/6; tool-rendering 6/6 and gen-ui-tool-based 4/4 unchanged
(no regression). Isolated to genUiAgent — no other agent/demo affected.
--no-verify: sparse showcase checkout has no monorepo lefthook/commitlint binaries.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Mastra Partner Refresh — showcase finalization (OSS-381)
Bumps the showcase Mastra integration onto the **v1 bridge alpha** and
flips the
features it unblocks out of `not_supported`. Opened for CI to run the
D6/e2e
suite (local Docker daemon is wedged in the authoring env — see notes).
### Landed
- **OSS-382 (gate):** `@ag-ui/mastra` `0.2.1-beta.2` →
**`1.1.0-alpha.0`**.
- Alpha verified to ship all features (grep on dist):
`emitInterruptOutcome`,
`STATE_DELTA`, `observationalMemory`, `background-task`,
`tracingOptions`,
`getA2UITools`/recovery. Peers satisfied (`@mastra/core` 1.41,
`client-js`
1.23.2, runtime 1.61.2).
- `next build` passes (40 routes). Unit tests **identical to the beta.2
baseline** (13 pre-existing failures in `route.test.ts`'s error-path
mocks,
unrelated to the bump — proven by a stash+reinstall A/B).
- **OSS-384 / OSS-423:** moved into `features` (demos + e2e + aimock
fixtures
were already wired, gated on this release):
`agentic-chat-reasoning`, `reasoning-default-render`,
`tool-rendering-reasoning-chain`, `shared-state-streaming`. Added the
missing
`reasoning-default` / `reasoning-custom` manifest demo entries.
- **Parity:** `not_supported_features` now holds only `gen-ui-interrupt`
+
`interrupt-headless`, matching the **langgraph-python gold standard**,
which
quarantines the same two cells on an upstream `@copilotkit/react-core`
v2
resume-path hook bug (published-package fix, out of scope). The native
interrupt + RUN_FINISHED-outcome path ships in the bridge; the showcase
cell
is blocked by the same upstream bug, not the bridge.
- **OSS-424:** execution-tracing note (`tracingOptions` in / `traceId`
on
`RUN_FINISHED.result` out) added to the Mastra Copilot Runtime doc.
- **OSS-425:** GenUI `generative_ui` spectrum already at parity with
gold
(`constrained-explicit`, `a2ui-fixed-schema`, `a2ui-dynamic-schema`).
### Not in this PR (scoped, blocked, or pending)
- **OSS-422 a2ui-recovery**, **OSS-426 background-agents**, **OSS-427
observational-memory** — new demo cells. Reference material + build
plans
ready. OM additionally needs `@mastra/memory` ≥1.21.2 (repo pins
`1.0.1-alpha.1`; the on-stream async-buffering path won't fire below
that).
- **OSS-91 browser-use** — Mastra-only, non-deterministic (no clean
aimock
replay), needs a Browserbase key not present in the env. Blocked.
- **OSS-392 input.context** — owner exception; only if langgraph
showcases it.
### Verification note
Local D6 could not be run: the Docker daemon's container-creation path
is wedged
in this environment (a trivial `hello-world` create hangs), and
unwedging needs a
Docker Desktop restart that would destroy a concurrent session's running
stack.
Relying on CI for D6/e2e. Everything above is build-level verified +
committed.
## Why
Staging and production showcase dashboards were showing whole columns
red. Reproduced live: seed one integration's slug-scoped `chat:<slug>`
red and the OLD engine fans it across **every** feature cell in that
column (CrewAI column header `✗31` — all features red, even ones with no
ladder of their own). The root cause is in the dashboard's cell-model
ladder, not the demos: `resolveD4` was slug-keyed, so one transient
`chat:<slug>` red folded the whole column. Two divergent ladders
(`buildCellModel` vs the shell dashboard's `deriveDepth`) meant the chip
a viewer saw and the depth the engine computed could disagree.
This reworks the ladder derivation into one engine.
## What
- **Unified cell-model engine.** A single per-rung `classifyRung`
verdict folded uniformly through `foldFamily`/`combine`, enforcing the
§2a coherence invariants (chip / isRegression / achievedDepth /
d6Effective all from one verdict). D4 is now **feature-keyed**, so a red
in one feature stays confined to its cell — the same seed that reds the
whole OLD column renders as one amber cell with isolated gray siblings
on NEW (column header `~1`).
- **First-strike de-amplification (new anti-flap behavior).** A genuine
first-strike failure de-amplifies to amber instead of immediately
reddening, gated on the max non-infra red fail-count so a *sustained*
red is never softened. Infra-class reds collapse `d6Effective` to null
so they can't masquerade as a product-red badge.
- **`deriveDepth` collapses to a thin adapter** over `buildCellModel` —
dashboard and API render from one ladder, no drift.
- **`GET /api/matrix`** runs `buildCellModel` server-side (with the full
`signal` the browser strips) and returns the true per-cell chip state as
JSON — the dashboard-visual state is now API-derivable without
screenshots.
- **d0-gone-monitor + matrix short-read guards** trust the authoritative
PocketBase total: an inconclusive/truncated read serves
`matrix_unavailable` (never a silent all-gray matrix) and the monitor
HOLDs (fails toward alerting). One bad `featureId` degrades a single
cell instead of crashing the whole surface.
- **Single-source shared catalog flatten** with manifest validation at
parity with the codegen path.
## Verification
Behavior was frozen with a golden-master equivalence baseline before the
change, then proven **live on the local control plane** as an OLD-vs-NEW
differential over identical seeded PocketBase data, on both surfaces:
- **Engine differential (83 scenarios, headless):** 38 AGREEMENT-HELD,
31 FIX-LANDED, **0 regressions, 0 unlisted divergences** — every old≠new
maps to an allowed change. Everything the old engine got right is
byte-identical; everything it got wrong is corrected.
- **Live dashboard + API:** old-dashboard vs new-dashboard over the same
seed. Headline before/after — the incident fan-out: OLD `✗31` (whole
column red) → NEW `~1` (isolated). First-strike de-amp visible as
old-red → new-amber. `GET /api/matrix` returns 940 real cells and
matches the rendered dashboard (api==render).
- **`/api/matrix` deploy-readiness:** the endpoint's runtime deps
(`shared/feature-registry.json`, `SHOWCASE_ROOT`) were not staged in the
harness image — fixed in the harness Dockerfile and proven with a Docker
build→run→curl (dead `matrix_unavailable` → 940 live cells).
- Full CR to convergence (5 rounds, Tier 3); `pre-push-quality` + CI
green (harness 3563 tests, shell-dashboard 1326 tests, both builds).
## Not in this PR (follow-ups)
- Pre-existing d0-gone-monitor hardening (subject-neutral): corrupt
`alert_state` observability, `pb.list`-rejection error-id, the `isDue`
re-post cadence, the `loadRegistryDoc` array guard. Not regressions from
this change.
- **I5 cold-load per-cell signal** — on a cold load, a fresh red/amber
D4 renders gray until the first SSE delta (the bulk fetch strips
`signal`); self-heals in prod. Staged on a separate branch, gated on a
live PocketBase filter smoke-test.
- Stubs now enter the ladder (ceiling 4, achieved 0–2 reflecting real
liveness); chip, isRegression, and all aggregates are unchanged — the
per-cell depth *label* now reflects liveness. Doc-only spec note; no
aggregate impact.
- Stale `d5-mapping-drift` harness test (map moved to the barrel in an
earlier refactor already on main); repoint it.
---
**Draft** — validated on the local control plane; not for merge until
confirmed on staging/prod, which `GET /api/matrix` now makes checkable
without screenshots.
The get_weather generative-UI card never rendered: a CR round-3 "consistency"
tweak added agentId: AGENT_ID to the renderToolCalls entry, which scoped the
renderer such that the incoming (server-side) tool call didn't match and the
card was filtered out. The internal examples/v2/angular/demo renderer sets no
agentId; omitting it here restores rendering.
Verified LIVE against the running app (real Gemini key): the WeatherCard now
renders for 'Get the weather in San Francisco' (location + weather chrome),
matching the React adk reference. Caught by a live React-vs-Angular comparison
that the static CR loop could not.
(Note: setThemeColor NOT calling the agent is inherited — the React adk example
behaves identically; ag_ui_adk doesn't bridge forwarded client tools into the
ADK LLM tool set. Separate upstream concern, tracked as bucket-d follow-up.)
Mounts the framework-agnostic cpk-web-inspector web component (dev aid for
watching AG-UI events / agent state / runtime connectivity), handing it the
shared CopilotKit.core. Mirrors the internal examples/v2/angular/demo pattern —
@copilotkit/angular does not integrate the inspector via the provider the way
React's <CopilotKit inspectorDefaultAnchor> does. Matches the richer React
integration examples (langgraph-python et al.); a deliberate extra vs. the
leaner adk reference.
- add @copilotkit/web-inspector@1.63.1
- src/app/web-inspector.ts + <app-web-inspector /> in app.ts
- README file map updated
ng build green; oxlint 0/0.
- app.ts: setThemeColor comment notes it also recolors the weather card
- server.ts: identifyUser comment notes the id must exist in the platform
- README: document that INTELLIGENCE_API_KEY must accompany COPILOTKIT_LICENSE_TOKEN
Comment/doc only — no behavioral change.
- proverbs.ts: latch the seed one-shot UP FRONT (per agent instance) so a first
snapshot with defined proverbs can't leave the effect subscribed and re-seed on
a later transient undefined; spread existing state in the seed too. Comment now
states the new-thread-hydration race honestly (inherited from React's [agent])
- server.ts: fix the runtime port at 8200 (matches the hardcoded UI runtimeUrl);
don't read env PORT — resolves both the client/server port drift and the
shared-PORT collision with the Python agent
- app.config.ts: pass agentId to the get_weather renderToolCalls entry (parity with
the setThemeColor frontend tool; future-proofs multi-agent)
- app.ts: correct the theme comment (center panel themes via its input, not the var)
Verified: ng build green; oxlint 0/0. Deferred (bucket c/d, inherited from React adk):
Intelligence empty-API-key fail-loud, .sh||.bat + dev:debug + postinstall-uv script
patterns, demo-user stub, dead ProverbsState — aggregated for a cross-example follow-up.
- app.ts: theme via a demo-specific --app-theme-color, not --copilot-kit-primary-color
(the chat re-declares that token on [data-copilotkit] hosts, shadowing the layout
value, so the weather card never themed — round 1's fix was ineffective)
- app.ts: mobile overrides the desktop 100dvh on .center/.chat so the chat row isn't
starved to 0 and clipped (regression introduced by round 1's mobile stack)
- proverbs.ts: seed ONCE per agent instance (mirror React [agent]) instead of on every
state emission — stops transient undefined mid-run/on thread-restore from re-seeding
- proverbs.ts: remove() spreads existing state (setState is a full replace; was wiping
any non-proverbs state)
- server.ts: read RUNTIME_PORT (not the shared PORT the Python agent also reads)
- main-content.ts: drop the redundant/shadowed --copilot-kit-primary-color binding
- weather-card.ts: location required (matches React/backend) + 'Weather' fallback label
- real favicon.ico (was a 12-byte stub); README file map + Node 20.19+/≥22 notes
Verified: ng build green; oxlint 0/0. Refuted in review: no dual @copilotkit/core
(published angular@0.2.0 depends on core/shared@1.63.1 exactly).
headless-simple's "Give me a fun fact." pill rendered the wrong text
("Here's a fun fact: honey never spoils…") and the e2e failed. Root cause:
d4/mastra/chat.json keyed the prebuilt-sidebar agentic-chat fixture on the
bare "fun fact", which is a substring of the headless-simple pill "Give me a
fun fact." Because d4 loads before d6, that fixture shadowed headless-simple's
own d6 fixture ("A fun fact: Honey never spoils!").
Re-key it to the unique "fun fact for the prebuilt sidebar" phrase (mirrors
gold langgraph-python), so it no longer substring-collides. The prebuilt-sidebar
demo pill "Give me a fun fact." now shares headless-simple's d6 fun-fact
fixture, exactly as gold does; prebuilt-sidebar/popup e2e unaffected.
Verified (Node 22 + next start + aimock 1.37.4 replay): headless-simple 4/4,
prebuilt-sidebar 4/4, prebuilt-popup 4/4 — and the full mastra card/chat suite
green (tool-rendering 6/6, tool-rendering-reasoning-chain 5/5, headless-complete
5/5, beautiful-chat 8/8, agentic-chat 4/4). Only a userMessage key changed
(more specific → strictly fewer aimock substring-shadows, well under the
KNOWN_SHADOW_CEILING).
--no-verify: sparse showcase checkout has no monorepo lefthook/commitlint binaries.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- server.ts now loads .env (import dotenv/config) so COPILOTKIT_LICENSE_TOKEN /
INTELLIGENCE_* / AGENT_URL / PORT are honored — Intelligence/threads were
unreachable before (tsx does not auto-load .env like Next.js did)
- .env.example: drop PORT=8200 (the agent's load_dotenv read it and bound 8200,
colliding with the runtime); align AGENT_URL trailing slash
- app.ts: set --copilot-kit-primary-color on the .layout root so setThemeColor
reaches the chat-rendered weather card (was scoped to .main-content only)
- app.ts: mobile stacks center+chat instead of hiding the chat (kept assistant reachable)
- proverbs.ts: re-seed on every fresh thread (drop lifetime latch); mirror React
- agent-state.ts: proverbs?: string[] (code depends on undefined-before-seed)
- package.json: drop unused hono/@hono/node-server/@angular/forms/@angular/router; add dotenv
- .gitignore: !.env.example so the tracked template can't be dropped by the .env* rule
- scripts/run-agent.bat: quote cd path + exit guard (match setup-agent.bat)
- agent/main.py: fix garbled prompt rule 5, stray quote, wether/provers/incude/recipe typos
- README: Intelligence path needs Node >=22 + a real provisioned user (not demo-user)
Verified: ng build green; server.ts binds .env PORT (dotenv loads); oxlint 0/0
headless-complete is marked supported but its WeatherCard / StockCard / ChartCard
stalled in the "running" state. headlessCompleteAgent registered tools via object
shorthand ({ weatherTool, stockPriceTool }), which exposes the JS variable names
instead of the snake_case names the aimock fixtures + useRenderTool renderers emit
(get_weather / get_stock_price / get_revenue_chart) — so the scripted tool calls
were never executable — and get_revenue_chart had no backend tool at all.
- Re-key headlessCompleteAgent to explicit { get_weather, get_stock_price,
get_revenue_chart } (mirrors gold langgraph-python headless_complete.py).
- Add revenueChartTool (id get-revenue-chart) returning gold's fixed payload
{ title: "Quarterly revenue", subtitle, data: [6x {label,value}] }.
- Make weatherTool accept optional scripted temperature/conditions/humidity/
wind_speed (echoed when provided, else the seeded getWeatherImpl) — mirrors
get_stock_price's scripted price_usd. Gold's headless get_weather is a fixed
68 degF / Sunny mock while mastra's is seeded, so the headless weather fixtures
script 68/Sunny to match gold's card; tool-rendering's SF pill keeps its seeded
value. Scripted the winning headless-complete + gen-ui-headless-complete
"What's the weather in Tokyo" legs and aligned the narration to gold. (No gold/
shared backend touched — mastra tool + mastra fixtures only.)
Verified (Node 22 + next build/start + aimock 1.37.4 replay): headless-complete
5/5; tool-rendering 6/6, tool-rendering-reasoning-chain 5/5, beautiful-chat 8/8,
agentic-chat and headless-simple weather unaffected — no regression.
--no-verify: sparse showcase checkout has no monorepo lefthook/commitlint binaries.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tool-rendering cell (marked supported) had 5/6 e2e failing and
tool-rendering-reasoning-chain 2/5, all from mastra aimock fixtures diverging
from the langgraph-python gold standard. Root cause: several fixtures were
keyed on generic substrings where gold uses unique tails, so they
substring-collided with the longer chain pills and — loading earlier
(alphabetical file order) — hijacked them.
Verified on a faithful rig (Node 22 + next build/start + aimock 1.37.4 replay):
- Stock: the pill's scripted $338.37 fixture was shadowed by
headless-complete's ticker-only "price of AAPL" leg (tool's 189.42 default).
Restore gold's unique "price of AAPL right now" key; key the tool-rendering
emit leg on toolName (gold parity).
- d20: the first-roll leg gated on hasToolResult:false never matched once
prior-pill tool results lingered in thread history -> 0 cards. Match on
userMessage only (gold). Add stopWhen: stepCountIs(8) to toolRenderingAgent
so the 5-roll sequence + narration (and the 3-tool chain-tools turn) run to
completion instead of stopping at the default step cap.
- chain-tools: headless-complete's generic "weather in Tokyo" leg hijacked the
"...get the weather in Tokyo..." pill and emitted only get_weather. Restore
gold's "What's the weather in Tokyo" key.
- reasoning-chain flights+weather + sequential: beautiful-chat's generic
"Find flights from SFO to JFK" legs hijacked the "...JFK and show me the
weather there" pill. Restore gold's "for next Tuesday" key.
Result: tool-rendering 6/6, tool-rendering-reasoning-chain 5/5, beautiful-chat
8/8 (no regression) under aimock replay.
Note: headless-complete's own weather/stock/revenue cards remain red on a
separate pre-existing bug (headlessCompleteAgent tool-registration + a missing
get_revenue_chart tool) — addressed in a follow-up commit.
--no-verify: this sparse showcase checkout has no monorepo lefthook/commitlint
binaries (matches prior commits on this branch).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Release angular v0.2.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.2.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.2.0`
- Creates git tag `angular/v0.2.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.
The /eval workflow ran 'showcase eval ... --ci' on a bare runner, but --ci
makes the eval CLI SKIP the Docker lifecycle and assume the showcase fleet is
already running (harness/src/cli/eval/index.ts:348). Nothing in the job starts
it, so every /eval found no healthy container and failed in ~1s without running
a single test (e.g. #5798's /eval d5 mastra: mastra _status=fail, 364ms, zero
tests).
Drop --ci so the CLI builds + starts the in-scope slugs + aimock and
health-checks them before running d5 (the non-ci path). docker compose output
is captured (piped, not inherited) and --json keeps stdout clean, so
eval-results.json stays parseable.
## Problem
The deployed Slack triage bot (`examples/slack`) started failing
**every** turn with:
```
400 Invalid schema for function 'save_diff_comment':
In context=('properties','anchor'), 'propertyNames' is not permitted.
```
No change on our side triggered it — Linear's hosted MCP server changed
the `save_diff_comment` tool schema. Its `anchor` param is now a
free-form map (open object declared with `propertyNames` + open
`additionalProperties`). The bot fetches Linear's tool list at runtime,
so it picked up the new schema automatically.
## Root cause
`@tanstack/ai-openai@0.15.2` (what the bot resolves to) forces `strict:
true` on every function tool. OpenAI's strict function-calling validator
only accepts a subset of JSON Schema and **rejects the entire request
(400, before the model runs)** for a free-form-map object like `anchor`.
One over-rich third-party tool takes down the whole turn.
## Fix — adopt the upstream fix via a dependency upgrade
Already fixed upstream: `@tanstack/openai-base@0.9.8`
([tanstack/ai#933](https://github.com/TanStack/ai/pull/933)) makes the
tool converter detect free-form-map schemas and emit those tools with
`strict: false` (so they stay callable) instead of forcing an invalid
strict schema. First ships in `@tanstack/ai-openai@0.17.0`.
The bot's `^0.15.2` range can't reach it, so this bumps the aligned set
and refreshes `pnpm-lock.yaml`:
| package | before | after |
|---|---|---|
| `@tanstack/ai` | `^0.32.0` | `^0.42.0` |
| `@tanstack/ai-openai` | `^0.15.2` | `^0.17.1` (→ `openai-base@0.9.9`)
|
| `@tanstack/ai-mcp` | `^0.1.3` | `^0.2.5` |
**zod stays at `^3.25.76`.** The repo pins zod to 3.x via a root
`pnpm.overrides` (`zod: ">=3.22.3"`), so the whole workspace resolves
zod 3 regardless. `ai-openai@0.17` peers `zod ^4` (unmet → advisory
warning only), but the strict-schema fix operates on plain JSON Schema,
not zod, so it's unaffected.
**No runtime code change** — the fix lives entirely in the upgraded
adapter (an earlier revision of this PR hand-rolled a schema sanitizer;
that's removed in favor of leaning on TanStack's built-in handling).
## Verification
⚠️ Not verifiable in this worktree (example deps aren't installed here).
Before merge, in an installed env:
- `pnpm --filter slack-example check-types` and `pnpm --filter
slack-example test`
- One live turn hitting Linear (previously-failing `save_diff_comment`
path)
- Sanity-check the bot runs on the workspace's pinned **zod 3** despite
`ai-openai@0.17`'s `zod ^4` peer (the fix path is zod-independent, but
confirm no other `@tanstack/ai` code the bot exercises needs a
zod-4-only API).
🤖 Generated with [Claude Code](https://claude.com/claude-code)