Commit Graph

14123 Commits

Author SHA1 Message Date
Mark dd36800a47 chore: release angular v0.3.1 (#6350)
## Release angular v0.3.1

**Scope:** `angular` | **Bump:** `patch`

---

### How this release process works

1. **This PR was created automatically** by the "release / create-pr"
workflow.
   It bumped the `angular` packages to `0.3.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 `angular` packages to npm at version `0.3.1`
   - Creates git tag `angular/v0.3.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.
angular/v0.3.1
2026-08-03 14:14:30 -07:00
contextablemark bc968ce96b chore: release angular v0.3.1 2026-08-03 20:50:53 +00:00
Mark f1156c9125 fix(deps): patch eventsource so Bun stops breaking the runtime integration job (#6334)
## What

Fixes the intermittently-red `test / integration / runtime` **bun** leg.
Three commits, smallest blast radius first:

1. **`ci(runtime)`** — pin `bun-version` from `latest` to `1.3.14` so a
Bun release can't change module-resolution behaviour between runs. (Only
`bun-version: latest` in the repo.)
2. **`fix(deps)`** — **this is the actual fix.** Patch
`eventsource@3.0.7` to drop its `bun` export condition, via `pnpm patch`
+ `patchedDependencies`.
3. **`refactor(runtime)`** — module-graph hygiene: load the MCP SSE
transport lazily. Explicitly **not** a behaviour fix; commit 2 is.

## Root cause

```
TypeError: require() async module ".../eventsource@3.0.7/node_modules/eventsource/dist/index.js" is unsupported. use "await import()" instead.
    at .../@modelcontextprotocol/sdk/dist/cjs/client/sse.js:4:7
    at .../@ag-ui/mcp-apps-middleware/dist/index.js:1:983
    at processTicksAndRejections (unknown:7:39)
```

- `eventsource@3.0.7` maps its `bun` export condition to the **ESM**
build (`dist/index.js`). Bun resolves `bun` **before** `require`, so a
CJS `require("eventsource")` receives an async ESM module and throws.
The package ships a real CJS build (`dist/index.cjs`) 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@0.0.3` — a
CJS-only package (`main: ./dist/index.js`, no `exports`, no `type:
module`) that `require`s that SDK path unconditionally at module load.
- **Why intermittent:** it's a load-order race. If the ESM graph fully
evaluates `eventsource` first, the later CJS `require` can be served
synchronously and the run passes; otherwise it throws.

Dropping the `bun` key makes Bun fall through to `import` for ESM
consumers (same `dist/index.js` as before — no behaviour change) and to
`require` for CJS consumers (`dist/index.cjs`, which is what they need).
Only `bun` is touched; `deno`/`source`/`import`/`require`/`default` are
left alone.

**A version bump is not an alternative:** `eventsource@4.1.0` still
ships the same `bun` → ESM mapping.

## Patch diff

`patches/eventsource@3.0.7.patch` (header abridged — the file carries
the full rationale and an explicit deletion criterion so it doesn't
become permanent by accident):

```diff
# Drops the `bun` export condition from eventsource.
# ...
# DELETE THIS PATCH WHEN: eventsource drops the `bun` condition or points it at
# dist/index.cjs, OR Bun stops preferring `bun` over `require` for CJS requires.
diff --git a/package.json b/package.json
@@ -10,7 +10,6 @@
   "exports": {
     ".": {
       "deno": "./dist/index.js",
-      "bun": "./dist/index.js",
       "source": "./src/index.ts",
       "import": "./dist/index.js",
       "require": "./dist/index.cjs",
```

Root `package.json` gains:

```json
"patchedDependencies": { "eventsource@3.0.7": "patches/eventsource@3.0.7.patch" }
```

This repo had no `patches/` precedent (it uses `pnpm.overrides`), so
this sets one — hence the minimal one-line patch and the documented
removal criterion.

## Red-green proof

All four states. Local runs are the **same command on the same
machine**, differing only by whether the patch is applied. Bun 1.3.14,
macOS arm64, run from `packages/runtime`:

```sh
bun test src/v2/runtime/__tests__/integration/bun/bun-servers.integration.test.ts
```

A single green run proves nothing here — it's a race — so both local
states are N=20.

### 1. CI-RED

- This branch before the patch, run
[30835752558](https://github.com/CopilotKit/CopilotKit/actions/runs/30835752558)
@ `5456e308b9` — `runtime / node` success, **`runtime / bun` failure**:

```
4 | const eventsource_1 = require("eventsource");
TypeError: require() async module "/home/runner/work/CopilotKit/CopilotKit/node_modules/.pnpm/eventsource@3.0.7/node_modules/eventsource/dist/index.js" is unsupported. use "await import()" instead.
 0 pass
 1 fail
```

- Also on `main` @ `26a23bbf3a`, run
[30825667393](https://github.com/CopilotKit/CopilotKit/actions/runs/30825667393)
— same leg, same failure.

### 2. LOCAL-RED (eventsource UNPATCHED, N=20)

```
run  1:  72 pass  0 fail
run  2:   0 pass  1 fail
run  3:   0 pass  1 fail
run  4:   0 pass  1 fail
run  5:   0 pass  1 fail
run  6:   0 pass  1 fail
run  7:   0 pass  1 fail
run  8:   0 pass  1 fail
run  9:   0 pass  1 fail
run 10:  72 pass  0 fail
run 11:   0 pass  1 fail
run 12:   0 pass  1 fail
run 13:  72 pass  0 fail
run 14:   0 pass  1 fail
run 15:   0 pass  1 fail
run 16:  72 pass  0 fail
run 17:   0 pass  1 fail
run 18:  72 pass  0 fail
run 19:   0 pass  1 fail
run 20:   0 pass  1 fail
LOCAL-RED TOTAL: pass=5 fail=15  (out of 20)
```

### 3. LOCAL-GREEN (eventsource PATCHED, N=20)

```
run  1:  72 pass  0 fail
run  2:  72 pass  0 fail
run  3:  72 pass  0 fail
run  4:  72 pass  0 fail
run  5:  72 pass  0 fail
run  6:  72 pass  0 fail
run  7:  72 pass  0 fail
run  8:  72 pass  0 fail
run  9:  72 pass  0 fail
run 10:  72 pass  0 fail
run 11:  72 pass  0 fail
run 12:  72 pass  0 fail
run 13:  72 pass  0 fail
run 14:  72 pass  0 fail
run 15:  72 pass  0 fail
run 16:  72 pass  0 fail
run 17:  72 pass  0 fail
run 18:  72 pass  0 fail
run 19:  72 pass  0 fail
run 20:  72 pass  0 fail
LOCAL-GREEN TOTAL: pass=20 fail=0  (out of 20)
```

**5/20 → 20/20.**

### 4. CI-GREEN

The `test / integration / runtime` bun leg on this PR is the
load-bearing evidence. See checks below.

## Clean-install verification

A patch that only works incrementally is worthless in CI, so this was
verified from scratch — every `node_modules` in the workspace deleted,
then `pnpm install --frozen-lockfile`:

- Install exited **0** with `--frozen-lockfile` (lockfile is
self-consistent; no drift).
- Exactly one `eventsource` entry in the store, and it is the patched
one:

`node_modules/.pnpm/eventsource@3.0.7_patch_hash=427032a8df76e38f39988ff5fb919a02ccb70eb8a235e2b484007e4bebb1e67e/`
- Resolved `package.json` in the store after clean install:

`{"deno":"./dist/index.js","source":"./src/index.ts","import":"./dist/index.js","require":"./dist/index.cjs","default":"./dist/index.js"}`
— `bun` absent, everything else intact.
- Lockfile records it deterministically:
`patchedDependencies.eventsource@3.0.7` with `hash: 427032a8...` and
`path: patches/eventsource@3.0.7.patch`, and the dependency edge
resolves as `eventsource@3.0.7(patch_hash=427032a8...)`.
- `--frozen-lockfile` accepted the lockfile verbatim (it does not
rewrite), so the lockfile is self-consistent with the manifests.
- The comment header on the patch file does not break pnpm's patch
applier.
- **Lockfile diff is scoped to eventsource — 9 lines, 3 hunks, nothing
else.** An earlier revision of this branch carried incidental drift
(`vue-component-type-helpers` 3.3.8→3.3.9 and a `vite` peer-range
narrowing) picked up by a non-frozen install; that has been reverted so
the diff contains only the patch wiring.

## Tests

All from `packages/runtime`, with the patch applied:

| Suite | Command | Result |
|---|---|---|
| Full runtime suite | `pnpm exec vitest run` | **130 files / 1835 tests
passed**, 0 failed |
| Node integration (other CI leg) | `pnpm exec vitest run
src/v2/runtime/__tests__/integration/node-servers.integration.test.ts` |
**153 passed** |
| MCP + SSE transport | `pnpm exec vitest run
src/agent/__tests__/mcp-servers-integration.test.ts
src/agent/__tests__/mcp-clients.test.ts
src/v2/runtime/__tests__/mcp-apps-middleware-integration.test.ts` | **3
files / 22 passed** |
| Bun integration | `bun test .../bun-servers.integration.test.ts` |
**20/20** (was 5/20) |

Non-Bun consumers are unaffected by construction — Node never reads the
`bun` export condition — and the Node suites above confirm it. The SSE
path stays covered: `mcp-servers-integration.test.ts` exercises
`mcpServers: [{ type: "sse", url }]`, so it executes the new `await
import()`, which sits **outside** the `try/catch` that swallows
per-server connection failures.

## Module-graph proof for commit 3

Commit 3 is hygiene, so it gets its own narrower proof. Probe: Bun
populates `require.cache` with the resolved path of every module
actually loaded, so importing one module and inspecting that cache shows
whether `eventsource` entered the graph. Two controls run every time so
it can't pass vacuously.

```ts
const target = process.argv[2]!;
await import(target);
const keys = Object.keys(require.cache).filter(
  (k) => /eventsource/.test(k) && !/eventsource-parser/.test(k),
);
console.log(`${target}\n  eventsource loaded: ${keys.length > 0 ? "YES" : "NO"}`);
```

| Module | before commit 3 | after commit 3 |
|---|---|---|
| `@copilotkit/shared` (negative control) | NO | NO |
| `@modelcontextprotocol/sdk/client/sse.js` (positive control) | YES |
YES |
| `../src/agent/index.ts` (subject, non-SSE path) | **YES** | **NO** |

Both controls hold steady; only the subject flips. Measured on its own,
commit 3 does **not** move the bun pass rate (5/20 before, 3/20 after
within noise) — which is exactly why commit 2 exists.

## Typing

No `as any`, no `@ts-ignore`. `const { SSEClientTransport } = await
import(...)` keeps the class fully typed — TypeScript resolves
dynamic-import types statically. `packages/runtime/tsconfig.json`
already sets `"module": "es2022"` with the comment *"so dynamic import()
typechecks"*, so the pattern is anticipated.

Two adjacent bare `let` declarations (`transport`, `mcpClient`) gained
explicit annotations (`MCPTransport | undefined`, `MCPClient`) because
editors surface them as implicit-any suggestions. Both pre-existed on
`main`. Verified: `tsc --noEmit` clean; `tsc --noEmit --strict` error
set **identical to baseline** (3 pre-existing unrelated `TS2769`s);
`oxlint` warnings **unchanged from baseline** (2, both pre-existing).

`SSEClientTransport` is `@deprecated` in SDK 1.29.0 in favour of
`StreamableHTTPClientTransport`. That deprecation pre-exists on `main`
and is left alone: `type: "sse"` is documented public config, SSE and
Streamable HTTP are different wire protocols, and the SDK's own note
says clients "may need to support both transports during the migration
period." Migrating is a user-facing change for its own PR.

## Gates run

- `pnpm exec oxfmt --check packages/runtime/src/agent/index.ts` — clean
- `pnpm exec oxlint packages/runtime/src/agent/index.ts` — 0 errors, 2
warnings (both pre-existing on `main`)
- `pnpm nx run @copilotkit/runtime:check-types` — pass
- `pnpm exec commitlint --from HEAD~3 --to HEAD` — pass
- `pnpm install --frozen-lockfile` from a fully wiped workspace — exit 0
2026-08-03 13:33:48 -07:00
Tyler Slaton 34b81a3657 chore: release monorepo v1.66.0 (#6348)
## Release monorepo v1.66.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.66.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.66.0`
   - Creates git tag `monorepo/v1.66.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.66.0
2026-08-03 13:23:06 -07:00
tylerslaton a87b77a991 chore: release monorepo v1.66.0 2026-08-03 20:14:52 +00:00
Tyler Slaton d44b6c8e92 chore: release channels v0.7.0 (#6345)
## Release channels v0.7.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.7.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.7.0`
   - Creates git tag `channels/v0.7.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.7.0
2026-08-03 13:02:31 -07:00
tylerslaton 53bf978904 chore: release channels v0.7.0 2026-08-03 19:52:48 +00:00
Mark 9e08421653 fix(a2ui-renderer): bump @a2ui/web_core to 0.10.4 for openUrl XSS (#6343)
Closes the `openUrl` XSS reported against `@a2ui/web_core`
([GHSA-72qq-p3r5-f7wq](https://github.com/a2ui-project/a2ui/security/advisories/GHSA-72qq-p3r5-f7wq),
CVSS 9.3).

## The vulnerability

`@a2ui/web_core` <= 0.10.1 passed an agent-supplied `openUrl` argument
straight to `window.open()` with no scheme allowlist:

```js
// basic_catalog/functions/basic_functions.js — 0.9.0
if (args.url && typeof window !== 'undefined' && window.open) {
    window.open(args.url, '_blank');   // no scheme check
}
```

A malicious agent could emit a Button whose `functionCall` named a
`javascript:` URI; clicking it executed arbitrary script in the host
application's origin. The Basic Catalog is the default, so no
non-default configuration was required to be exposed.

## Why it reached us

We pinned `0.9.0` **exactly**, as a runtime `dependencies` entry of two
published packages — so downstream users could not upgrade out of it
without an `overrides` entry:

| Published package | Path to the vulnerable version |
|---|---|
| `@copilotkit/a2ui-renderer` | direct pin `0.9.0` |
| `@copilotkit/vue` | direct pin `0.9.0` |
| `@copilotkit/react-core` | → `a2ui-renderer` |

We import the sink deliberately (`BASIC_FUNCTIONS`) in four places
across the React, Lit, and Vue catalogs, and add no sanitisation of our
own. Note the advisory enumerates three affected renderers (React, Lit,
Angular); we ship a fourth, Vue, that upstream did not list.

## The change

Bump to `0.10.4`, which adds a strict http/https allowlist plus
`noopener,noreferrer`. This is a drop-in upgrade: `0.10.4` still exports
the `./v0_9` and `./v0_9/basic_catalog` entrypoints we import, and of
the `v0_9` surface (165 → 194 exports) the only symbol removed is
`FrameworkSignal`, which is referenced nowhere in this repo.

`showcase/angular` and `examples/v2/angular/demo` are private; those
bumps are hygiene only.

## Tests

Adds 6 regression tests over the React and Lit renderers, which reach
the sink independently of each other. They assert that `javascript:` and
`data:` URIs never reach `window.open`, that https URLs still open with
`noopener,noreferrer`, and that a blocked scheme leaves the surface
mounted rather than escaping into the click handler.

These were verified to be non-vacuous: pinned back to `0.9.0`, **5 of
the 6 fail**, including direct confirmation that `javascript:alert(1)`
reaches `window.open` through our own renderer on the vulnerable
version.

Full suites green: `a2ui-renderer` 22, `vue` 1074, `react-core` 1471,
`runtime` 1835, `angular` 292, `react-native` 251. Builds and
type-checks clean.

## Behaviour change worth knowing

`0.10.x` changes `openUrl`'s failure mode: the old code silently no-op'd
on a bad URL, the patched one throws `A2uiExpressionError` for a
non-http(s) scheme. That throw does **not** reach the render path —
`web_core`'s own `evaluateFunctionReactive` already catches it and
routes it to `surface.dispatchError`. Confirmed by exercising a real
click in both renderers: nothing escapes, no uncaught error, the surface
stays mounted.

## Follow-ups (not in this PR)

- **`@copilotkit/angular@0.3.0` remains exposed.** It pins
`@copilotkit/a2ui-renderer@1.63.2`, which pins the vulnerable `0.9.0`.
It needs a release after `a2ui-renderer` publishes, or Angular users
stay on the vulnerable transitive.
- **Blocked actions are invisible.** The resulting `EXPRESSION_ERROR` is
emitted on `surface.onError`, which no renderer subscribes to — so an
agent probing `javascript:` URIs is blocked completely silently, with no
log or telemetry. Surfacing it is a cross-renderer API decision,
deliberately kept out of a security bump.
2026-08-03 12:09:07 -07:00
Sam Julien 5d802f3932 docs(channels): reconcile launch documentation (#6335)
## Summary

This PR reconciles the Channels documentation with the released Channels
SDK and Runtime behavior, recent CopilotKit and Intelligence changes,
and the launch review of the provider docs.

The scope is intentionally limited to Channels guides, provider routing,
Channels SDK reference pages, and regression coverage in
`showcase/shell-docs`.

## Why

The existing docs mixed older package versions and pre-launch
assumptions with newer SDK behavior. In particular:

- examples still referenced Channels SDK `0.5.0` / `0.6.0` and Runtime
`1.64.2`
- lifecycle guidance did not distinguish eager Node/Express startup from
lazy Hono/generic Fetch startup
- Slack setup omitted current interactivity and file-scope requirements
- routing, welcome-event, reply-continuation, event-sanitization, clone,
and file-identifier behavior had changed or lacked precise documentation
- identity and Memory were not documented as a complete provider-scoped
journey
- scaling and HITL guidance overstated public configuration and
cross-backend support
- Channels CLI support is not yet part of the released public setup path
and should not be presented as available

## What changed

### Released versions and setup paths

- updates Channels examples to SDK `0.6.1` and Runtime `1.65.0`
- keeps the Intelligence browser wizard as the documented released setup
path
- excludes Channels CLI instructions until that support ships in a
public release
- preserves the latest browser-based Teams wizard details for draft
creation, branding/package handling, permissions, installation state,
and runtime verification
- updates Slack setup to use a bot token plus Signing Secret, enable
Interactivity, and include `files:read` / `files:write`
- tells existing Slack app owners where to obtain the current generated
manifest, when to reinstall, and when to refresh the bot token

### Identity and Memory

- adds one shared `Identity and Memory` source guide exposed only
through provider-scoped routes:
  - `/slack/identity-and-memory`
  - `/teams/identity-and-memory`
- does not introduce a channel-agnostic documentation surface
- separates conversation, current provider actor, and canonical
application user
- provides provider-specific Slack and Teams identity mapping examples
without leaking the other provider's identifiers into the rendered page
- documents safe per-run user/project Memory grants, unlinked-user
behavior, and approval resume subjects
- replaces the incomplete approval snippet with a complete
registered-component callback example
- adds a verification sequence for multi-actor conversations, unlinked
actors, and initiator-versus-actor approval behavior
- corrects the `channel_memory_unavailable` description to reflect the
runtime/adapter capability check
- cross-links the guide from both provider quickstarts,
thread/transcript guidance, and SDK reference content

### Runtime lifecycle and routing

- documents that Node and Express listeners start the Channel eagerly
- documents lazy startup for Hono and generic Fetch adapters
- clarifies when `ready()` is optional versus required
- places signal-handler registration before readiness
- corrects connecting-status expectations
- documents the single-path routing rules for mentions and ordinary
messages
- describes `onWelcome` as a lifecycle event rather than a synthetic
user message

### SDK reference corrections

- documents reply-continuation defaults and truncation behavior:
  - 11,000 UTF-8 bytes
  - 20 messages
- narrows `sanitizeAgentEvents` guidance to the known `parentMessageId`
repair
- documents per-turn agent cloning, including factory results, and the
`0.6.1` warning behavior
- distinguishes `assetId` from provider `fileId` in `Thread.postFile()`
- adds a current direct Slack adapter example
- updates command and reaction coverage for the current SDK surface
- keeps detailed option semantics in the `createChannel` function
reference and links to it from the `Channel` class page to reduce
duplication

### Production and HITL guidance

- removes the nonexistent public `maxConcurrentDeliveries` option
- describes the runtime's internal bounded delivery capacity without
presenting it as user configuration
- names the validated managed approval path precisely: a DeepAgent
tool-driven `on_interrupt` that returns from delivery and resumes on a
later interaction
- gives other backends an explicit real-provider verification checklist
instead of implying universal native AG-UI interrupt compatibility
- clarifies that event sanitization does not create framework interrupt
support

### Regression coverage

- adds route tests for both provider-scoped identity-and-memory pages
- adds documentation assertions for versions, lifecycle, Slack manifest
requirements, routing, SDK behavior, scaling/HITL claims,
provider-specific identity examples, verification steps, and cross-links
- keeps a broad guard preventing unreleased Channels CLI commands from
entering the public setup journey without enumerating future command or
configuration details
- makes wording assertions tolerant of normal MDX line wrapping

## Rebase reconciliation

The branch is rebased onto current `origin/main` (`0c72359cbd`). The
rebase included overlapping changes to the Intelligence setup guide. The
resolution retains the newer browser-based Teams setup improvements
while excluding the CLI-only path so the page remains aligned with the
released public setup surface.

## User impact

Readers get setup instructions that match the released product, more
precise lifecycle and SDK behavior, and a complete provider-scoped
identity/Memory journey. Existing Slack users can find and apply the
current generated manifest, approval implementers get a testable
compatibility boundary, and users are not directed to unreleased CLI
commands.

## Scope boundaries

This PR does **not**:

- change Channels SDK or Runtime implementation code
- document unreleased CLI commands
- introduce channel-agnostic Channels pages
- modify unrelated documentation
- commit generated registry, catalog, setup-content, or search-index
churn

## Validation

From the repository root:

- `pnpm exec oxfmt --check showcase/shell-docs/next.config.ts
showcase/shell-docs/src/lib/channel-guide-routes.ts
showcase/shell-docs/src/lib/__tests__/channel-guide-routes.test.ts
showcase/shell-docs/src/lib/__tests__/channels-docs.test.ts`
- `git diff --check origin/main...HEAD`
- publication-safety scan of added lines for credentials, private Slack
references, internal email addresses, and private keys
- scan confirming Channels CLI commands and unreleased setup details are
absent from public Channels docs and regression tests

From `showcase/shell-docs`:

- `npm run lint` — 0 errors; existing package warnings remain unchanged
- `npm run typecheck`
- `./node_modules/.bin/vitest run --maxWorkers=1 --no-file-parallelism`
— 54 files, 368 tests passed
- `npm run build` — production build completed; 222 pages generated

Local rendered-route verification:

- `/slack/identity-and-memory` — HTTP 200
- `/teams/identity-and-memory` — HTTP 200
- `/slack/intelligence` — HTTP 200
- `/teams/intelligence` — HTTP 200

The worktree is clean, and the local review server remains available at
`http://localhost:3003`.
2026-08-03 12:07:09 -07:00
Sam Julien 2cc279b4e9 test(channels): tolerate wrapped setup copy 2026-08-03 11:59:04 -07:00
Sam Julien 9ca0727a08 docs(channels): refine launch guidance 2026-08-03 11:53:33 -07:00
Sam Julien 9910ac9293 docs(channels): reconcile launch documentation 2026-08-03 11:53:32 -07:00
Mike Ryan 0c72359cbd Support Slack data visualization blocks (#6342)
## What does this PR do?

Slack data visualization blocks were listed in the native catalog, but
the JSX surface emitted a non-Slack `visualizations` field and had no
typed `chart` prop. This PR makes `Slack.Block.DataVisualization`
produce the payload that Slack documents.

- Adds public types for pie, bar, area, and line chart payloads.
- Requires `title` and `chart` at compile time and runtime.
- Checks Slack limits for titles, labels, segments, series, points,
categories, axis labels, positive pie values, unique series names, and
exact category coverage.
- Rejects more than two data visualization blocks in one message.
- Preserves the chart through direct Slack `chat.postMessage` delivery
and managed Intelligence delivery.
- Documents a small `defineChannelComponent` weather chart example.

Slack contract:
https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/

## Review path

1. Start with `packages/channels-slack/src/native.ts` for the public JSX
and TypeScript contract.
2. Read `packages/channels-slack/src/data-visualization.ts` for runtime
checks.
3. Read `packages/channels-slack/src/native-data-visualization.test.tsx`
for the weather component through the real adapter boundary.
4. Read
`packages/channels-slack/src/native-data-visualization-validation.test.ts`
for provider limit and cross-field cases.
5. Read
`packages/channels-intelligence/src/delivery-provider-elements.test.ts`
for the managed effect payload.

## Test coverage

- All four chart variants serialize to exact Slack JSON.
- Invalid nested payloads fail before a network request.
- Two charts pass and three charts fail.
- Negative values pass for series charts; non-positive values fail for
pie charts.
- Compile-time tests reject missing fields, unsupported chart types,
wrong chart shapes, and children.
- A `defineChannelComponent` weather card reaches
`SlackAdapter.client.chat.postMessage` with the expected Block Kit
request.

## Validation

- `NX_DAEMON=false pnpm nx run-many -t test,check-types,build
--projects=@copilotkit/channels-slack,@copilotkit/channels-intelligence`
- `NX_DAEMON=false pnpm nx run-many -t publint,attw
--projects=@copilotkit/channels-slack,@copilotkit/channels-intelligence`
- `NX_DAEMON=false pnpm verify:channels-umbrella`
- `pnpm check:channel-native-catalogs && pnpm
audit:channel-native-catalogs`
- `pnpm exec oxfmt --check packages/channels-slack/README.md`

This checkout has no `docs:check` script. The changed README passes the
repo formatter, and its executable example is covered by the adapter
test.

## Related PRs and Issues

- Follows the native Slack JSX surface merged in
https://github.com/CopilotKit/CopilotKit/pull/6331

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation
- [x] Allow edits by maintainers is enabled
2026-08-03 11:45:23 -07:00
Mark 36f2972150 fix(a2ui-renderer): bump @a2ui/web_core to 0.10.4 for the openUrl XSS advisory
GHSA-72qq-p3r5-f7wq (CVSS 9.3). web_core <= 0.10.1 passed an agent-supplied
`openUrl` argument straight to `window.open()` with no scheme allowlist, so a
Button whose `functionCall` named a `javascript:` URI executed arbitrary script
in the host origin when a user clicked it. The Basic Catalog is the default, so
no non-default configuration was required to be exposed.

We pinned 0.9.0 exactly, as a runtime dependency of two published packages
(@copilotkit/a2ui-renderer, @copilotkit/vue) and transitively of
@copilotkit/react-core and @copilotkit/angular, so downstream users could not
upgrade out of it on their own. 0.10.4 keeps the ./v0_9 and
./v0_9/basic_catalog entrypoints we import; the only symbol dropped from v0_9
is FrameworkSignal, which we never referenced.

Add regression tests over both renderers that reach the sink independently
(React and Lit). They assert that javascript: and data: URIs never reach
window.open, that https URLs still open with noopener,noreferrer, and that a
blocked scheme leaves the surface mounted rather than escaping into the click
handler. Verified they fail against 0.9.0 and pass against 0.10.4.
2026-08-03 18:43:00 +00:00
Mike Ryan 979a8ee990 feat(channels-slack): support data visualization blocks 2026-08-03 11:29:12 -07:00
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
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
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
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