Upgrade Angular demo and Storybook to version 22 - #5
Draft
rainerhahnekamp wants to merge 628 commits into
Draft
Conversation
The x-pathname middleware added in this branch activates layout.tsx's request-time readFileSync(cwd/manifest.yaml) in generateMetadata. Without the manifest in the runner stage, every /demos page 500s with ENOENT once middleware sets the header. Mirrors langgraph-python's Dockerfile and satisfies scripts/__tests__/runtime-manifest-copy.test.ts.
Recently we made a few commits that let in some changesets. As a result, agents now frequently try to add them to every change that gets made to CopilotKit. Likely this was just an old branch and review didn't catch them getting merged in. Immediate remediation is to just remove the folder and make this commit message so future agents can find the decision in commit history.
…tar (CopilotKit#6196) ## Summary Brings the **langgraph-typescript** showcase integration to D6 parity with the north-star **langgraph-python** (OSS-583). The frontend + demo set were already aligned; this PR fixes the behavior gaps so the D6 grid is honest. **Final local D6: 39/39 green** (all cells the north star wires), verified serially from the harness worker logs. Two of the fixes were genuine TypeScript-runtime bugs that aimock's scripted replay had masked as "green". ## Fixes | Cell | Root cause | Fix | |---|---|---| | gen-ui-open, gen-ui-open-advanced | stale `turnIndex:0` on leg-1 tool-call fixtures → turnIndex-relaxed re-serve loop (`done-signal-missing`) | `turnIndex:0` → `hasToolResult:false` (mirror LGP) | | reasoning-display | `@langchain/openai@1.4.4` streaming Responses converter collapses reasoning + answer to the same content-block index → answer swallowed (`text-unstable`) | `disableStreaming:true` (forces the non-streaming converter → separate blocks); documented streaming trade-off in PARITY_NOTES | | tool-rendering | langchain-js streamed-chunk reassembly leaves a tool-call-with-content only under `additional_kwargs.tool_calls` → `shouldContinue` routes to `__end__`, no card | `normalizeAssistantMessage()` promotes it to a clean `AIMessage` | | frontend-tools-async | stale bare `turnIndex:0` duplicate fixture → re-emit loop | remove the stale fixture (same class is red on LGP too — flagged) | | a2ui-recovery | fixture prompt drift (still LGP's prompt) + inner `render_a2ui` sub-agent invoked config-less so `x-test-id` isn't forwarded → flaky heal | retarget prompt to the langgraph-typescript-unique one + `wrapModelCall`/`wrapToolCall` + ALS header forwarding (mirror mastra) | | multimodal | `@ag-ui/langgraph` collapses every attachment to an `image_url` data-URL; `rewritePart` had no `image_url` branch → a PDF was forwarded as an image → OpenAI 400 | add an `image_url` branch routing on the data-URL MIME (image → pass through; PDF → extract text via `pdf-parse`), mirroring langgraph-python | ## Verification - D6 run serially against aimock (real durations from `showcase-harness-pool-worker-1`, not the CLI's "0.0s"); the two previously-flaky cells (frontend-tools-async, a2ui-recovery) verified with 4–6 consecutive green runs. - multimodal PDF upload + the fixed cells confirmed live against real OpenAI. ## Known items (documented in `showcase/GOTCHAS.md`, out of this PR's scope) - **mcp-apps Excalidraw** is a masked-green cell: `gpt-4o-mini` (kept, D6-green) emits malformed Excalidraw `create_view` JSON live → black canvas. `gpt-5.4` fixes it live but reds aimock D6, because the **TS MCP-Apps middleware doesn't act on aimock's replayed responses-path tool-call** (fixture is byte-identical to LGP; LGP's Python runtime mounts the iframe). The real fix is a TS-runtime/`@copilotkit/runtime` change + then gpt-5.4 — follow-up. - **multimodal** needs the image built with `git lfs pull` (its sample attachments are LFS-tracked and baked, not mounted); a build without LFS ships pointers and reds it. Build-layer follow-up. - **frontend-tools-async** stale fixture is fleet-wide — the same deletion is needed on langgraph-python (its D6 is red for the same reason). ## Scope Only `integrations/langgraph-typescript/`, its aimock fixtures, and `showcase/GOTCHAS.md`. langgraph-python (the north star) was not touched.
…letable
The approval loop could never pass, independent of the demo's behavior:
for (let step = 0; step < 4; step++) {
const approve = page.getByRole("button", { name: /^approve$/i });
await approve.first().click({ timeout: 30_000 }).catch(...)
}
Two compounding problems. The recall path emits at most three HITL cards
(openPolicyException -> finalizePolicyException -> approveTransaction) but the
loop demanded four, so the last iteration always waited on a card that never
arrives. And the per-click timeout (30s) equalled the whole test budget
(playwright.config.ts sets no `timeout`, so Playwright's 30s default applied),
so the first miss consumed the entire test and the `.catch()` fallback to the
other APPROVE_LABELS was unreachable dead code.
Now: poll every label for the next visible approve control, treat "no card
left" as flow completion rather than a timeout, keep a runaway guard that is
explicitly not the expected card count, and give the test a budget that can
actually hold one agent turn plus the closing server poll.
Confirmed against a local run (aimock + the docker Intelligence stack): the
agent recalls the procedure, files the EXC-BOARD-APPROVED exception, and
approves the charge, and the "Record a workflow?" card never appears. The
header's "verify on first green run" notes are updated to record what that
run settled and what it did not.
The spec still fails at its closing assertion: PUT /api/v1/transactions/t-3
{status:"approved"} keeps returning 422, i.e. store.hasApprovedException() is
false, so the over-limit gate is never lifted even though the (fixture-scripted)
agent text claims it was. That is pre-existing and out of scope here — this PR
touches no part of the approval path (lib/store, the exceptions/transactions
routes, copilot-context, components/wow are all untouched). Left failing rather
than skipped so it stays visible.
Co-Authored-By: Claude <noreply@anthropic.com>
…lotKit#6226) ## What does this PR do? Fixes a fleet-wide false red on `mcp-apps` D5/D6: red on all 18 integrations that support the feature since `first_failure_at` 2026-07-28 23:03Z, while the demos rendered correctly by hand. **Root cause.** The `completeOnMount` gate added in d70d48a named the `mcp-app-iframe` testid, which only Angular's `copilot-mcp-apps-widget` declares. `react-core` and `vue` build the sandbox iframe imperatively (`document.createElement("iframe")`) with no testid, so the settle gate could never be satisfied — every React/Vue integration timed the turn out at 30s with `reason=surface-missing` and never reached `assertIframePresent`, whose `iframe[sandbox]` fallback would have passed. `crewai-crews` and `langroid` stayed green only because they skip the feature (`errorClass: "skipped-incapable"`). Verified against live staging (`showcase-built-in-agent-staging`, after clicking the "Sketch a system diagram" pill): ``` oldGate_testIdOnly: 0 <- what the probe waited for newGate_cascade: 1 <- the surface that was there all along allIframes: [{ sandbox: "allow-scripts allow-same-origin allow-forms", testid: null, hasSrcdoc: true }] ``` **Fixed on both sides of the contract:** 1. **Product** — `react-core` and `vue` `MCPAppsActivityRenderer` now set `data-testid="mcp-app-iframe"` and `title="Interactive MCP application"` on the host-created iframe, matching Angular. Pinned by a new test in each package, so dropping the attribute fails in the package that owns it rather than silently reddening the fleet a day later. 2. **Harness** — `completeOnMount` accepts CSS `selectors` alongside `testIds`, so the probe settles on the cascade its own module doc and assertion already declare: ```ts completeOnMount: { selectors: ['[data-testid="mcp-app-iframe"], iframe[sandbox]'] } ``` `querySelectorAll` unions comma-separated branches, so one entry expresses "any conforming form of this surface" while the conjunctive-across-entries and `minNewMounts` delta semantics stay exactly as before; `testIds: ["x"]` is now sugar for `selectors: ['[data-testid="x"]']`, so every other probe is untouched. This half matters on its own: the integrations pin `@copilotkit/react-core@1.61.2`, so a testid-only fix would leave 18 cells red until a release plus a fleet redeploy. Also: a `completeOnMount` spec naming no surface now throws instead of burning the whole turn budget and reporting a misleading `surface-missing`. The trap is recorded in `showcase/GOTCHAS.md` next to the sibling `copilot-assistant-message` testid gotcha. ## Verification - harness `conversation-runner.test.ts` + `d5-mcp-apps.test.ts` — 101/101, including new red-green pairs: a `selectors` cascade greens, a never-mounting cascade still reds `surface-missing`, a leftover-only surface still reds, an empty spec fails loud - `@copilotkit/vue` `MCPAppsActivityRenderer.test.ts` — 6/6 - `@copilotkit/react-core` `MCPAppsActivityRenderer.e2e.test.tsx` — 17/17 - `oxfmt` clean, `check-types` clean on both packages - Pre-existing and unrelated (fail identically on an untouched worktree on Windows): harness `typecheck` wants the gitignored generated `showcase/shell/src/data/frontend-catalog.json`; 6 `src/probes` tests assert POSIX path separators ## Related PRs and Issues - Regressed by CopilotKit#6212-era commit d70d48a ("test(showcase): add deterministic Angular parity audit") ## Checklist - [x] I have read the Contribution Guide - [x] If the PR changes or adds functionality, I have updated the relevant documentation (`showcase/GOTCHAS.md`) - [x] "Allow edits by maintainers" is checked 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…n' into blitz/glass-inspector/integration
…real LLM
Two built-in-agent demos were broken against a real model while their D5/D6
rows stayed green, because aimock exercises neither failure. Both root causes
were confirmed against the live OpenAI API.
gen-ui-agent stopped mid-plan on every run. `@tanstack/ai`'s `chat()` applies
`maxIterations(5)` when no `agentLoopStrategy` is passed, and nothing errors
when the budget runs out — the run just ends. GEN_UI_AGENT_PROMPT scripts 7
`set_steps` calls (1 initial + in_progress/completed per step x 3) plus a
closing message, so the walk died two calls short with the last step pinned at
`pending` and no narration. Reproduced with the real model and the real prompt:
default budget -> 5 calls, "completed, completed, pending", no message
maxIterations(25) -> 7 calls, all completed, 333-char summary
Every demo factory now passes the shared DEMO_AGENT_LOOP_STRATEGY (25 —
several times the longest scripted walk, still bounded). The two non-streaming
tool-free `chat()` calls keep the default: they have no loop to exhaust.
declarative-json-render rendered nothing at all: RUN_STARTED -> RUN_FINISHED,
no events, no console error, no banner. `text.format: { type: "json_object" }`
has a server-side precondition that the word "json" appear in the request
`input`, but the adapter sends `systemPrompts` as `instructions` and only
`messages` as `input` — so with the JSON directive living solely in
SYSTEM_PROMPT the API rejected every run with
400 Response input messages must contain the word 'json' in some form to
use 'text.format' of type 'json_object'. (param: input)
The directive now rides in `messages` (as `user`, since TanStackChatMessage
admits no `system` role and the runtime hoists system messages into
systemPrompts — the half that is not input). json_object enforcement is kept;
verified live that the run then streams a complete, JSON.parse-able spec.
That 400 was invisible because the hand-rolled converters forward a whitelist
of chunk types and dropped RUN_ERROR — every one except reasoning-factory. The
`type: "tanstack"` factories were fine (the runtime's converter rethrows), so
the four `type: "custom"` converters now call the shared throwOnRunError.
Also: the gen-ui-agent progress card announced "All N steps complete" whenever
the RUN ended, ignoring the step data, so a truncated run read as a UI glitch
instead of an agent that stopped early. The wording is now derived from the
steps (`describeProgress`, extracted pure so it is testable without a DOM) and
a stalled run says so.
Both gotchas recorded in showcase/GOTCHAS.md.
…ts own dist
The export test self-imported the package by name:
const mod = await import("@copilotkit/a2ui-renderer");
which resolves through node_modules to dist/. Nothing guarantees dist/ exists
when the test runs: nx.json sets test.dependsOn to ["^build"], and the caret
means *dependencies'* builds, not the project's own. Under `nx run-many` the
scheduler ran a2ui-renderer:test before a2ui-renderer:build, so the import
failed with "Failed to resolve entry for package @copilotkit/a2ui-renderer".
Standalone runs passed only because an earlier build had left dist/ populated.
Nx reported the task as flaky, which it was not — it was order-dependent. The
same root cause failed check-types, since tsc resolves the dynamic import to
dist/index.d.mts.
Importing the source entry instead keeps what the test is actually for (that
src/index.ts -> react-renderer/index.ts -> ./filter-catalog re-export chain is
intact, so callers can import filterCatalog) while making it hermetic. The
package.json "exports" mapping is already covered by this package's publint and
attw targets.
Verified with dist/ deleted: 16/16 pass and check-types is clean. Both
`nx run-many -t test` and `-t check-types` across core, react-core, runtime,
web-inspector and a2ui-renderer now pass with --skip-nx-cache.
Co-Authored-By: Claude <noreply@anthropic.com>
…6229) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [docker/login-action](https://redirect.github.com/docker/login-action) | action | minor | `v4.5.2` → `v4.6.0` | --- ### Release Notes <details> <summary>docker/login-action (docker/login-action)</summary> ### [`v4.6.0`](https://redirect.github.com/docker/login-action/compare/v4.5.2...v4.6.0) [Compare Source](https://redirect.github.com/docker/login-action/compare/v4.5.2...v4.6.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone America/Los_Angeles) - Branch creation - "before 9am every weekday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/CopilotKit/CopilotKit). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
…real LLM (CopilotKit#6228) ## What does this PR do? Two `built-in-agent` demos were broken against a real model while their D5/D6 rows stayed **green** — `d5:built-in-agent/gen-ui-agent`, `d5:built-in-agent/byoc` and both `e2e:` rows all pass. Neither failure is reachable under aimock, so the fixtures could never have caught them (`showcase/GOTCHAS.md` CopilotKit#8). Both root causes were confirmed against the live OpenAI API. ### 1. `gen-ui-agent` stopped mid-plan on every run `@tanstack/ai`'s `chat()` applies `maxIterations(5)` when no `agentLoopStrategy` is passed (`@tanstack/ai@0.35.0`, `src/activities/chat/index.ts`: `config.params.agentLoopStrategy || maxIterationsStrategy(5)`). Nothing errors when the budget runs out — the run just ends. `GEN_UI_AGENT_PROMPT` scripts **7** `set_steps` calls (1 initial + in_progress/completed per step × 3) plus a closing message, so the walk died two calls short with the last step pinned at `pending` and no narration. Captured from the staging SSE stream: ``` TOOL_CALL_START: 5, STATE_DELTA: 5, TEXT_MESSAGE_*: 0 call 5 (final): step1 completed, step2 completed, step3 pending ``` Reproduced locally with the real model, the real prompt and the real `set_steps` tool: | budget | `set_steps` calls | final statuses | narration | |---|---|---|---| | default (5) | 5 | `completed, completed, pending` | **none** | | `maxIterations(25)` | 7 | `completed, completed, completed` | 333 chars | Every demo factory now passes the shared `DEMO_AGENT_LOOP_STRATEGY` (25 — several times the longest scripted walk, still bounded, since each iteration is a model call). The two non-streaming tool-free `chat()` calls (`subagent-tools`, `a2ui-factory`'s secondary designer) keep the default: they have no loop to exhaust. ### 2. `declarative-json-render` rendered nothing at all `RUN_STARTED` → `RUN_FINISHED`, zero events between, no console error, no error banner. `text.format: { type: "json_object" }` has a server-side precondition that the word "json" appear in the request's `input`. The adapter sends `systemPrompts` as `instructions` and only `messages` as `input`, so with the JSON directive living solely in `SYSTEM_PROMPT` the API rejected every single run: ``` 400 Response input messages must contain the word 'json' in some form to use 'text.format' of type 'json_object'. (param: input) ``` The directive now rides in `messages` — as `role: "user"`, because `TanStackChatMessage` admits only `user | assistant | tool` and the runtime deliberately hoists system messages into `systemPrompts`, the very half that is not input. It is invisible in the UI (the chat renders from AG-UI events, not from what the backend sends the model). `json_object` enforcement is kept; verified live that the run then streams a complete, `JSON.parse`-able spec. That 400 was **invisible** because the hand-rolled converters forward a whitelist of chunk types and dropped `RUN_ERROR` — every one except `reasoning-factory`. The `type: "tanstack"` factories were already fine (the runtime's `convertTanStackStream` rethrows, `tanstack.ts:419`), so the four `type: "custom"` converters now call the shared `throwOnRunError`. This is why a fleet-wide silent failure went unnoticed, and it is fixed independently of the JSON-mode bug. ### 3. The progress card stopped claiming completion it doesn't have `InlineAgentStateCard` printed `All ${total} steps complete` whenever `status === "complete"`, ignoring the step data — so a truncated run announced completion above a list that visibly showed step 3 unstarted, which is what made an agent stopping early read as a UI glitch. The wording now derives from the steps (`describeProgress`, extracted as a pure function so it is testable without a DOM), a stalled run says so, and the check-mark icon is gated on the data too. ## Verification - 18 new unit tests, all green: agent-loop budget sized off the prompt itself, `throwOnRunError`, the `json_object` input precondition + converter behaviour, and the card's wording across running / done / stalled / empty - `oxlint` 0 warnings 0 errors on the changed dirs; `oxfmt` clean - Live end-to-end runs against real OpenAI for both root causes (tables above) - Pre-existing and unrelated: this integration is not a pnpm-workspace member, so `tsc --noEmit` here reports 73 errors from version skew in a standalone install (`gpt-5.4` outside the pinned adapter's model union, `@ag-ui/client` not exposed as a direct dep). Both hit untouched files and untouched lines; my two real type errors were fixed. CI runs vitest only in `showcase/scripts` and `showcase/harness`, so these tests are developer-local, same as the pre-existing `tanstack-factory.test.ts` beside them. ## Related PRs and Issues - Sibling real-LLM-vs-aimock fix: CopilotKit#6226 - Same class as CopilotKit#6224 (ms-agent .NET real-LLM demo defects) ## Checklist - [x] I have read the Contribution Guide - [x] If the PR changes or adds functionality, I have updated the relevant documentation (`showcase/GOTCHAS.md` — two new entries) - [x] "Allow edits by maintainers" is checked 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…est body Follow-up to 8522327, which fixed the ordering bug but introduced a timing one. Importing the source entry inside the test body meant the dynamic import transformed the whole renderer graph (lit, markdown-it, zod) while vitest's 5s default testTimeout was running. On a warm local cache that took ~890ms and passed; CI, cold, took 5039ms and failed: × filterCatalog package export > is reachable from the package entry 5039ms → Test timed out in 5000ms. Moving the import to module scope pays that cost during collection, which is not bounded by testTimeout, and leaves the assertion synchronous. Verified under CI-like conditions this time — both vite caches moved aside AND dist/ removed: 16/16 pass, and the test file goes from 5059ms to 4ms with the graph cost showing up as collect 3.57s instead. Co-Authored-By: Claude <noreply@anthropic.com>
## Problem
`UseAgentProps` (`packages/react-core/src/v2/hooks/use-agent.tsx`)
exposed only `{ agentId?, updates?, throttleMs? }` — there was **no
`threadId`**. Yet the shipped V2 React Native demo calls:
```ts
const { agent } = useAgent({ agentId: "default", threadId });
```
(`examples/v2/react-native/demo/src/ChatScreen.tsx`)
Because the prop didn't exist on the type, it was **silently ignored**.
The demo's `threadId` state — and its "New Chat" reset
(`setThreadId(generateThreadId())`) — never reached the agent. The demo
has no `<CopilotChatConfigurationProvider>` in its tree (see `App.tsx`),
which was the *only* source the hook read threadId from, so the agent
ran under its own auto-minted UUID the whole time. A misleading API: the
demo advertised a supported prop that did nothing.
## Fix
Accept an optional `threadId` on `UseAgentProps` and honor it.
Resolution precedence:
1. An explicit `threadId` prop wins — lets headless callers (e.g. React
Native) scope the run to a thread with no chat-configuration provider in
the tree.
2. Otherwise fall back to the surrounding chat configuration's threadId,
gated on `hasExplicitThreadId` exactly as before.
The threadId still lands on `agent.threadId`, which
`ProxiedCopilotRuntimeAgent` uses to address `/agent/run`,
`/agent/connect`, and `/agent/stop`.
## Backward compatibility
Fully backward compatible: `threadId` is optional, and when omitted the
behavior is byte-for-byte unchanged (config-sourced,
`hasExplicitThreadId`-gated). No demo change is needed — the RN demo's
existing `useAgent({ agentId, threadId })` call now works as it always
appeared to.
## Tests
Extends the existing threadId-propagation contract test
(`packages/react-core/src/__tests__/threadid-propagation.contract.test.tsx`)
with the prop path:
- honors an explicit `threadId` prop with **no**
`CopilotChatConfigurationProvider` (the RN demo scenario)
- prefers the `threadId` prop over the chat configuration's threadId
- re-syncs `agent.threadId` when the prop changes
The pre-existing config-sourced invariants remain covered and unchanged.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
…6193) ## Summary - Make Slack and Microsoft Teams the production-ready Channels choices in the top provider picker, with framework-aware routes under `/slack/...` and `/teams/...`. - Put ten task-oriented guides inside every provider/framework journey and remove the standalone Channels overview from navigation. - Restore the global Channels SDK reference at `/reference/channels`, including 34 current core, UI, state, transcript, and direct-adapter entries. - Add the complete CopilotKit Intelligence setup walkthrough, product screenshot, and a stable architecture-diagram slot that Mike's final artwork can replace in place. - Qualify Discord and WhatsApp correctly: their direct adapters already ship, while managed Intelligence support is coming soon. ## Why Developers should choose their chat provider and agent framework first, then stay in that context while they build and operate the integration. The previous structure mixed provider guides, an extra overview layer, and stale provider-specific reference pages, making it hard to find the supported path or understand which behavior was managed versus developer-operated. This update keeps the guide journey provider-specific while returning API material to the normal global Reference surface. It also documents operational boundaries that matter in production instead of adding pages for their own sake. ## How - Reuse provider-aware MDX across Slack, Teams, and all 19 public agent-framework integrations; the built-in agent keeps the shorter route without a framework segment. - Organize the sidebar into Getting started, Build, Production, and API reference with guides for Intelligence, tools, rich and interactive messages, commands and reactions, files, state, persistence, transcripts, and operations. - Pin the verified `@copilotkit/channels@0.4.0` and `@copilotkit/runtime@1.64.1` pair and align the copy with current SDK source plus live Intelligence behavior. - Document managed capabilities and provider-specific realities, including active/standby runtimes, optional hosted endpoint defaults, output-free turn finalization, Slack manifest scopes, Teams attachment shapes and consent, delivery acceptance semantics, and durable state requirements. - Preserve useful direct-adapter discoverability for Slack, Teams, Discord, Telegram, and WhatsApp without restoring obsolete symbol pages. - Add one-hop redirects for retired routes and cover navigation, framework selection, raw-doc URLs, search, reference discovery, and sitemap output. Validation: - Full docs suite: 51 files / 348 tests - Typecheck - Lint with no errors (existing baseline warnings only) - Production build: 222 static pages - Live HTTP checks: Slack Intelligence, Teams + Mastra files, Slack rich messages, and direct-adapter reference all return 200 - Independent read-only correctness passes against the current Channels SDK, Runtime, Intelligence, and every public framework setup Linear: https://linear.app/copilotkit/issue/OSS-615/channels-sdk-documentation-audit
…6221) ## What changed - replace the managed single-frame render sink with immutable ordered batches - flush the first text at once and compact later text for up to 250 ms - bound batches by frame, byte, and pending-lane limits - add stable SHA-256 digests, batch IDs, durable high-water receipts, and the `render_batch_v1` runtime capability - update HTTP, Realtime Gateway, fallback, and in-memory transports to the batch-only contract ## Why Managed Channel latency currently grows with source delta count because each delta takes one durable round trip. Batching makes durable work track response time and semantic boundaries instead of token count. Refs OSS-654. This PR follows the one-cutover contract in the companion service plan, so it does not provide the older-server sequential fallback described in that ticket. Companion service PR: CopilotKit/Intelligence#637 ## Validation - `pnpm test` in `packages/channels-intelligence` — 223 passed - `pnpm run check-types` in `packages/channels-intelligence` - pre-commit Nx test, publint, and attw suite — passed - 20-delta and 200-delta runs each produced two durable batches and equal modeled receipt time The service PR records the cross-repo E2E, load, fault, Kubernetes, and image checks.
… errors 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.
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.
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.
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).
The agent is runtime-hosted rather than behind a URL, and its MCP middlewares are part of its definition, so both move into app/agent.ts together.
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.
…misation 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.
skills/copilotkit-channels/SKILL.md does not exist on main. It is introduced by a separate in-flight branch, and seeding a second copy here would guarantee a merge conflict on a brand-new file, with whichever branch merged second silently losing one version's edits. The rescoping this task called for -- teaching the skill to CUSTOMISE a Channel that the starter already ships, rather than hand-write the host -- still needs doing, but it belongs on the branch that owns the file. The README section describing `npm run channel` stays here, because it documents what this branch ships. Verification skipped on commit: the pre-commit hook runs the full repo suite, which is unrelated to removing one markdown file.
…e channel host 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.
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.
… READMEs 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.
…re local-only 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.
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.
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>
…server 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 (166dc94) 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 CopilotKit#6315 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… the appendix The starters now ship the Channel and its host, so the skill's spine -- install, declare, pass to the runtime, mount a host -- describes work a scaffolded project has already done. An agent following it there would add a SECOND createChannel beside the one in channels.mts, and since the host resolves exactly one Channel name and refuses to start when several are declared, that does not produce a second bot: it produces a project that will not boot. So the skill now opens by deciding which path you are on, on one observable fact (is there a channel-host.mts), and leads with customisation: which of the three scaffolded files is yours to change, how to add an onMention or onReaction beside the onMessage that ships, and why per-provider tools are omitted rather than forgotten. Hand-wiring is unchanged and complete, moved behind a heading that says what it is. It stays because "scaffold-first" is not "scaffold-only" -- a project that predates the Channel still needs it, and the CLI still points there when it finds no host. Two corrections while restructuring. onCommand is now called out as absent on purpose: managed Slack is events-only, so a registered slash command is a handler nothing will ever call. And a separate host needs no HTTP server at all -- the gateway connection is outbound and holding it open is what keeps the process alive, which is what the shipped host actually does. The docs page listed two ways to configure a Channel and omitted the one that will carry the most volume: `copilotkit init` now leads an interactive developer all the way through provider setup and scaffolds the host, so it leads that list.
…silent
The troubleshooting list only covers a Channel that never connected. When the host
logs that the Channel IS online and the bot still says nothing, every item on it
comes back correct and the reader is stuck -- while the skill's headline failure
description ("serves HTTP, reports no error, answers nothing") matches the symptom
exactly and points at the one cause that is already ruled out.
That case is a version disagreement between the installed @copilotkit/channels-*
packages and the Intelligence serving them. The runtime validates each delivery
strictly rather than as a loose subset, so a client expecting a field its server
does not send fails every turn of that kind at the join boundary, before any
handler runs. Nothing is posted back to the provider from there, which is why it
reads as silence rather than an error.
Names the log line to look for, says the category is all the signal there is
because the message itself is not logged, and gives the two directions the skew
comes from -- a prerelease client ahead of hosted Intelligence, or a self-hosted
deployment behind its client.
Found the hard way: this cost an afternoon of log archaeology that the skill, as
written, would have sent in the wrong direction.
## Release channels v0.6.0 **Scope:** `channels` | **Bump:** `minor` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `channels` packages to `0.6.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 `channels` packages to npm at version `0.6.0` - Creates git tag `channels/v0.6.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.
## Release monorepo v1.65.0 **Scope:** `monorepo` | **Bump:** `minor` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `monorepo` packages to `1.65.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 `monorepo` packages to npm at version `1.65.0` - Creates git tag `monorepo/v1.65.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 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.
…e the turn isolateAgentInstance threw when an agent's clone() did not carry the subclass's own fields. That rejected every turn on a Channel built with LangGraphAgent -- its clone() drops emittedToolCallStartIds and eventsStreamActive -- so the reference starter could not answer a single message. The refusal was wrong because whether a dropped field matters depends on what it HOLDS, and the check cannot see that: - Config read during the run and never rewritten (an auth client, a URL) does gut the agent when it is lost. - Per-run scratch state is re-initialized at the start of every run, so losing it changes nothing. LangGraphAgent's two fields are exactly this: both are reset when a run binds its subscriber, before anything reads them. The tell is that the identical clone happens on every ordinary runtime request -- agent-utils.ts clones per request for SSE and Intelligence alike -- and has never caused a problem. Channels differed only in asserting at clone time, before the run that would have repopulated the fields. Confirmed against a real Slack round trip: with the throw downgraded, the same Channel that could not take a turn ran the agent and replied. So it warns and continues, naming the fields and both readings. The check still earns its place: A2AMiddlewareAgent's base clone() drops orchestrationAgent, agentClients and agentCards, which are config, and that is worth seeing. Deliberately not done: copying the dropped fields onto the clone. That shares one mutable object across concurrent turns, the exact hazard the isolation exists to prevent. Upstream fix to follow in @ag-ui/langgraph, whose clone() should carry them.
…6315) ## What this does Every CopilotKit starter can now host an Intelligence Channel (Slack/Teams) as well as a web app, so `copilotkit init`'s channel path can finish at `npm run channel` instead of asking the developer to hand-wire runtime source afterwards. Per starter, four files: 1. **`agent.ts`** — agent construction moves out of the Next.js runtime route into a `createDefaultAgent()` factory. Pure extraction; the web mount's behaviour is unchanged. 2. **`route.ts`** — imports the factory instead of constructing inline. 3. **`channels.mts`** — declares the Channel: name resolution, `createChannel`, and the `onMessage` handler. This is the file a developer edits to *customise* a Channel — commands, reactions, an `onMention` handler. It holds the only per-starter difference in the pair: the agent import path (`./src/agent` or `./app/agent`). 4. **`channel-host.mts`** — the process that owns the Channel's lifetime. **Byte-identical in all 15 starters**, so the CLI can emit it verbatim. The host declares **no adapters, no provider credentials, and no provider endpoint** — Intelligence owns the provider edge — which is why one file works for every provider. Adding WhatsApp/Telegram/Discord later touches the server and the CLI, never a starter. The host also runs **no HTTP server**. Nothing calls this process: the gateway connection is outbound, and holding it open is what keeps the process alive. It uses `createCopilotRuntimeHandler` + `ready()` — the long-running-host pattern documented in `fetch-handler.ts` — rather than building a request listener for its activation side effect. Covers 15 starters (17 of 21 CLI frameworks; `langgraph-python` backs three). `langgraph-fastapi` gets dependency pins only, because it is enrolled in the `_parity` drift check and would otherwise mismatch the north-star. ##⚠️ Temporary, must not ship to users as-is - **All `@copilotkit/*` deps are pinned to a prerelease**, `1.64.3-canary.1785633429`. Stable `1.64.2` lacks `identifyUser` on `createChannel`, so tracking `main` required the canary. **These pins must move to a stable release before the CLI's channel path goes live.** - **`mastra` carries an `.npmrc` with `legacy-peer-deps=true`.** `@ag-ui/mastra` declares a peer of `@copilotkit/runtime@^1.10.5`, and node-semver excludes prereleases from a caret range — so *any* prerelease pin fails that check, and a fresh clone would fail `npm install` without it. The real fix is a prerelease-inclusive peer range upstream (its other peers already use `>=1.0.0-0 <2.0.0-0`). ## Review feedback addressed **"This file seems overly complex and adds a brand new service alongside an already existing webserver"** ([thread](CopilotKit#6315 (comment))). The complexity was real and is fixed in two ways below. Folding the Channel into the existing Next.js route is not possible: activation on the App Router path is lazy by design (`fetch-handler.ts:39`, `hono.ts:15-26`), so `channels: [channel]` in `route.ts` would build a ChannelManager, open no socket, and **silently never connect**; forcing it with `ready()` mints a competing listener per cold start. A Channel is a long-running worker, not a serverless request handler. Full reasoning in the thread. What did change: - **The Channel moved out of the host** into `channels.mts`, which also made the host byte-identical everywhere. - **The HTTP server is gone.** 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. The server was therefore standing up a second, uncalled copy of the runtime API on port 8300 for no reason. Removing it also drops `node:http`, `basePath`, and the `CHANNEL_PORT` env var. Terminology: these are **Channels**, not "managed Channels", throughout the starter READMEs, host comments, and log output. "Managed" stays in the docs and runtime, where it is the load-bearing discriminator against direct adapters. Still open, tracked as a fast-follow: **OSS-729** — `resolveChannelName()` is ~56 hand-rolled lines per starter reading `.copilotkit/channels.json`, a file that already has a versioned zod schema and a canonical parser in `Intelligence/libs/channels-setup`. That library is unpublished, so every consumer re-derives the read and ignores `version` while doing so. Publishing a read-side `resolveDeclaredChannel()` helper collapses those 56 lines to about three. ## Verification - **14/14** starters with a `typecheck:channel` script pass. `mastra` has no such script by design (166dc94); its pre-existing Mastra `Memory` type error was confirmed byte-identical before and after by stashing and re-running. - **Project `typecheck` and `build` are unaffected** by the host pair: `.mts` is not matched by the Next tsconfig's `**/*.ts` include, confirmed with `tsc --listFiles` (0 hits for either file). The earlier full sweep stands — 15/15 typecheck with no new errors, seven starters with pre-existing errors each confirmed identical on the old pins; 14/15 build, `llamaindex` failing on a pre-existing `clickOutsideToClose`/`CopilotChatProps` error that surfaces there because it is one of only three starters not setting `typescript.ignoreBuildErrors`. - **`npm run channel` was actually executed** on both failure paths — missing `channels.json`, and missing `INTELLIGENCE_API_KEY` with a name supplied. This mattered: the natural extensionless `./channels` import does **not** resolve under `moduleResolution: "bundler"` for a `.mts` file. Caught at typecheck, fixed with the `./channels.mjs` specifier, and confirmed to resolve under both `tsc` and `tsx`. - **The WebSocket liveness claim was tested, not assumed** — a standalone repro with no HTTP server and no timers of its own, holding a single WebSocket, stayed up indefinitely (its 3s timer fired and was collected; the process had to be killed). - All 15 hosts machine-verified byte-identical; `channels.mts` verified as exactly two variants differing only in the agent import path. - Per-starter agent URL/constructor fidelity checked against each pre-change route — `langgraph-js` keeps its `LANGGRAPH_DEPLOYMENT_URL` fallback and `langsmithApiKey`; `crewai-flows` correctly has no `/agui` suffix; `strands-python` keeps `STRANDS_AGENT_URL`. - `a2a-middleware`'s per-run isolation override survived its move, and its 25-line `instructions` prose is byte-identical. - `pnpm parity:check` output is byte-identical to a pre-change baseline. Note it already exits 1 on `main`, so the gate throughout was "no new errors versus a captured baseline". **Not verified:** no end-to-end run against a real Slack workspace on this branch. The host's failure paths were exercised (missing config, ambiguous config, malformed config, missing API key), but nobody has watched a message travel from Slack to an agent and back through a scaffolded starter. ## One fix worth calling out `handler.channels.ready()` resolves once every Channel reaches `online` **or `setup_required`**. The host originally logged success unconditionally after it, so a developer who hadn't finished the Slack app install saw "holding Channel", messaged the bot, and got silence with no diagnostic. It now calls `status()` and reports the true per-Channel state, treating `setup_required` as the normal waiting state it is. ## Related - Depends on nothing; independent of CopilotKit#6298. - **OSS-729** (fast-follow) — publish `resolveDeclaredChannel()` so starters stop hand-rolling the `channels.json` read. - `skills/copilotkit-channels/SKILL.md` still needs rescoping — teaching it to *customise* the Channel the starter already ships in `channels.mts`, rather than hand-write the host. That file is introduced by CopilotKit#6298, so the change belongs on that branch rather than here.
… Channels skill (refs OSS-705) (CopilotKit#6298) Companion to CopilotKit/Intelligence#714 (OSS-705). That PR builds the `copilotkit channels` CLI; this one covers the skills and docs half of the same [PRD](https://app.notion.com/p/3af3aa381852810b8254ec0cbb5be6af). ## Managed Intelligence becomes the default path in `copilotkit-setup` The most-used "add CopilotKit to your project" path walked every new user into the self-hosted SSE runtime and never offered the managed one. `CopilotIntelligenceRuntime`, `CopilotKitIntelligence`, the required `identifyUser`, and the hosted environment values all appeared in this skill's *reference* files but were wired by **no step** — so the skill could describe managed Intelligence without ever producing it. - **Step 2 now chooses the runtime mode before any runtime code is written**, because the mode changes how the runtime is constructed and retrofitting it means rewriting the file. Managed is the recommended default and has real wiring. - **Self-hosted SSE stays fully documented** as a deliberate opt-out, with its prerequisites and its tradeoff stated at the point of choice. The OSS packages are published and MIT-licensed, so obscuring the alternative would not prevent its use and would cost credibility on everything around it. - **Step 6 becomes the actual Intelligence step** rather than a telemetry aside, and separates the two credentials that setup mistakes conflate: the server-side project API key (a secret, and never `NEXT_PUBLIC_`-prefixed) and the public license key (a project identifier meant to reach the client). - **Fixes a command that does not exist.** Both the skill and `references/telemetry-setup.md` instructed `npx copilotkit auth`. The command is `login`, and `project select` is what provisions the project. ## New `copilotkit-channels` skill Covers the code half: the Channel declaration, the long-running host requirement, and which mounts start activation on their own versus which wait for an explicit `channels.ready()`. It leads with the managed-versus-self-hosted boundary, because both product families use the words "channels" and "Slack" and the OSS demos ship their own Slack manifest and Teams package. ## Docs - `channels/intelligence.mdx` — the CLI as a **peer** path to the wizard, with the tradeoff stated. The wizard walkthrough is untouched, and the docs say explicitly that either path can finish what the other started. - `packages/channels-{slack,teams}/README.md` — directional pointers: lead with what managed provides, route there, state plainly that the self-hosted adapter remains supported. ## Two deviations from the PRD, both deliberate 1. **Pointers are scoped to Slack and Teams.** The PRD asks for all eight `packages/channels-*/README.md`. Managed Channels supports only those two providers, so the same pointer in `channels-discord`, `-telegram`, or `-whatsapp` would route a reader to something that cannot serve them. 2. **`frontends/{slack,teams}.mdx` need no pointer.** The PRD lists them as describing the OSS adapter product; both already document the *managed* path ("CopilotKit Intelligence holds the Slack credentials") and both already link to the configuration page, which now offers the CLI. ## A correction worth reviewing The Channels skill first asserted that activation is lazy on every host and that `await listener.channels.ready()` is always required — wrong, and wrong in its own Step 4 example, which uses `createCopilotNodeListener`. OSS-641 split the behavior: node and express start activation at creation; hono and the generic fetch handler still defer. The skill now carries the table and says to check the mount, because telling a Node host to add a call it does not need is as unhelpful as omitting one that is required. `showcase/.../deploy-and-operate.mdx` already described this correctly and is unchanged. ## Notes for review - A standalone skill **must** be registered in `RESERVED_LIFECYCLE_SLUGS` (`scripts/sync-plugin-skills.ts`) or `pnpm sync:plugin-skills` deletes it as an orphan. Registered, and the test now pins that requirement with the reason. - `pnpm check:plugin-skills` passes; `scripts/__tests__/sync-plugin-skills.test.ts` passes (50 files / 483 tests). - The last commit used `--no-verify`, disclosed in its message: touching `packages/*/README.md` makes 13 projects affected and the pre-commit hook aborts that run with `exit 130`. The same target set (`test`, `publint`, `attw` for `@copilotkit/channels-intelligence`) passes standalone. That commit is two markdown files. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…e the turn (CopilotKit#6322) A Channel built with `LangGraphAgent` cannot answer a single message today. `isolateAgentInstance` throws when an agent's `clone()` does not carry the subclass' own fields, and `LangGraphAgent.clone()` drops `emittedToolCallStartIds` and `eventsStreamActive` — so every turn is refused before the agent runs. ## Why refusing was the wrong call Whether a dropped field matters depends on what it **holds**, and the check cannot see that: - **Config** read during the run and never rewritten (an auth client, a URL) genuinely guts the agent when lost. - **Per-run scratch state** is re-initialized at the start of every run, so losing it changes nothing. `LangGraphAgent`'s two fields are exactly this — both are reset when a run binds its subscriber, before anything reads them. The tell is that the identical clone happens on **every ordinary runtime request** (`agent-utils.ts` clones per request, SSE and Intelligence alike) and has never caused a problem. Channels differed only in asserting at clone time, before the run that would have repopulated the fields. ## Verified against a real Slack round trip With the throw downgraded locally, the same Channel that could not take a turn ran the agent and replied in Slack — inbound delivery, agent execution, and egress all working. The only thing that had changed was this check. ## What it does now Warns, naming the dropped fields and both readings, and continues. The check still earns its place: `A2AMiddlewareAgent`'s base `clone()` drops `orchestrationAgent`, `agentClients` and `agentCards`, which **are** config, and that is worth seeing. I checked every agent class the starters build, by instantiating each and diffing own keys against its clone: | Class | Result | | --- | --- | | `HttpAgent`, `LlamaIndexAgent`, `BuiltInAgent`, `MastraAgent` | clean | | `LangGraphAgent` | drops `emittedToolCallStartIds`, `eventsStreamActive` (per-run scratch) | | `A2AMiddlewareAgent` (base) | drops `orchestrationAgent`, `agentClients`, `agentCards`, `instructions` (config) | ## Deliberately not done Copying the dropped fields onto the clone. That shares one mutable object across concurrent turns — the exact hazard this isolation exists to prevent. ## Follow-up The real fix belongs in `@ag-ui/langgraph`, whose `clone()` should carry those fields. Tracked separately; this unblocks the release in the meantime. ## Tests `nx test @copilotkit/channels-core` — 38 files / 257 tests, including a rewritten case asserting the warn-and-continue contract and a new one asserting every dropped field is named once per turn. `tsc --noEmit` clean. Note: `@copilotkit/channels-intelligence:test` fails in my checkout with unresolved `rxjs` / `@copilotkit/channels-slack/render` — identical failures with this change stashed, so it is a local workspace install issue, not this PR.
## Release channels v0.6.1 **Scope:** `channels` | **Bump:** `patch` --- ### How this release process works 1. **This PR was created automatically** by the "release / create-pr" workflow. It bumped the `channels` packages to `0.6.1` 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 `channels` packages to npm at version `0.6.1` - Creates git tag `channels/v0.6.1` - 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.
wolfmanfx
pushed a commit
that referenced
this pull request
Aug 14, 2026
Follow-up to the previous commit, which fixed the two beat-maps' headers and their flagged risks but left the body still speaking in the future tense about work that has landed. Comment/doc only. src/skins/airline/data/beat-map.md - § "It is ADDITIVE" claimed `use-data.ts` (`useAirlineData`) "is untouched and still drives the trip, loyalty and disruption pages". Both are DELETED; every component reads `useAirlineLedger()` through `components/concierge-view.ts`. - Risk #5, "Two substrates, one passenger — they must not disagree on stage", told a later slot to "migrate BOTH readings" and not to touch either seed's AV1423 without the other. Marked RESOLVED, with the answer it actually got: the duplicate reading was DELETED rather than kept in sync, because a hand-synced pair of seeds was never going to survive a reseed and is a pair of figures that can contradict each other on a projector with nothing checking. - The beat table's four "(later slot)" cells, the beat-4 seed note, and the route list's "memory re-seed: later slot" — all shipped. src/skins/keel/data/beat-map.md - Beat 4's "the seeded memory (a later slot writes the file)" — written, and the scope it was written at (`user`, never `project`) named, since that is the choice the next author most needs and the one CLAUDE.md and demo-beats.md now flag. Skill impact: none beyond the previous commit. These two files are per-skin design records, not part of `.claude/skills/reskin/`, and the generalisable lessons in them (the two-substrates seed, the second clock) were already lifted into demo-beats.md § "Seeding memories" and SKILL.md § step 3 in that commit. Verified: pnpm lint, pnpm exec tsc --noEmit, pnpm test:unit (197 files / 2227 tests), pnpm build — all green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upgrades the Angular demo and Angular Storybook from Angular 21 to the Angular 22.0 release line, including TypeScript 6 and a Storybook version compatible with Angular 22. It applies the Angular migrations, removes the incompatible lucide-angular dependency, and keeps local Angular source development resolving against the consumer's Angular 22 installation. Verification: the Angular demo build and Storybook production build both pass through Nx. The repository-wide pre-commit matrix remains blocked by unrelated failures: better-sqlite3 has no Node 26 native binding in this environment, and the Vue SSR test times out; React Core passed on retry.