Commit Graph

15424 Commits

Author SHA1 Message Date
Jordan Ritter 67ec66be0d refactor(runtime): load the MCP SSE transport lazily
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.
2026-08-03 10:48:14 -07:00
Jordan Ritter 6a8dc4ec50 fix(deps): patch eventsource to drop its bun export condition
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.
2026-08-03 10:48:14 -07:00
Jordan Ritter a9f98314d0 ci(runtime): pin Bun to 1.3.14 for the integration job
`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.
2026-08-03 10:48:14 -07:00
Tyler Slaton 7f37c3395e fix(channels-intelligence): restore inbound trigger files (#6332)
## 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.
2026-08-03 10:43:41 -07:00
Tyler Slaton ebace44a9a feat(channels): add native channel JSX (refs OSS-655) (#6331)
## 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`
2026-08-03 10:42:19 -07:00
Tyler Slaton 6120cebebe docs(channels): document Teams one-command setup (#6320)
## 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.
2026-08-03 10:40:35 -07:00
GeneralJerel ad5fc1bab5 feat(skills): add setup-slack-channel and split it from copilotkit-channels
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
2026-08-03 10:33:26 -07:00
Tyler Slaton 65150a683b fix(channels-intelligence): repair all transcript consumers 2026-08-03 09:26:04 -07:00
Mike Ryan b88f9b7e4f feat(channels): complete native JSX contracts 2026-08-03 09:23:15 -07:00
Mike Ryan f8145b05b7 feat(channels): add native Slack and Teams JSX 2026-08-03 09:23:15 -07:00
Mike Ryan fa92ebcf44 feat(channels): recover actions by stable JSX key 2026-08-03 09:23:15 -07:00
Mike Ryan 8707d8ce12 feat(channels): add agent-rendered component tools 2026-08-03 09:23:15 -07:00
Tyler Slaton 135a614631 docs(channels): name the short-scoped Slack token as its own silent failure (#6330)
## 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)
2026-08-03 09:12:00 -07:00
Tyler Slaton 6c2c289d85 fix(channels-intelligence): restore prepared trigger files 2026-08-03 08:52:57 -07:00
Benjamin Taylor b9ea51e819 docs(channels): name the short-scoped Slack token as its own silent failure
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.
2026-08-03 10:37:00 -05:00
Tyler Slaton 84d1caaa58 feat(shell-docs): launch Channels activation experience (#6276)
## 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
2026-08-03 08:29:22 -07:00
Ben Taylor 52966e6ec1 chore(examples): move the starters onto channels 0.6.1 (#6329)
## 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)
2026-08-03 10:11:03 -05:00
renovate[bot] 26a23bbf3a chore(deps): update github actions (#6326)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [pnpm/action-setup](https://redirect.github.com/pnpm/action-setup) |
action | patch | `v6.0.9` → `v6.0.10` |
|
[zizmorcore/zizmor-action](https://redirect.github.com/zizmorcore/zizmor-action)
| action | patch | `v0.6.1` → `v0.6.2` |

---

### Release Notes

<details>
<summary>pnpm/action-setup (pnpm/action-setup)</summary>

###
[`v6.0.10`](https://redirect.github.com/pnpm/action-setup/compare/v6.0.9...v6.0.10)

[Compare
Source](https://redirect.github.com/pnpm/action-setup/compare/v6.0.9...v6.0.10)

</details>

<details>
<summary>zizmorcore/zizmor-action (zizmorcore/zizmor-action)</summary>

###
[`v0.6.2`](https://redirect.github.com/zizmorcore/zizmor-action/releases/tag/v0.6.2)

[Compare
Source](https://redirect.github.com/zizmorcore/zizmor-action/compare/v0.6.1...v0.6.2)

zizmor 1.29.0 is now the default version.

</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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->
2026-08-03 15:03:29 +00:00
Sam Julien 9c250af1a9 Merge remote-tracking branch 'origin/main' into codex/channels-docs-followups
# Conflicts:
#	showcase/shell-docs/src/lib/__tests__/channels-docs.test.ts
2026-08-03 08:01:08 -07:00
Benjamin Taylor 23671e09f1 chore(examples): move the starters onto channels 0.6.1
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.
2026-08-03 09:19:47 -05:00
renovate[bot] 42e0df471a chore(deps): update github actions 2026-08-03 14:10:54 +00:00
Ben Taylor d6f3914d18 chore: release channels v0.6.1 (#6328)
## 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.
channels/v0.6.1
2026-08-03 09:08:49 -05:00
BenTaylorDev bdf054f8bd chore: release channels v0.6.1 2026-08-03 14:06:07 +00:00
Ben Taylor b0848f522f fix(channels): report a clone that drops subclass state, do not refuse the turn (#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.
2026-08-03 06:34:12 -05:00
Alem Tuzlak 8101e5ef52 feat(skills): make managed Intelligence the default setup path, add a Channels skill (refs OSS-705) (#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)
2026-08-03 12:54:29 +02:00
Alem Tuzlak a97abd2bc5 feat(examples): let every starter host a managed Channel (#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](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.
2026-08-03 12:52:22 +02:00
seanxuu de0a659b2d fix(react-core): invalidate messages memo for activity object content
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.
2026-08-03 14:17:20 +08:00
JYbill 8a172a3941 Merge branch 'CopilotKit:main' into fix/state-manager-run-id 2026-08-03 14:02:37 +08:00
xiaoqinvar ba5e2fde60 fix(web-inspector): record step events 2026-08-03 11:59:39 +08:00
Benjamin Taylor 768a69c99d fix(channels): report a clone that drops subclass state, do not refuse 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.
2026-08-02 21:45:23 -05:00
Benjamin Taylor 475002e49d chore(examples): move the starters off the canary onto stable
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.
2026-08-02 20:05:55 -05:00
Ben Taylor bd2b2a7a9c chore: release monorepo v1.65.0 (#6318)
## 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.
v1.65.0
2026-08-02 19:42:02 -05:00
Maxim 731c23d595 feat(reskinnable-demo): wire and register the keel skin
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
2026-08-03 01:44:47 +02:00
Maxim b3404d490b feat(reskinnable-demo): add keel chat components and canvas surface
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
2026-08-03 01:44:46 +02:00
Maxim 4e58f25b76 feat(reskinnable-demo): add keel chrome, identity, and pages
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
2026-08-03 01:44:46 +02:00
Maxim 8eb9fcbb2b feat(reskinnable-demo): add keel playbooks, run engine, and ticker
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
2026-08-03 01:44:45 +02:00
Maxim 95095bea54 feat(reskinnable-demo): add keel knowledge corpus and deterministic retrieval
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
2026-08-03 01:44:45 +02:00
Maxim e3efa79d0a feat(reskinnable-demo): add keel skin contracts, theme, and shared atoms
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
2026-08-03 01:44:44 +02:00
Tyler Slaton dab3cde9a0 docs(channels): document Teams one-command setup 2026-08-02 16:23:02 -07:00
BenTaylorDev 6988d5d8e2 chore: release monorepo v1.65.0 2026-08-02 22:43:24 +00:00
Ben Taylor bba3706bdc chore: release channels v0.6.0 (#6317)
## 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.
channels/v0.6.0
2026-08-02 17:08:38 -05:00
Benjamin Taylor b2803f89f9 docs(channels): name the failure where a Channel is online and still 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.
2026-08-02 15:27:46 -05:00
BenTaylorDev 63c15ce445 chore: release channels v0.6.0 2026-08-02 20:24:17 +00:00
Benjamin Taylor d00831878b docs(channels): lead with the scaffolded Channel, keep hand-wiring as 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.
2026-08-02 15:13:51 -05:00
Benjamin Taylor e1f88ebc12 refactor(examples): split the Channel out of the host, drop its HTTP 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 (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>
2026-08-02 12:41:46 -05:00
Benjamin Taylor b13fa63cb5 docs(examples): call them Channels, not managed Channels
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>
2026-08-02 12:17:13 -05:00
Benjamin Taylor fa134a71a5 fix(examples): copy mastra's .npmrc before npm install in Docker
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.
2026-08-02 08:21:36 -05:00
Benjamin Taylor c8736df6f0 docs(examples): warn that the Intelligence URL vars in .env.example are 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.
2026-08-02 01:18:13 -05:00
Benjamin Taylor 115717e4df docs(examples): document the managed Channel host in the remaining 14 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.
2026-08-02 01:17:58 -05:00
Benjamin Taylor 166dc94691 fix(examples): drop mastra's typecheck:channel — it can never pass
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.
2026-08-02 01:17:37 -05:00