CopilotKitIntelligence resolves apiUrl/wsUrl to the managed hosts when they are
omitted, and its own docstring says leaving both unset is always correct against
the managed service. Every starter's runtime route supplied
`?? "http://localhost:4201"` instead, so a managed reader who copied the block
got a runtime aimed at a local stack that is not running -- the failure the
starter's own .env.example warns about two files away.
Replace the fallbacks with the conditional spread these same starters already use
in channel-host.mts, so a self-hosted override still works and the managed
default applies when it is absent. Three .env.example files also set the values
uncommented, two of them directly under a comment telling the reader to leave
them unset; comment those out to match the other nineteen starters.
Guard both shapes in validate-intelligence-env-names.ts, which already polices
the canonical Intelligence key name and hosts and runs unfiltered on every PR.
The rule is the pattern rather than the literal, so a staging host substituted
for localhost fails the same way. Local e2e harnesses and demo stacks that
genuinely target a local deployment are allowlisted with their reasons.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
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).
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).
- 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