Skip to content

[pull] main from CopilotKit:main - #471

Merged
pull[bot] merged 29 commits into
TheTechOddBug:mainfrom
CopilotKit:main
Aug 4, 2026
Merged

[pull] main from CopilotKit:main#471
pull[bot] merged 29 commits into
TheTechOddBug:mainfrom
CopilotKit:main

Conversation

@pull

@pull pull Bot commented Aug 4, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

dsfaccini and others added 29 commits August 3, 2026 17:08
`Agent.to_ag_ui()`, `AGUIApp`, and the `pydantic_ai.ag_ui` shim were removed in
Pydantic AI v2, so the example only ran on v1.

- pin `pydantic-ai-slim[ag-ui,openai]>=2.0.0` and `ag-ui-protocol>=0.1.19`
- import `StateDeps` from `pydantic_ai.ui`
- serve via `AGUIAdapter.dispatch_request` on a Starlette route
- give each request its own `deps` copy so state no longer leaks between threads
…skill

The Channels overview page, the docs landing activation strip, the website's
/channels strip, and the channels-sdk README each carried their own copy of the
onboarding workflow — six versions across three repos. They drifted, and each
went stale against the CLI independently: developers were told to install
unnamed skills, run `copilotkit channels` (which the published CLI does not
have), and use a bare `npx copilotkit` that a cached older binary shadows.

Every surface now emits the same two sentences naming one skill, and the
workflow itself lives in that skill. Shipped here:

- `buildChannelsActivationPrompt` returns a pointer, not a workflow, built from
  a single `CHANNELS_ONBOARDING_SKILL` constant.
- The overview page's two 20-line prompts — one Slack, one Teams, both hidden in
  accordions — collapse into the shared `<ChannelsStartPrompt />`.
- The prompt text renders on screen instead of living only in a clipboard
  payload. That is why it was missable: a copy button with an invisible payload
  reads as decoration. At one line there is nothing left to hide behind an
  accordion.
- Both surfaces emit `promptCopied` with a `surface` property, so the funnel can
  answer which road people actually take.

Tests pin the corrections that drift produced: the skill is named, the install
is non-interactive, the CLI is `@latest`, and the overview cannot re-embed a
workflow.
Keeps the featured Accordion treatment from #6356 as a shared opt-in component.
Its two consumers — the Slack and Teams starter prompts on the Channels
overview — are replaced here by the shared entry-point component, because the
payload those accordions concealed is now one line and there is nothing left to
disclose.
…tment

Adopts the visual language from the featured-Accordion work — accent panel,
terminal mark, eyebrow, prominent copy action — for the shared Channels entry
point, and drops the disclosure it was attached to. The accordion existed
because the payload was twenty lines; the payload is now one sentence, so
hiding it behind "Open & copy prompt" costs a click and buys nothing.

The panel renders exactly the text the button copies. Two earlier shapes were
wrong in instructive ways: a full monospace paragraph wrapped like a rendering
bug, and a code block with the ask underneath read as a shell command with a
footnote, which made a button labelled "Copy prompt" look like it was lying.
Only the command is monospace now; the prose around it wraps like prose.

The featured Accordion variant stays in mdx-components as a shared opt-in
capability, unused for the moment.
Points the entry point at setup-slack-channel, the slug the combined onboarding
prompt will occupy in skills/ once it lands from channels-sdk.
…clipboard

Two changes.

**Impressions.** Both docs entry points emitted a copy event and nothing else,
so the copy count had no denominator: a surface nobody scrolls to and a surface
everybody ignores were indistinguishable. `docs.channels_activation_viewed`
fires once per surface on the first intersection at 50%, from an
IntersectionObserver rather than on mount, since both sit below the fold. The
`surface` values move into a shared `CHANNELS_ACTIVATION_SURFACES` map so the
two docs roads — and copilotkit.ai/channels, which sends its own event name with
the same property — stay separable inside one funnel.

The observer is guarded on `typeof IntersectionObserver`. An impression is never
worth breaking a render for, and this repo's jsdom tests do not define it.

**The prompt is no longer rendered.** The panel offers the prompt through the
copy button alone. The supporting line says so explicitly rather than saying
"paste this" next to nothing.
…ction

The prompt is not on the page, so the button is the whole point of the panel. It
now sits directly under the heading instead of off to the right, where it read
as trailing furniture.

"Copy the prompt and paste it into your coding agent" also had nothing to point
at once the text stopped being rendered. The supporting line now says only what
the skill does.
…failure

The clipboard write and the capture call shared one try block, so a PostHog
client that threw reported "Copy blocked" for a prompt already sitting on the
clipboard. Only the write decides what the reader is told; capture moves behind
the same isolated helper the activation strip already uses, and the impression
observer uses it too instead of its own inline catch.

Two regression tests: capture throwing after a resolved write still shows
"Copied", and a rejected write still shows "Copy blocked" without emitting a
copy event.

Also drops 16 lines of `dev: true` lockfile churn picked up from an `npm install`
in the review worktree — no dependency actually changed.
…s palette

Two changes from review.

**The disclosure is back.** The panel is the shared featured `<Accordion>`
again — reused, not restyled — so the overview stays compact when collapsed and
a reader can expand to read the exact prompt before copying it. Copy-only made
the payload opaque, which was the problem #6356 set out to fix. The component
still owns the Slack/Teams switch and the analytics; the container is markup.

**The treatment is now token-only.** The featured variant carried a saturated
`--accent` tile and an accent-mixed gradient. `copilotkit-ui-theme` names a
purple accent bar or stripe as a known wrong direction, and
`copilotkit-branding` scopes accent to restrained, atmospheric use with
gradients behind content rather than as the contrast layer — the old treatment
was both at once, on a docs `--accent` that resolves to violet. It now matches
the in-content panel idiom already in `OpsPlatformCTA`: `--bg-elevated`,
`--border`, `--shadow-control`, and accent carried only by a small glyph and the
hover state. Padding drops to `p-4` like every other docs panel, so a collapsed
prompt no longer pushes the page's own introduction below the fold. Verified in
light and dark.

Also adds `docs.channels_activation_prompt_expanded`. The disclosure is where a
funnel loses people and neither `viewed` nor `promptCopied` can see it: someone
who never opened the panel is indistinguishable from someone who opened it and
walked away.

The prompt inside wraps instead of scrolling. The docs' usual code block scrolls
horizontally, which is right for code and wrong here — it hid half the prompt
behind the overflow, defeating the point of letting people read it first.
Replaces the skill-install pointer with a fetch of one hosted file:

    Read https://copilotkit.ai/channels-guide.md and help the user build
    their first channel

The guide lives at `public/channels-guide.md` on the marketing site and owns the
whole workflow. It asks the developer which platform and which agent framework
they want, which is why this pointer passes neither.

That is what resolves the review's blocking issue rather than papering over it.
Interpolating the picker's channel and backend meant these pages promised
coverage on a skill's behalf — and the skill it named is scoped to Slack, to the
provider half, and to an OpenTag checkout, so the Teams road pointed at a
workflow that does not exist and the backend picker implied nineteen it never
claimed. A pointer that names nothing cannot overpromise, and the guide handles
selection itself.

Consequently `CHANNELS_ONBOARDING_SKILL`, its install command, and the
per-selection prompt builders are gone: one constant serves every surface.
Supporting copy that said the prompt "installs the onboarding skill" or offered
a "tailored" prompt is corrected — neither is true now.
Keep drop, give-up, and recovery logs immediate.

Space repeated still-down reminders during long gateway outages.
## Release channels v0.7.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.7.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.7.1`
   - Creates git tag `channels/v0.7.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.
**Root page.** The channel and agent-backend pickers are gone, and the copy
action moves beside the supporting sentence, under the heading. The pickers had
stopped earning their place: the guide asks which platform and framework the
developer wants, so choosing here asked the same question twice and changed
nothing about what got copied. `ActivationSelect` and its option plumbing go with
them. The setup-guide link becomes one aside alongside OpenTag rather than a
per-selection route.

**Per-framework pages.** The accordion is gone. It existed to keep a twenty-line
prompt out of the way; the payload is now a single action, so a disclosure cost a
click and revealed nothing. The panel keeps the in-content idiom it shares with
`OpsPlatformCTA` — neutral surface, `--border`, accent on a small glyph — and the
button reads "Copy prompt" like every other surface.

`docs.channels_activation_prompt_expanded` retires with the disclosure that was
its only trigger.
## Release monorepo v1.66.1

**Scope:** `monorepo` | **Bump:** `patch`

---

### 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.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 `monorepo` packages to npm at version `1.66.1`
   - Creates git tag `monorepo/v1.66.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.
## Visual

Docs `/slack` — the featured panel from #6356, now carrying the one-line
pointer.

| Desktop screenshot | Mobile screenshot |
|---|---|
| ![docs slack
desktop](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-desktop-screenshot-7f14af56.png)
| ![docs slack
mobile](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-mobile-screenshot-7f14af56.png)
|

| Desktop scroll | Mobile scroll |
|---|---|
| <video
src="https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-desktop-scroll-7f14af56.webm"
controls width="400"></video> | <video
src="https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-mobile-scroll-7f14af56.webm"
controls width="240"></video> |

Docs `/teams` — same component, Teams wording, proving the frontend
switch.

| Desktop screenshot | Desktop scroll |
|---|---|
| ![docs teams
desktop](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-teams-expanded-screenshot-7f14af56.png)
| ![docs
root](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-root-desktop-screenshot-7f14af56.png)
|

<sub>Artifacts are hosted on a CopilotKit/website release tag rather
than a new tag in this repo, to keep clear of the monorepo's release
automation.</sub>

---

## Summary

Builds on #6356 (merged in here, its featured-panel treatment kept) and
finishes the job it started.

#6356 made the Channels starter prompts **discoverable**. This makes
them **correct**, and makes every other road say the same thing.

The onboarding workflow was written out as prose on six surfaces across
three repos: the Channels overview page (Slack and Teams), the docs
landing activation strip, copilotkit.ai/channels, and the channels-sdk
README twice. They drifted apart, and each went stale against the CLI on
its own schedule. That drift is what the dogfooding feedback was
reporting:

- **"It didn't tell me which skills to install, so it dropped me into a
list of god knows how many."** Neither copy button named a skill.
- **"`npx copilotkit project select` didn't work — I had an old CLI."**
Only some surfaces pinned `@latest`.
- **An agent halted on `copilotkit channels`.** No such command exists
in the published CLI — verified against 4.5.1, whose `--help` has no
`channels` entry.
- **"Copy this prompt is super easy to miss."** The strongest prompts
were behind an accordion; the two visible buttons had invisible
payloads.

## What changed

- **`<ChannelsStartPrompt />`** — one shared entry point,
frontend-aware, wearing #6356's featured treatment: accent panel,
terminal mark, eyebrow, prominent copy action.
- **Both overview accordions are gone.** The accordion existed because
the payload was twenty lines. The payload is now one sentence, so hiding
it behind "Open & copy prompt" costs a click and buys nothing.
- **The activation strip emits the same pointer**, rendered on screen
rather than living only in a clipboard payload.
- **Both surfaces send `promptCopied` with a `surface` property**, so
the funnel can answer which road people take. The website emits its own
event name with the same property for the same reason.

The prompt every surface now emits:

```
Run `npx copilotkit@latest skills install --skill setup-slack-channel -y`, then follow that
skill to build your first CopilotKit Channels agent and connect it to Slack using CopilotKit's
built-in agent.
```

## Notes for reviewers

- **`-y` respects `--skill`** — verified empirically against
`copilotkit@4.5.1`: exactly one skill installs, no picker. The installer
detects the coding agent it runs inside, so `--agent` is omitted.
- **The panel renders exactly what the button copies.** Two earlier
shapes were wrong in instructive ways: a full monospace paragraph
wrapped like a rendering bug, and a code block with the ask underneath
read as a shell command with a footnote — which made a button labelled
"Copy prompt" look like it was lying.
- **The `featured` Accordion variant from #6356 stays in
`mdx-components.tsx`** as a shared opt-in capability, but its only two
consumers were the accordions removed here, so it is currently unused.
Worth a call: keep it, or drop it in a follow-up.
- **`setup-slack-channel` is the slug the combined onboarding prompt
will occupy** once it lands from channels-sdk. Its frontmatter is
currently scoped hard to the Slack provider half, so it needs rescoping
before this reads correctly for Teams.

Companion PRs: CopilotKit/website#444 and the channels-sdk README.

## Validation

- `npx vitest run` in `showcase/shell-docs` — 54 files, 372 tests passed
- `npx tsc --noEmit` — clean
- pre-commit lint and commitlint hooks passed
- Rendered and reviewed `/slack` locally; confirmed the copied string
matches the rendered one



## Update — impressions and a copy-only prompt

Since the artifacts above were first posted:

- **Impression event.** `docs.channels_activation_viewed` fires once per
surface on first intersection at 50%. Both docs entry points previously
emitted a copy event and nothing else, so the copy count had no
denominator. `surface` values now live in a shared
`CHANNELS_ACTIVATION_SURFACES` map, keeping the landing strip, the
overview panel, and copilotkit.ai/channels separable inside one funnel.
- **Guarded on `typeof IntersectionObserver`.** An impression is never
worth breaking a render for, and this repo's jsdom tests do not define
it.
- **The prompt is no longer rendered.** Per review, the copy button
carries the payload. The panel leads with the copy action directly under
the heading — the prompt is not on the page, so the button is the point
of the panel rather than trailing furniture — and the supporting line no
longer says "paste this" next to nothing.

Screenshots and videos above were re-captured after all of it.

## Validation (current)

- `npx vitest run` in `showcase/shell-docs` — 55 files, 376 tests passed
- `npx tsc --noEmit` — clean
- pre-commit lint and commitlint hooks passed
- Reviewed `/slack` and `/teams` at 1440×900 and iPhone 13

## Companion PRs

- CopilotKit/channels-sdk#15 — **merged**
- CopilotKit/website#444 — open


---

## Update — addresses both review blockers

### Blocking issue: the skill did not support the promised workflow —
resolved by removing the skill from the path

The pointer is now a fetch of one hosted file, not a skill install:

```
Read https://copilotkit.ai/channels-guide.md and help the user build their first channel
```

The guide lives at `public/channels-guide.md` in CopilotKit/website and
owns the whole workflow. It asks the developer which platform and which
agent framework they want, which is why the pointer passes neither.

That removes the mismatch at its root rather than narrowing the copy
around it. Naming the picker's channel and backend meant these pages
promised coverage on `setup-slack-channel`'s behalf, and that skill is
scoped to Slack, to the provider half, and to an OpenTag checkout — so
Teams pointed at a workflow that does not exist and the picker implied
nineteen backends it never claimed. A pointer that names nothing cannot
overpromise.

`CHANNELS_ONBOARDING_SKILL`, its install command, and the per-selection
prompt builders are gone; one constant serves every surface.

### Visual regression: disclosure restored, treatment re-tokenised

| Collapsed | Expanded |
|---|---|
|
![collapsed](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-desktop-screenshot-7f14af56.png)
|
![expanded](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-expanded-screenshot-7f14af56.png)
|

| Mobile | Teams variant |
|---|---|
|
![mobile](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-slack-mobile-screenshot-7f14af56.png)
|
![teams](https://github.com/CopilotKit/website/releases/download/channels-entry-points-2026-08-03/docs-teams-expanded-screenshot-7f14af56.png)
|

- **The expandable preview is back.** The panel is the shared featured
`<Accordion>` again — reused, not re-implemented — so the overview stays
compact and a reader can expand to read the exact prompt before copying.
- **Whitespace.** Collapsed height went from ~380px to ~110px with
`p-4`, matching every other docs panel. The page's own introduction is
visible without scrolling.
- **Colour.** You were right, and my "it is unchanged from #6356" reply
missed the point. `copilotkit-ui-theme` names *"purple accent bar or
stripe"* as a known wrong direction, and `copilotkit-branding` requires
accent to be restrained with gradients behind content rather than as the
contrast layer — the saturated `--accent` tile plus accent-mixed
gradient was both at once. It now matches the `OpsPlatformCTA` idiom:
`--bg-elevated`, `--border`, `--shadow-control`, accent only on a 20px
glyph and the hover state. Verified light and dark.

One deliberate deviation: I dropped the "READY-TO-USE STARTER PROMPT"
eyebrow on the low-noise rule, since the title says the same thing. Easy
to restore if you want it.

### Behavioural bug: fixed

`copyPrompt` shared one `try` with `posthog.capture`, so a throwing
client reported "Copy blocked" for a prompt already on the clipboard.
Capture now sits behind the same isolated helper
`ChannelsActivationStrip` uses. Two regression tests: capture throwing
after a resolved write still shows "Copied"; a rejected write still
shows "Copy blocked" and emits no copy event.

### Lockfile churn: dropped

Reverted to `origin/main`. It came from an `npm install` in the review
worktree; no dependency changed.

### Also added

`docs.channels_activation_prompt_expanded` — the disclosure is a funnel
step neither `viewed` nor `promptCopied` can see, so someone who never
opened the panel was indistinguishable from someone who opened it and
left.

## Validation (current)

- `npx vitest run` in `showcase/shell-docs` — 55 files, 376 tests passed
- `npx tsc --noEmit` — clean; pre-commit lint and commitlint passed
- Reviewed `/slack` and `/teams`, collapsed and expanded, light and
dark, at 1440×900 and iPhone 13
The Channels onboarding workflow is served at
https://copilotkit.ai/channels-guide.md, and every other entry point now
copies one line that points there (#6357 for the docs surfaces). Coding
agents invoked through a skill had no such pointer, so they fell back to
setup-slack-channel, which is scoped to Slack, to the provider half, and to
an OpenTag checkout.

channels-setup is a pointer, not a copy. The workflow stays in one place and
is corrected there instead of drifting against the CLI on its own schedule.

Verifying the fetch is the substance of the file. The site answers unknown
paths with a "Page not found" HTML page under HTTP 200, so a status check
proves nothing: an agent that keys on the status code gets a marketing 404
and improvises the workflow from memory. The skill checks the body instead
-- markdown, the guide's H1, five Phase headings -- and stops if any fail.

Standalone skills are not generated from packages/*/skills, so the slug is
registered in RESERVED_LIFECYCLE_SLUGS. Without that entry the sync treats
the directory as an orphan and deletes it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Address review on #6355:

- Pin `pydantic-ai-slim[ag-ui,openai]==2.22.0` and `ag-ui-protocol==0.1.19`,
  matching the starter fleet standard. Open-ended floors would pull a
  breaking major on release, and `docker/Dockerfile.agent:16` runs a bare
  `uv sync` that would silently re-resolve forward.
- Mirror the specifiers in `uv.lock`'s `requires-dist` so `uv sync --frozen`
  at `Dockerfile:42` keeps working. Resolved versions and hashes are
  unchanged; `uv lock` is a no-op.
- Construct `StateDeps(ProverbsState())` per request instead of
  `dataclasses.replace()`. `replace()` is a shallow copy, so the new deps
  point at the same state object; it is only safe today because `StateDeps`
  has one field that the adapter rebinds before every run. Any deps class
  with a second mutable field would silently share it.
## What changed

- Mark initial gateway HTTP 5xx and transient transport failures as
retryable.
- Retry initial managed Channel activation with exponential backoff from
1 second to a 30-second cap until it connects or the manager stops.
- Preserve retry hints from `gateway_draining` join replies and retry
initial join timeouts.
- Keep HTTP 4xx and NXDOMAIN failures terminal.
- Back off established-session outage reminders from 30 seconds to a
15-minute cap while Phoenix continues reconnecting.

## Why

The OpenTag Railway runtime saw the gateway host return HTTP 502 during
an outage. Established Phoenix sessions keep retrying, but a runtime
that starts during the outage stops after its one initial connect
window. It cannot recover when the gateway comes back unless the process
restarts. Fixed 30-second reminder logs also flood long outages.

The gateway drain work now rejects new joins with a structured retryable
response. The client must preserve that response so the runtime can
retry instead of leaving the Channel in a terminal error state.

## Companion change

CopilotKit/OpenTag#25 keeps the Railway HTTP server alive while an
initial Channel retry is pending. OpenTag must consume a CopilotKit
release containing this PR before that companion change can recover by
itself.

## Validation

- `pnpm nx run-many -t test,check-types,build -p
@copilotkit/runtime,@copilotkit/channels-intelligence`
- `pnpm nx run-many -t publint,attw -p
@copilotkit/runtime,@copilotkit/channels-intelligence`
- pre-commit tests and package checks for all affected projects
- `pnpm exec oxfmt --check` on all five changed files
- `pnpm exec oxlint` on all five changed files
- `git diff --check`
This pull request was posted by Claude Code using claude-opus-5 on
behalf of David. David has not reviewed this diff.

`examples/integrations/pydantic-ai` only runs on Pydantic AI **v1**.
`Agent.to_ag_ui()`, `AGUIApp`, and the `pydantic_ai.ag_ui` shim were all
removed in v2 (pydantic/pydantic-ai#5464, announced in
pydantic/pydantic-ai#5345). The example pins `pydantic-ai-slim==1.0.18`,
so anyone installing it against current Pydantic AI (2.22.0) fails at
import.

This ports it to v2.

## Changes

1. `agent/pyproject.toml` — `pydantic-ai-slim[ag-ui,openai]>=2.0.0`,
`ag-ui-protocol>=0.1.19`
2. `agent/src/agent.py` — `StateDeps` moved from `pydantic_ai.ag_ui` to
`pydantic_ai.ui`
3. `agent/src/main.py` — serve via `AGUIAdapter.dispatch_request` on a
Starlette route
4. `agent/uv.lock` — relocked (resolves `pydantic-ai-slim` 2.22.0,
`ag-ui-protocol` 0.1.19)

## One fix beyond the mechanical port

The old wiring built the app once around a single shared `StateDeps`
instance. `dispatch_request` mutates `deps.state` with the state the
client sends, so one shared instance lets state leak between threads,
channels and users — which matters more for Channels than it did for a
single browser tab. Each request now gets its own `replace(deps)` copy,
matching the pattern in [Pydantic AI's own AG-UI
examples](https://github.com/pydantic/pydantic-ai/blob/main/examples/pydantic_ai_examples/ag_ui/api/shared_state.py).

<details><summary>Verified end to end</summary>

`uv sync` + a request through the actual ASGI app (model overridden with
`TestModel` so no API call), with a Channels-shaped payload (`threadId`,
`state`, `forwardedProps`):

```
health: 200 {'status': 'ok'}
POST / -> 200 text/event-stream; charset=utf-8
event types: ['RUN_STARTED', 'TOOL_CALL_START', 'TOOL_CALL_END', 'TOOL_CALL_START', 'TOOL_CALL_ARGS',
 'TOOL_CALL_END', ..., 'TOOL_CALL_RESULT', 'STATE_SNAPSHOT', 'TOOL_CALL_RESULT', 'STATE_SNAPSHOT',
 'TEXT_MESSAGE_START', 'TEXT_MESSAGE_CONTENT', ..., 'TEXT_MESSAGE_END', 'RUN_FINISHED']
threadId echoed: slack-C123-thread-1
tool call: get_proverbs
tool call: add_proverbs
tool call: set_proverbs
tool call: get_weather
```

The emitted event set is exactly what `channels-slack`'s `RunRenderer`
subscribes to (`RUN_*`, `TEXT_MESSAGE_*`, `TOOL_CALL_*`), plus
`STATE_SNAPSHOT` which it ignores.

</details>

<details><summary>Note on <code>ag-ui-protocol</code>: 0.1.19, not
0.1.18</summary>

Not required by this port, but worth pinning forward: typed multimodal
input content (`ImageInputContent` &c.) landed in 0.1.15 and the
interrupt lifecycle in 0.1.19. We found that a Pydantic AI install below
0.1.15 rejects an inbound image attachment with a 422 rather than
skipping it — so a Channels gateway forwarding a Slack image needs the
newer floor. We're tracking that on our side.

</details>
…ide (#6366)

## Summary

The Channels onboarding workflow is served at
**https://copilotkit.ai/channels-guide.md**, and every other entry point
now copies one line that points there — the docs surfaces in #6357, the
website strip in
[website#444](CopilotKit/website#444), the
README in
[channels-sdk#15](CopilotKit/channels-sdk#15).

Coding agents reached through a **skill** had no such pointer. They
matched `setup-slack-channel`, which is scoped to Slack, to the provider
half, and to an OpenTag checkout — so "help me get my agent into Teams"
landed on a workflow that does not cover it.

`channels-setup` closes that gap as a **pointer, not a copy**. The
workflow stays in one place and is corrected there, instead of becoming
a seventh surface that drifts against the CLI on its own schedule.

## Verifying the fetch is the substance of the file

A thin pointer has one non-obvious failure mode, and it is the reason
this skill is more than two sentences:

```
$ curl -sL -o /dev/null -w "%{http_code} %{content_type}\n" https://copilotkit.ai/channels-guide.md
200 text/html; charset=utf-8
$ curl -sL https://www.copilotkit.ai/channels-guide.md | grep -o "<title>[^<]*</title>"
<title>Page not found | CopilotKit | ...</title>
```

**A missing guide does not return 404.** The site answers unknown paths
with a "Page not found" HTML page under HTTP 200. An agent that keys on
the status code gets a 73KB marketing page, concludes the fetch
succeeded, and improvises channel setup from memory — which is exactly
what the guide's boundaries exist to prevent, and it fails in the most
expensive way available here: the project installs cleanly and answers
nothing.

So the skill checks the **body**: markdown rather than HTML, the guide's
`# Build and prove a CopilotKit Channels agent` H1, and five `## Phase`
headings. If any fail it stops and hands the user the URL, no matter
what the status code said.

## Notes

- **`RESERVED_LIFECYCLE_SLUGS` is not optional.** Standalone skills are
not generated from `packages/*/skills`, so without the entry
`sync-plugin-skills` treats the directory as an orphan and deletes it.
Test updated alongside, including the hard-coded `size` (10 → 11).
- **No existing skill is modified.** `setup-slack-channel` and
`copilotkit-channels` keep their descriptions and bodies. Three skills
now match channel work; if that proves too ambiguous in practice,
narrowing the other two's `description` frontmatter is a follow-up, not
a body rewrite.
- **No manifest change.** `plugin.json` and `marketplace.json` do not
enumerate skills individually.
- No changeset — `packages/**` is untouched.

## Blocked on

**The guide is not live yet.**
[website#444](CopilotKit/website#444) is still
open, so the URL currently serves the 404 page shown above. This skill
is inert until that merges and deploys — at which point it works with no
further change here. The sentinel means the pre-deploy state is a clean
stop rather than a wrong answer, so merging early is safe; it just isn't
useful yet.

## Validation

- `npx vitest run scripts/__tests__/sync-plugin-skills.test.ts` — 10
passed
- `pnpm run check:plugin-skills` — `plugin skill mirror in sync`, exit
0; `skills/channels-setup/` survives the orphan pass
- `npx oxfmt --check` on both changed TS files — clean
- `npx oxlint scripts/` — 0 errors
- `SKILL.md` frontmatter parsed with js-yaml: `name=channels-setup`,
`version=1.0.0`, description 660 chars
- Soft-404 behaviour confirmed against production with the `curl`
commands above

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Retesting the Slack setup end to end found the skill too cautious to be useful.
It stopped at almost every step, where the earlier version stopped only for
passwords and got through — and the run was rescued by the developer telling the
agent outright to just control their browser. A run that pauses at every control
is slower than the manual path it replaced.

Three changes, all pulling the same direction.

Driving the browser is now stated as the default rather than left implicit in
"most of this workflow happens in a browser". When the agent has no browser tool
it asks the developer to install one before starting, and names the route for the
harness it is actually running in — Claude Code and Codex enable this differently,
most other harnesses want a browser-use MCP server — with an instruction to look
it up rather than guess. The manual walkthrough survives only for an explicit
decline.

Consent is batched into one Phase 0 authorization naming the whole sequence: the
Slack app from the wizard manifest, its install, the Channel, the adapter attach,
and the API key. Phase 3 and the Intelligence reference previously required "state
what you are about to change, get an explicit yes" for every dashboard goal, and
the reference said it twice. Reading the page before acting stays required; it is
no longer a reason to stop.

The secret boundary is untouched: the developer still types the bot token, signing
secret, and API key themselves, and those remain the only mandatory stops
alongside anything the authorization did not cover.
## Summary

- stop handling in-flight lock renewal failures after the run has
settled
- keep aborting when a renewal fails during an active run
- cover the completion-before-renewal race with a regression test

## Why

Intelligence releases the thread lock after it accepts a terminal run
event. A renewal that was already in flight can then return a 409.
Clearing the interval stops future renewals, but it does not cancel that
pending promise, so Runtime logged an error and called `abortRun()`
after the run had completed.

The lifecycle guard makes that late rejection a no-op. Active-run
renewal failures still follow the existing abort path.

## Testing

- `pnpm nx test @copilotkit/runtime` — 1,866 tests passed
- `pnpm nx run @copilotkit/runtime:check-types`
- `pnpm nx build @copilotkit/runtime`
- `pnpm exec oxlint
packages/runtime/src/v2/runtime/handlers/intelligence/run.ts
packages/runtime/src/v2/runtime/__tests__/intelligence-lock-heartbeat.test.ts`
- `pnpm exec oxfmt --check
packages/runtime/src/v2/runtime/handlers/intelligence/run.ts
packages/runtime/src/v2/runtime/__tests__/intelligence-lock-heartbeat.test.ts`

The pre-commit hook also passed affected tests, `publint`, and `attw`.
The repo-wide `pnpm check-format` still reports 25 unrelated files
already present on `main`; both changed files pass the focused format
check.
Batching the Phase 0 authorization removed the per-goal confirmations that were
accidentally serving as decision points. Nothing then asked the developer for the
inputs the agent cannot legitimately choose, and Phase 1 still said "Enter a
Display name" in the imperative — so an autonomous run named the bot itself.

That name is the expensive one. The wizard derives the Channel Code from it, the
Code is what createChannel({ name }) declares and what the developer types as
/invite, and Slack bot names are workspace-wide — Phase 1 already warns that a
collision blocks the install. An agent that settles it has named someone's bot for
them and can fail the install doing it.

Phase 0 now gathers four decisions in one exchange before any browser opens: the
display name, the workspace, the test channel, and whether this is throwaway.
Phase 1 consumes the chosen name instead of inventing one, Phase 1's workspace step
uses the named workspace, and Phase 2's invite names the agreed channel and says
the developer runs it.

States the rule the whole design turns on: the decisions are inputs you cannot
invent, the authorization is permission you need once, and collapsing the second
does not license skipping the first.
…refs OSS-705) (#6370)

## Summary

Retesting the Slack setup end to end found the skill **too cautious to
be useful**. It stopped at almost every step, where the earlier version
stopped only for passwords and got through — and the run was rescued by
the developer telling the agent outright to just control their browser,
which cut human involvement down to typing passwords.

A run that pauses at every control is slower than the manual path it
replaced. Three changes, all pulling the same direction.

## 1. Driving is now the default, not implicit

The skill said *"most of this workflow happens in a browser"* —
descriptive, and it never told the agent to **drive** that browser. It
now does, explicitly, and checks its own capability before Phase 0
rather than assuming either way.

## 2. When there is no browser, ask for one — per harness

Generic advice is useless here, because enabling browser control differs
by harness. The agent now works out which one it is in and names the
single applicable route: Claude Code and Codex each ship their own
support and enable it differently, most other harnesses take a general
browser-use MCP server such as Playwright MCP. **If it isn't sure, it
looks it up rather than guessing.**

It also names the payoff — driving turns this into typing three secrets,
the fallback is roughly fifteen manual browser steps — because that is
what turns a shrug into a yes. The step-by-step walkthrough survives
only for an explicit decline.

## 3. Consent is batched into one authorization

Phase 0 now takes **one** yes naming the whole sequence: the Slack app
from the wizard manifest, its install into a named workspace, the
Channel, the adapter attach, and the project key.

Phase 3 and `references/intelligence-channel.md` previously required
*"state what you are about to change, get an explicit yes"* for
**every** dashboard goal — and the reference said it twice, back to
back. That is the concrete source of the stop-at-every-step behaviour.
Reading the page before acting stays required; it is no longer a reason
to check in.

## What did not change

The secret boundary. The developer still types the bot token, signing
secret, and API key themselves, and those remain the only mandatory
stops alongside anything the authorization did not cover. Batching
consent must not batch away a password — that is called out in the text.

`version` bumped 1.0.0 → 1.1.0.

## Validation

- `tsx scripts/sync-plugin-skills.ts --check` → **plugin skill mirror in
sync**
- `scripts/__tests__/sync-plugin-skills.test.ts` → 10 tests passed
- `prettier --check` clean on both changed files

## Companion

The same shift landed on the hosted big prompt in
[CopilotKit/website#445](CopilotKit/website#445)
— default to driving, one batched authorization, harness-aware
capability request. Both surfaces now say the same thing, which was the
point of keeping them in step.

Worth noting for reviewers: this takes Atai's side on *"where the skill
asks permission to proceed, just do it"*, which was only half-applied
before. It also reverses my own per-step confirmations from the first
pass on #445 — the dogfooding run is the reason.
@pull pull Bot locked and limited conversation to collaborators Aug 4, 2026
@pull pull Bot added the ⤵️ pull label Aug 4, 2026
@pull
pull Bot merged commit f1b42e2 into TheTechOddBug:main Aug 4, 2026
1 of 47 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants