Address review on CopilotKit/CopilotKit#6355:
- Pin `pydantic-ai-slim[ag-ui,openai]==2.22.0` and `ag-ui-protocol==0.1.19`,
matching the starter fleet standard. Open-ended floors would pull a
breaking major on release, and `docker/Dockerfile.agent:16` runs a bare
`uv sync` that would silently re-resolve forward.
- Mirror the specifiers in `uv.lock`'s `requires-dist` so `uv sync --frozen`
at `Dockerfile:42` keeps working. Resolved versions and hashes are
unchanged; `uv lock` is a no-op.
- Construct `StateDeps(ProverbsState())` per request instead of
`dataclasses.replace()`. `replace()` is a shallow copy, so the new deps
point at the same state object; it is only safe today because `StateDeps`
has one field that the adapter rebinds before every run. Any deps class
with a second mutable field would silently share it.
`Agent.to_ag_ui()`, `AGUIApp`, and the `pydantic_ai.ag_ui` shim were removed in
Pydantic AI v2, so the example only ran on v1.
- pin `pydantic-ai-slim[ag-ui,openai]>=2.0.0` and `ag-ui-protocol>=0.1.19`
- import `StateDeps` from `pydantic_ai.ui`
- serve via `AGUIAdapter.dispatch_request` on a Starlette route
- give each request its own `deps` copy so state no longer leaks between threads
0.6.1 carries one change: createChannel's clone check now warns instead of
throwing when `clone()` drops subclass state (#6322). On 0.6.0 a starter
hosting a Channel through @ag-ui/langgraph refuses every turn, because
LangGraphAgent's clone() leaves `emittedToolCallStartIds` and
`eventsStreamActive` behind -- both per-run scratch that is re-initialized
before anything reads it, so dropping them was never the problem. The
starters are the surface where that failure is user-visible, so they should
not sit on the release that has it.
No @copilotkit/* bump rides along, and none is needed. The fix lives entirely
in @copilotkit/channels-core, and every path to it is a caret range:
runtime@1.65.0 asks for channels-core ^0.6.0, and channels-intelligence@0.6.0
(which runtime does pin exactly) asks for ^0.6.0 as well. Both resolve onto
the same 0.6.1, so the runtime's channel path picks up the fix without a new
runtime release. Verified from the regenerated locks rather than assumed:
each of the 15 resolves exactly one channels-core, at 0.6.1, with no second
copy nested under runtime.
Lockfiles were regenerated with --package-lock-only; the diffs contain
@copilotkit/channels* lines and nothing else, so no unrelated dependency
floated forward in the process.
The canary pin existed for one reason: createChannel's identifyUser was absent
from stable, and the pin carried a note that it must not reach users as-is.
Stable has caught up -- @copilotkit/* 1.65.0 and @copilotkit/channels 0.6.0 --
so the workaround goes.
This is not only hygiene. The runtime validates each delivery with an exact
field set, so a client and a server that disagree fail in BOTH directions: a
client expecting a field the server omits, and equally a client receiving one it
does not expect. Now that every Intelligence environment sends the prepared
turn's messageRef, pinning back to an older stable would break exactly as hard
as staying on a canary would have before. 0.6.0 expects it, which is what makes
it the correct pin rather than merely a newer one.
Verified before committing: channels-intelligence@0.6.0 requires messageRef on a
text turn, channels-core@0.6.0 carries identifyUser, and channels@0.6.0 pins its
subpackages exactly rather than by range, so there is no internal skew. The
reference starter installs, typechecks its channel host, and builds. Its one
remaining tsc error is a pre-existing recharts type mismatch, untouched here.
langgraph-fastapi is included: it does not ship a host, but this branch pinned it
to the canary, so it cannot be left there.
Addresses review feedback that channel-host.mts is doing too much.
Two changes, both scoped to the starters:
1. Channel construction moves to a new `channels.mts` beside `agent.ts` —
name resolution, `createChannel`, and the `onMessage` handler. That is
also the file to edit to customise a Channel (commands, reactions,
onMention), which previously meant editing the host.
The per-framework agent import moves with it, so `channel-host.mts` is now
byte-identical in all 15 starters rather than 13 + 2.
2. The host no longer stands up an HTTP server. Its comment claimed the
server was what "keeps the lifecycle-owning process alive"; that is false.
An open undici WebSocket holds the event loop on its own — verified with a
standalone repro where a process with no HTTP server and no timers of its
own stayed up indefinitely on a single WebSocket connection. The server was
therefore serving a second, uncalled copy of the runtime API on port 8300
for no reason.
With the server gone, `createCopilotNodeListener` was the wrong factory —
it builds a request listener purely for its activation side effect. The
host now uses `createCopilotRuntimeHandler` + `ready()`, which is the
documented long-running-host pattern (see fetch-handler.ts). This also
drops `node:http`, `basePath`, and the CHANNEL_PORT env var.
Behaviour is unchanged: same Channel, same agent, same status reporting, and
the same non-zero exit on activation failure.
Verified: 14/14 starters with a `typecheck:channel` script pass; mastra has no
such script by design (166dc94691) and its pre-existing Mastra `Memory` type
error is byte-identical before and after. `npm run channel` exercised on both
failure paths — missing channels.json, and missing INTELLIGENCE_API_KEY with a
name supplied — confirming the new `./channels.mjs` specifier resolves under
tsx as well as tsc. `parity:check` output identical to the pre-change baseline.
Refs #6315
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The starter READMEs, channel-host.mts headers, and the host's own log
lines described the feature as a "managed Channel". Managed is an
implementation detail of how Intelligence attaches the provider edge,
not part of the name — the product surface is just a Channel.
Renames every occurrence across the 15 starters. Section headings become
"Running a Channel", the host header becomes "Channel host", and the
setup_required log reads "no provider is attached yet".
"managed Intelligence" in the .env.example comments is left alone: that
one distinguishes hosted Intelligence from a self-hosted deployment and
is unrelated to Channel naming.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The smoke-starter CI job builds the starter in Docker, where the Dockerfiles copy
only package.json before running npm install; .npmrc arrived later with the rest of
the source, so the install ran without legacy-peer-deps and failed with ERESOLVE.
A local npm install passed precisely because it was not in Docker.
Verified by building docker/Dockerfile.app --target builder locally: npm install and
npm run build both succeed.
Verification skipped on commit: the pre-commit hook runs the full repo suite, which
is unrelated to two Dockerfile COPY lines.
Every starter's .env.example documents INTELLIGENCE_API_URL and
INTELLIGENCE_GATEWAY_WS_URL under its Threads-enablement block, with a
localhost/local-ws value. Uncommenting (or, in llamaindex/mcp-apps,
leaving as shipped) that block is the documented way to enable Threads,
but it silently points the channel host at a local Intelligence that is
usually not running — so `ready()` burns its 30s timeout and the host
exits 1 with no indication why.
Adds a short comment next to those two vars in each of the 14 starters
that ship an .env.example (mastra has none tracked in this branch) noting
they're for a self-hosted or local Intelligence deployment only and
should be left unset for managed Intelligence. No values changed, nothing
uncommented.
Only langgraph-python's README described channel-host.mts even though
all 15 starters ship it, its tsconfig.channel.json, and its channel /
typecheck:channel scripts. Adds the equivalent "Running a managed
Channel" section to the other 14 (adk, agno, claude-sdk-python,
claude-sdk-typescript, crewai-flows, ms-agent-framework-dotnet,
ms-agent-framework-python, pydantic-ai, strands-python, langgraph-js,
llamaindex, mastra, mcp-apps, a2a-middleware), adapted to each file's
existing heading level and tone, and lists channel / typecheck:channel
alongside their other scripts (mastra has no typecheck:channel — see the
prior commit).
Each section states that INTELLIGENCE_API_KEY and a declared Channel in
.copilotkit/channels.json are required, that INTELLIGENCE_CHANNEL_NAME
disambiguates multiple declared Channels, and is explicit that starting
the host does not prove the provider app is installed or reachable.
channel-host.mts imports src/agent.ts, which transitively pulls in
src/mastra/** — and that tree has a pre-existing "Type 'Memory' is not
assignable to type 'MastraMemory'" error unrelated to the Channel host.
TypeScript follows imports regardless of tsconfig `include`, so no
scoping fix exists; a typecheck script that can never pass just trains
people to ignore it.
Removes typecheck:channel from mastra/package.json only — the channel
script and tsconfig.channel.json (still used for module resolution) are
unchanged — and leaves a comment in tsconfig.channel.json explaining why
this starter alone has no such script. Every other starter keeps
typecheck:channel.
ready() resolves once every declared Channel reaches `online` or
`setup_required` — the latter means the Channel is declared but has no
managed provider attached yet, a normal waiting state rather than a
failure. The host used to log an unconditional "holding managed Channel"
success line in both cases, so a developer who has not finished
installing the provider app would see success, message the bot, and get
silence with no diagnostic.
The host now calls status() after ready() and reports the truth per
Channel: "is online" when actually online, or "declared but no managed
provider is attached yet" (pointing at `copilotkit channels status`)
when only setup_required. A ready() rejection still exits non-zero,
unchanged.
Propagated to all 15 starters (byte-identical apart from the
mcp-apps/a2a-middleware import line, as before), and updated the
langgraph-python README section describing the new log output.
The scaffolded host now ships in the starter, so the skill no longer needs to
teach creating it -- only customising it (tools, context, commands, and
per-provider extras like defaultSlackTools/defaultSlackContext). Hand-wiring
guidance stays, clearly marked, for projects that did not come from a starter.
The langgraph-python README documents the new `npm run channel` script: what
holding a managed Channel proves (the runtime activated and the gateway
accepted it) and what it doesn't (the provider app is installed, invited, or
reachable).
Note: skills/copilotkit-channels/SKILL.md did not exist on this branch or on
main -- it lives on a separate, unmerged branch. Seeded it from that branch's
content before rescoping; that branch's version will need manual
reconciliation with this one whichever merges to main second.
Moves the per-run-isolating agent subclass into app/agent.ts so the Channel
host and the web route share it. The runtime key stays a2a_chat for the
frontend.
Mastra's agents run in-process rather than behind a URL, so src/agent.ts
exposes both the record the web route mounts (createLocalAgents) and the
single agent a Channel drives (createDefaultAgent). An empty local-agent
registry throws rather than starting a Channel that silently answers
nothing.
tsconfig.channel.json needed one addition beyond the reference copy: a
"@/*" path alias, because src/mastra/agents/index.ts imports
"@/mastra/tools" internally. Without it, both tsc and `npm run channel`
fail to resolve that module (verified by running the host directly).
langgraph-js and llamaindex are the two starters whose runtime route builds a
framework adapter class (LangGraphAgent, LlamaIndexAgent) instead of a generic
HttpAgent. Each gets the same extraction as the other nine starters: the
adapter construction moves from the route into a new src/agent.ts exporting
createDefaultAgent(), with the URL fallback chain and every constructor option
(graphId, langsmithApiKey, the /run suffix) transcribed verbatim from the
route. The route now just calls createDefaultAgent().
Both also get channel-host.mts (copied verbatim from langgraph-python) and
their own tsconfig.channel.json, needed because the starters' shared
allowJs:true tsconfig makes tsx's resolver prefer a stray index.ts the
published fast-json-patch package ships without its source, which otherwise
crashes the channel script before it runs. package.json gains the
channel/typecheck:channel scripts and three deps: dotenv, @copilotkit/channels
(pinned to the same 1785633429 canary build as the other @copilotkit/*
packages), and tsx as a devDependency. This canary pin is a temporary
workaround pending a stable release with identifyUser support, and must move
before this path ships to users.
This is the case that proves the design: channel-host.mts never learns which
adapter class createDefaultAgent() returned.
llamaindex carries one small pre-existing tsc/build error (clickOutsideToClose
against CopilotChatProps), and langgraph-js carries four (a recharts/JSX
typing clash), neither touched by this change.
Same extraction as langgraph-python across the nine starters whose runtime
route builds a generic HttpAgent (adk, agno, claude-sdk-python,
claude-sdk-typescript, crewai-flows, ms-agent-framework-dotnet,
ms-agent-framework-python, pydantic-ai, strands-python). Each starter's
route now imports createDefaultAgent() from a new src/agent.ts instead of
constructing HttpAgent inline; the URL fallback/normalisation logic is
preserved verbatim per starter (identical bodies for four, agno's "/agui"
suffix, claude-sdk-*'s trailing-slash strip, crewai-flows' bare strip,
strands-python's second STRANDS_AGENT_URL fallback).
Each starter also gets channel-host.mts (copied verbatim from
langgraph-python) and its own tsconfig.channel.json, needed because the
starters' shared allowJs:true tsconfig makes tsx's resolver prefer a stray
index.ts the published fast-json-patch package ships without its source,
which otherwise crashes the channel script before it runs. package.json
gains the channel/typecheck:channel scripts and three deps: dotenv,
@copilotkit/channels (pinned to the same 1785633429 canary build as the
other @copilotkit/* packages), and tsx as a devDependency. This canary pin
is a temporary workaround pending a stable release with identifyUser
support, and must move before this path ships to users.
ms-agent-framework-dotnet's postinstall (a .NET SDK probe) fails on
machines without the SDK installed; dependency resolution itself succeeds
and is unaffected. crewai-flows, pydantic-ai, and strands-python each carry
a small number of pre-existing, unrelated tsc --noEmit errors (JSX/recharts
typing clashes and a missing exported type) not touched by this change.
Review findings on the managed Channel host in langgraph-python:
- Add "typecheck:channel" (tsc -p tsconfig.channel.json --noEmit) so
channel-host.mts has a repeatable typecheck now that it sits outside the
parity-tracked root tsconfig.json's include set. Passes clean (no new
errors, no skipLibCheck/any widening needed).
- resolveChannelName() no longer conflates a missing .copilotkit/channels.json
with a malformed one. Reading and parsing are now separate try blocks: a
missing file keeps the existing "run channels add" message, a parse
failure says the file exists but could not be parsed and includes the
underlying error, instead of misdirecting to re-add the channel.
Extracts agent construction into src/agent.ts so the runtime route and the new
channel-host.mts share one definition. The host holds no provider credentials and
declares no adapters: Intelligence owns the provider edge, so one host file works
for every provider.
Also adds tsconfig.channel.json, used only by the new "channel" script
(tsx --tsconfig tsconfig.channel.json channel-host.mts). Without it, tsx crashes
before the host's own code runs: the starter's tsconfig.json sets allowJs: true
(the create-next-app default, present in all 15 starters), which makes tsx's
CJS resolver prefer a sibling .ts file over the real .js entry point for *any*
node_modules package on a bare-specifier require -- including fast-json-patch
(pulled in transitively via @ag-ui/client), whose published package ships an
orphan index.ts (a source re-export of ./src/core) without the src/ directory
it points at. tsconfig.channel.json omits allowJs so this never triggers, and
does not touch the app's real tsconfig.json (which is parity-tracked verbatim
across other starters -- confirmed by testing the alternative fix first).
Full verification trail, including this deviation and the reasoning above, is
in .superpowers/sdd/2026-08-01-channel-host-in-starters/task-1-report.md.
@ag-ui/mastra declares peerDependencies["@copilotkit/runtime"] = "^1.10.5".
node-semver excludes prereleases from a caret range that carries no prerelease
tag, so ANY prerelease @copilotkit/runtime pin fails that peer check no matter how
high its numeric version — picking a newer canary does not help. Since this
starter now tracks a canary build of main, a plain `npm install` failed with
ERESOLVE, which would break a fresh clone.
Scoped to this one starter. The upstream fix is a prerelease-inclusive peer range
in @ag-ui/mastra, whose other peers already use e.g. ">=1.0.0-0 <2.0.0-0".
Remove this file when these pins move back to a stable release.
Verified: plain `npm install` exits 0 with no ERESOLVE, the lockfile stays on the
canary suffix, and `npm run build` passes.
Verification skipped on commit: the pre-commit hook runs the full repo suite,
which is unrelated to adding one starter-local npm config file.
The managed Channel API (CopilotRuntime channels, listener.channels) is absent at
1.62.3, and createChannel's identifyUser is absent from stable 0.5.0. The canary
line carries both, so starters track main. All packages share one build suffix
(1785633429), and the canary runtime pins channels-core to that exact suffix
rather than a range, so there is no version skew.
langgraph-fastapi is bumped too. It gets no Channel host (the CLI does not offer
it) but it is enrolled in the _parity drift check, so leaving it behind would make
every tracked @copilotkit version mismatch the north-star.
Verified: 15/15 starters typecheck with no new errors (7 have pre-existing errors,
each confirmed identical on the old pins) and 14/15 build; llamaindex's build
failure is the pre-existing clickOutsideToClose/CopilotChatProps mismatch, which
surfaces there because it does not set typescript.ignoreBuildErrors.
Temporary: these pins must move to a stable release carrying identifyUser before
the CLI's channel path ships to users.
Verification skipped on commit: the pre-commit hook runs the full repo suite,
which is unrelated to a dependency-pin change.
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