Module-graph hygiene, not a behaviour fix -- the preceding eventsource patch is
what fixes the bun failure.
The SDK's SSE transport was imported at the top of the agent module but is only
constructed inside the `type === "sse"` branch ~1300 lines below, so
`eventsource` was pulled into the module graph of every non-SSE path, including
every test that merely touches the agent module. Move it to an `await import()`
at the point of use.
`transport` and `mcpClient` gain explicit annotations so they keep real types
instead of the bare `let` declarations they had before.
eventsource maps its `bun` export condition to the ESM build, and Bun resolves
`bun` before `require`. So a CJS require("eventsource") under Bun receives an
async ESM module and throws "require() async module ... is unsupported". The
package ships a real CJS build behind `require`, but Bun never reaches it.
Two CJS consumers in our graph hit this: the MCP SDK's own dist/cjs/client/sse.js,
and @ag-ui/mcp-apps-middleware, which requires that SDK path unconditionally at
module load. It surfaced as an intermittent failure of the runtime bun
integration job -- intermittent because it is a load-order race, where the run
only passes if the ESM graph happens to evaluate eventsource first.
Dropping the `bun` key makes Bun fall through to `import` for ESM consumers
(same file as before) and `require` for CJS consumers (the CJS build they need).
Takes the bun integration test from 5/20 to 20/20 locally. A version bump is not
an alternative: eventsource 4.1.0 still ships the same mapping.
`bun-version: latest` let a Bun release change module-resolution behaviour
between runs. 1.3.14 is the version the recent passing and failing runs both
resolved, so pin it and make the job reproducible.
## Problem
Channel agents lose inbound files whenever the current-trigger
transcript omits attachments. The currently deployed Intelligence path
always omits those files because normalized_payload never contains their
handles.
## Why
The delivery adapter seeds the current inbound turn from the transcript,
and core then deduplicates the explicit prepared input. Files present
only on the prepared delivery therefore never reach either the
agent-history consumer or channel.getMessages during version skew.
## Fix
Restore a missing current-trigger transcript file list from the prepared
delivery inside ClaimedChannelDelivery.getTranscript(), where the result
is shared and memoized for both consumers. Existing transcript files are
preserved, so the Intelligence fix and this fallback cannot duplicate
attachments.
Either PR independently repairs the agent path. Coverage proves both an
omitted transcript and an already-correct transcript hydrate the image
for getMessages and agent seeding.
## Summary
- add `defineChannelComponent` so an agent can call a server-rendered
JSX component as a typed tool
- add native JSX namespaces for Slack Block Kit and Teams Adaptive Cards
- use one provider codec for both direct adapters and
Intelligence-managed delivery
- recover interactive handlers by stable JSX key after a process restart
- generate and audit the native component catalog against the provider
catalogs
This keeps native provider UI in the existing Channels render and
delivery path. It does not add a second renderer, transport, or action
system.
## Review order
1. **Component tool contract:**
`packages/channels-core/src/channel-component.ts`, `create-channel.ts`,
and `thread.ts`
- Standard Schema validates agent arguments before render.
- Render receives the source platform and run `AbortSignal`.
- The rendered UI posts as a separate provider message; the tool returns
a short acknowledgement.
2. **Native IR:** `packages/channels-ui/src/native.ts` and `render.ts`
- Native nodes carry a provider tag.
- Traversal follows named slots such as Slack `accessory` and Teams
`actions`, not only `children`.
3. **Slack:** `packages/channels-slack/src/native*.ts`, `render.ts`, and
`interaction.ts`
- `Slack.Block`, `Slack.Element`, and `Slack.Object` map to Block Kit
field names.
- Direct and managed Slack share the same codec and fallback-text rules.
4. **Teams:** `packages/channels-teams/src/native*.ts`,
`render/index.ts`, and `interaction.ts`
- `Teams.AdaptiveCard` is the explicit root.
- The serializer computes the minimum Adaptive Card version from every
type and property used.
5. **Recovery and managed parity:**
`packages/channels-core/src/action-*.ts` and
`packages/channels-intelligence/src/delivery-adapter.ts`
- Stable JSX keys, the source platform, and the action value are stored
in the action snapshot.
- CopilotKit/Intelligence#729 asserts the final Slack Web API and Bot
Framework request bodies.
## Data flow
```text
agent tool call
-> Standard Schema validation
-> async JSX render
-> portable or provider-native Channel IR
-> Slack or Teams codec
-> direct adapter or Intelligence-managed delivery
-> provider API
```
```text
provider interaction
-> provider callback decoder
-> hot ActionRegistry lookup
-> ActionStore snapshot fallback
-> component re-render
-> stable keyed handler
```
## Public API
An agent-rendered component uses the same JSX vocabulary as
`thread.post`:
```tsx
const Approval = defineChannelComponent({
name: "show_approval",
description: "Post an approval request.",
parameters: z.object({ title: z.string() }),
render: ({ title }, { platform }) => (
<Card title={`${title} (${platform})`}>
<Button key="approve" value="approve" onClick={approve}>
Approve
</Button>
</Card>
),
});
createChannel({
name: "approvals",
components: [Approval],
});
```
Use native JSX only when the portable vocabulary does not expose a
provider feature:
```tsx
await thread.post(
<Slack.Block.Section
text={<Slack.Object.MarkdownText text="*Deploy ready*" />}
accessory={
<Slack.Element.Button
key="approve"
text={<Slack.Object.PlainText text="Approve" />}
value={{ decision: "approve" }}
onClick={({ action }) => approve(action.value)}
/>
}
/>,
);
```
```tsx
await thread.post(
<Teams.AdaptiveCard fallbackText="Deploy approval">
<Teams.TextBlock text="Deploy ready" wrap />
<Teams.ActionSet>
<Teams.Action.Submit
key="approve"
title="Approve"
value={{ decision: "approve" }}
onSubmit={({ action }) => approve(action.value)}
/>
</Teams.ActionSet>
</Teams.AdaptiveCard>,
);
```
## Guardrails
- Native nodes from one provider fail if rendered for another provider.
- Slack rejects missing required fields, invalid top-level nodes, and
more than 50 blocks.
- Teams rejects invalid roots and explicit versions below the minimum
required version.
- Interactive nodes in agent-rendered components require stable, unique
JSX keys.
- `Slack.Raw` and `Teams.Raw` accept reviewed provider JSON but do not
bind callbacks.
The generated catalog is in `packages/channels/native-catalogs.md`. The
package READMEs contain the full Slack, Teams, and component-tool usage
notes.
## Test map
| Contract | Main coverage |
| --- | --- |
| component tool schema, render context, post, and acknowledgement |
`packages/channels-core/src/channel-component.test.ts` |
| native IR, provider tags, and named-slot traversal |
`packages/channels-ui/src/native.test.tsx` |
| Slack catalog, serialization, validation, and callbacks |
`packages/channels-slack/src/native-*.test.*` |
| Teams catalog, versioning, serialization, and callbacks |
`packages/channels-teams/src/native-*.test.*` |
| keyed cold recovery and reaction recovery |
`packages/channels-core/src/*recovery.test.*` |
| managed codec parity |
`packages/channels-intelligence/src/delivery-provider-elements.test.ts`
and CopilotKit/Intelligence#729 |
## Validation
- `pnpm nx run-many -t test,check-types,build
--projects=@copilotkit/channels-ui,@copilotkit/channels-core,@copilotkit/channels-slack,@copilotkit/channels-teams,@copilotkit/channels-intelligence,@copilotkit/channels`
- `pnpm verify:channels-umbrella`
- `pnpm check:channel-native-catalogs`
- `pnpm audit:channel-native-catalogs`
- pre-commit tests, publint, and API Extractor checks for 27 affected
projects
- all PR checks pass on `b88f9b7e4f247939d65603d96df26090baad916a`
## Summary
Document the draft-first Microsoft Teams setup flow across Channels
docs, skills, and the Teams adapter README.
## Why
Intelligence now offers a resumable Fast CLI path and a Guided manual
path while keeping custom branding artifacts local and separating
provider completion from runtime health.
## How
- Describe the fully scoped provisioning and resume contract.
- Replace Azure Bot and manifest-editing guidance with Teams Developer
Portal plus Entra.
- Teach both setup skills the local-only artifact and Team-installation
boundaries.
- Update documentation contract tests for the new path.
Ports the setup-slack-channel skill from CopilotKit/channels-sdk into the
skill set that `copilotkit skills install` distributes, so the Slack setup
flow is reachable from the CLI rather than only from a channels-sdk checkout.
The skill is copied unchanged. It stays browser-first and makes no reference
to the `copilotkit channels` commands: per Ben Taylor on 2026-08-03, those
defer to the web app until they have more real-world usage.
Both frontmatter descriptions are narrowed so the two skills stop matching the
same request. copilotkit-channels now owns the code half — declaring, wiring,
and customising a Channel — and assumes the provider app exists.
setup-slack-channel owns the provider half for a first-time Slack setup. Each
names the other's territory and hands off.
Refs CopilotKit/channels-sdk#10
## What
Documents a Slack setup failure that passes every check we have, in the
two places that would have prevented it.
- `skills/copilotkit-channels` gets a new section under the existing
silent-failure guidance, and its verify checklist now says the app must
be **reinstalled**, not installed.
- `examples/slack/README.md` had the wrong button label and the wrong
order: *"OAuth & Permissions → **Install to Workspace** → copy the
`xoxb-` bot token."*
No code changes.
## Why
Measured against a real workspace: creating a Slack app from a manifest
**installs it**, and that install grants exactly two scopes —
`channels:history` and `chat:write`. The manifest's declared scopes
reach the app's configuration but not the grant, which is what Slack's
yellow *"you've changed the permission scopes"* banner is reporting. One
**Reinstall to Workspace → Allow** raises the grant to the full declared
set.
A bot token copied before that reinstall is the trap, because nothing
catches it:
- `auth.test` succeeds, so the credential stores and the adapter reports
healthy.
- `chat:write` is present, so the bot can post — it does not look
broken.
- `app_mentions:read` is absent, so Slack never delivers `app_mention`,
and no handler ever runs.
The result is a Channel that is genuinely online and structurally deaf.
The channels skill already documented an "online but silent" failure
whose cause is a version disagreement between the installed
`@copilotkit/channels-*` packages and the Intelligence deployment. That
one is identifiable by a rejected-delivery log line. This one logs
**nothing at all**, because Slack never sends anything to reject — so it
needed its own section rather than a footnote on that one, and the
distinguishing signal (a log line vs. no log line) is stated explicitly.
`examples/slack` is the self-hosted adapter path rather than managed
Channels, so the skill deliberately does not cover it — but the Slack
behaviour is identical, and that manifest declares more scopes than the
managed one (`users:read.email`, `team:read`, `chat:write.public`, plus
a user token), so the gap there is larger.
## Notes for review
- The two-scope measurement was taken against the managed Intelligence
manifest. The mechanism is Slack's create-from-manifest flow, which is
the same flow `examples/slack` uses, so I expect the same grant there —
but the README wording says "only a couple of the scopes the manifest
declares" rather than naming a count, since I have not measured that
manifest specifically.
- Intelligence now refuses a short-scoped token when it is pasted
(`CHANNEL_ADAPTER_SLACK_TOKEN_SCOPES_INCOMPLETE`), naming the absent
scopes. The skill says to read that error as this problem caught early,
and notes that a Channel attached before that check existed can still be
sitting in this state — only a rotation clears it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Slack installs an app when it creates one from a manifest, and that install
grants two scopes: channels:history and chat:write. The manifest's declared
scopes reach the app's configuration but not the grant, which is what Slack's
"you've changed the permission scopes" banner reports. One Reinstall to
Workspace raises the grant to the full set. Measured against a real workspace.
A token copied before that reinstall passes every check we have. auth.test
succeeds, so attaching stores it and reports the adapter healthy. chat:write is
present, so the bot can post. app_mentions:read is absent, so Slack never
delivers app_mention and no handler ever runs — an online, structurally deaf
Channel.
The channels skill already documents an "online but silent" failure caused by a
version disagreement, which logs a rejected delivery. This one logs nothing at
all, because Slack never sends anything to reject, so it gets its own section
next to it and the verify checklist now says "reinstalled" rather than
"installed". Intelligence refuses a short token at paste time now, so the
section also says to read that error as this problem caught early.
examples/slack said "Install to Workspace → copy the xoxb- bot token", which is
both the wrong button label and the wrong order. Its manifest declares even more
scopes than the managed one, so the gap there is larger.
## Summary
- add disabled, branded coming-soon entries for Discord, WhatsApp,
Telegram, and SMS in the Channels picker
- add a reusable Channels activation strip to the Shell Docs landing
page with verified channel/backend guide routing, accessible selection
controls, prompt copying, OpenTag guidance, and analytics
- replace the global course banner with a Channels announcement linking
to `/channels`
- add focused coverage for route resolution, selection behavior,
clipboard feedback, keyboard navigation, telemetry, and banner content
## Validation
- `npm exec -- oxfmt --check src/components/banners.tsx
src/components/__tests__/banners.test.tsx`
- `npm run lint` (no errors; existing warnings only)
- `npm run typecheck`
- `npm test -- --maxWorkers=2` — 54 files, 362 tests passed
- `npm run build`
- verified the activation flow and `/channels` destination locally at
desktop and mobile widths
## What
Bumps `@copilotkit/channels` from `0.6.0` to `0.6.1` in all 15
integration starters that host a Channel, with lockfiles regenerated to
match. Nothing else changes — `@copilotkit/*` stays on `1.65.0`.
`examples/slack` and `examples/teams` are untouched: they consume the
workspace copy (`workspace:*`), so they already track the fix.
## Why
`channels@0.6.1` carries exactly one change —
[#6322](https://github.com/CopilotKit/CopilotKit/pull/6322), where
`createChannel`'s clone check warns instead of throwing when `clone()`
drops subclass state. On `0.6.0`, a starter hosting a Channel through
`@ag-ui/langgraph` **refuses every turn**, because
`LangGraphAgent.clone()` leaves `emittedToolCallStartIds` and
`eventsStreamActive` behind — both per-run scratch that is
re-initialized before anything reads it. The starters are where that
failure is user-visible, so they shouldn't sit on the release that has
it.
## No runtime release needed
The question came up whether `@copilotkit/runtime` has to be re-released
so the pins don't conflict. It does not. The fix lives entirely in
`@copilotkit/channels-core`, and every path to it is a caret range:
| consumer | asks for | resolves to |
| --- | --- | --- |
| `@copilotkit/channels@0.6.1` (umbrella) | `channels-core` exactly
`0.6.1` | 0.6.1 |
| `@copilotkit/runtime@1.65.0` | `channels-core: ^0.6.0` | 0.6.1 |
| `@copilotkit/channels-intelligence@0.6.0` (runtime's one exact
channels pin) | `channels-core: ^0.6.0` | 0.6.1 |
The umbrella does not depend on `channels-intelligence`, so nothing
pulls a second copy of it in alongside the runtime. Everything dedupes
onto one `channels-core@0.6.1`, which is what makes the runtime's own
channel path pick up the fix without a release of its own.
## Testing
**1. Every regenerated lock resolves exactly one `channels-core`, at
0.6.1** — verified rather than reasoned, since a second nested copy is
the failure mode that would have forced a runtime release:
```
a2a-middleware: node_modules/@copilotkit/channels-core@0.6.1
adk: node_modules/@copilotkit/channels-core@0.6.1
claude-sdk-python: node_modules/@copilotkit/channels-core@0.6.1
agno: node_modules/@copilotkit/channels-core@0.6.1
claude-sdk-typescript: node_modules/@copilotkit/channels-core@0.6.1
langgraph-python: node_modules/@copilotkit/channels-core@0.6.1
crewai-flows: node_modules/@copilotkit/channels-core@0.6.1
langgraph-js: node_modules/@copilotkit/channels-core@0.6.1
mastra: node_modules/@copilotkit/channels-core@0.6.1
llamaindex: node_modules/@copilotkit/channels-core@0.6.1
ms-agent-framework-dotnet: node_modules/@copilotkit/channels-core@0.6.1
pydantic-ai: node_modules/@copilotkit/channels-core@0.6.1
mcp-apps: node_modules/@copilotkit/channels-core@0.6.1
ms-agent-framework-python: node_modules/@copilotkit/channels-core@0.6.1
strands-python: node_modules/@copilotkit/channels-core@0.6.1
```
**2. The full `@copilotkit/*` resolution in the reference starter
(`langgraph-python`)** — `channels-intelligence` correctly stays at
`0.6.0` (runtime's exact pin) while consuming the 0.6.1 core:
```
1.65.0 @copilotkit/a2ui-renderer 0.6.1 @copilotkit/channels
0.6.1 @copilotkit/channels-core 0.6.1 @copilotkit/channels-discord
0.6.0 @copilotkit/channels-intelligence
0.6.1 @copilotkit/channels-slack 0.6.1 @copilotkit/channels-teams
0.6.1 @copilotkit/channels-telegram 0.6.1 @copilotkit/channels-ui
0.6.1 @copilotkit/channels-whatsapp 1.65.0 @copilotkit/core
1.65.0 @copilotkit/react-core 1.65.0 @copilotkit/runtime
1.65.0 @copilotkit/shared 1.65.0 @copilotkit/web-components
```
**3. Reference starter installs and typechecks its channel host** — real
`npm install`, not lock-only:
```
> copilotkit-langgraph-template@0.1.0 typecheck:channel
> tsc -p tsconfig.channel.json --noEmit
EXIT=0
installed core: 0.6.1 | intelligence: 0.6.0
```
**4. The published 0.6.1 tarball actually carries the fix** — checked
the installed dist, not just the version number, so a mis-built release
would not pass as fixed:
```
node_modules/@copilotkit/channels-core/dist/create-channel.js
74: warnOnCloneDroppedOwnFields(prototype, cloned);
132: function warnOnCloneDroppedOwnFields(prototype, cloned) {
139: console.warn(`createChannel: ${name}.clone() dropped ${dropped.join(", ")}. ` +
```
`console.warn`, not `throw` — the 0.6.0 behaviour is gone.
**5. No unrelated dependency drifted.** Filtering every
non-`@copilotkit/channels*` line out of a regenerated lock diff leaves
nothing:
```
$ git diff examples/integrations/adk/package-lock.json | grep -E "^[+-]" \
| grep -vE "integrity|resolved|@copilotkit/channels" | grep -E '"[a-z]' | sort | uniq -c
8 + "version": "0.6.1",
8 - "version": "0.6.0",
```
**Not tested:** no starter was run end-to-end against a live Channel
here — the change is a version pin, and the behaviour it unblocks was
verified in #6322.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.
## 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.
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.
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)
## 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](https://github.com/CopilotKit/CopilotKit/pull/6315#discussion_r3699832425)).
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 (166dc94691); 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 #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 #6298, so the change belongs on that branch rather than here.
ACTIVITY_SNAPSHOT replace updates keep the same message id while mutating
object content. The messages memo fingerprint used contentKey=0 for any
non-string/non-array content, so generative-UI and progress activity
renderers stayed on the first frame until some other message list change
forced a refresh.
Serialize object content in the fingerprint (multimodal attachments remain
on the array length branch) and add a regression test for same-id replaces.
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.
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.
## 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 agent (grounding rule first and most emphatic, temperature 0 for
deterministic routing, two server tools), the OGUI design brief, the
suggestion pills, tools.tsx, and skin.tsx -- registered in both
registries under the id 'keel'. defaultSkinId is unchanged.
tools.tsx is the join point where corpus, run engine, and chat components
meet. Two details are load-bearing: agent-context readables memoize on
summaryKey (never on runs) so the 900ms ticker cannot thrash the agent's
context, and showSources takes only (docId, sectionId) pairs and resolves
the ref/heading/snippet from the real corpus client-side -- so a citation
the model invents fails to resolve and is dropped rather than rendering
as a convincing fake.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
The five in-chat cards plus the a2ui catalog, ops-report builder, canvas
surface, and OGUI sandbox functions.
Every interactive card carries pointer-events-auto: CopilotKit paints
useComponent renders with pointer-events:none, so without it the cards
render perfectly and are completely unclickable.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
App-shell chrome with the persona switcher (which gates what is
approvable), the RuntimeProviders/useRuntimeProperties/identifyUser triad
for per-persona memory scoping, and the six pages. Two routes are
parameterized -- knowledge/<docId> and runs/<runId> -- so resolvePage
destructures the segment array rather than using a flat lookup. An
unknown id renders a not-found body, never a 404: the route is
structurally valid and a citation deep-link must not break on a rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
Four playbooks whose every step carries a policyRef into the corpus, four
seeded runs, and a pure reducer driving them: steps advance on a 900ms
ticker and halt at approval gates keyed to the current persona's role.
seed.test.ts asserts all 23 policyRefs resolve to a real doc + section.
That invariant is what fuses the knowledge and process substrates -- a
dangling ref means an approval card cites a document that does not exist.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
Nine Harbor Point Health policy documents across three spaces, and a
pure lexical scorer over them (heading 3 / title 2 / body 1, stopword +
synonym expansion, total-order tie-break). Server-safe: imported by both
the agent's search tool and the client Knowledge pages. Determinism is a
requirement, not a nicety -- the same question returns the same citations
every run, which is what makes the demo reproducible.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
The frozen interfaces every other part of the keel skin compiles against:
knowledge + process types, the four demo personas, the role context, the
two shared presentational atoms, plus brand identity, nav, and the
.theme-keel token block (deep pine on warm neutrals, tightest radius in
the app, --brand-violet carrying the amber awaiting-approval accent).
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkMi54kqfNwAoUB3M3CBp3
## 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.
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.
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.
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.