Commit Graph

4771 Commits

Author SHA1 Message Date
Tyler Slaton 88fc3da497 fix(next): guard root bodies against extension hydration races 2026-08-05 11:25:13 -07:00
Maxim ab71913932 Merge branch 'main' into fix/in-memory-runner-bounding 2026-08-05 18:43:27 +02:00
Ran Shem Tov c2cb1c9e60 fix(showcase-mastra): render interactive open-gen-ui on a live endpoint
Both open-gen-ui cells reused the shared weatherAgent
("You are a helpful assistant.", gpt-4o). On a live LLM that produced
static HTML with no JS wiring - buttons rendered but were dead. aimock
hid it: fixtures match on userMessage/hasToolResult and replay a
fully-wired UI regardless of the agent.

Gold langgraph-python uses dedicated agents whose system prompt mandates
a single interactive generateSandboxedUi call and reads the design-skill
+ sandbox-function descriptors from copilotkit context. On Mastra,
RunAgentInput.context is a read-channel (requestContext.get("ag-ui")),
not injected into the prompt, so that guidance never reached the model.

Add dedicated openGenUiAgent / openGenUiAdvancedAgent porting gold's
prompts, with dynamic instructions that fold the ag-ui context into the
system prompt, and point the ogui route at them.

Verified live (real OpenAI, aimock bypassed) - all three advanced pills
render interactive, host-wired UI with confirmed round-trips:
evaluateExpression 7*8=56, notifyHost, evaluateExpression 5+3*2=11.
aimock matcher keys are untouched, so replay is unaffected.
2026-08-05 13:29:30 +03:00
Maxim c91376452a docs(runtime): document in-memory runner bounds, concurrency, and durability
Documents the now-bounded in-memory runner for users: the maxThreads /
maxRunsPerThread / maxBytes limits and their defaults, the precise eviction
model (LRU threads, per-thread run-cap, cross-thread byte ceiling enforced at
run completion), and the onConcurrentRun throw/supersede option. Clarifies that
the store is a process-global singleton shared by every runner, that dedup
weakens past the run cap, and points at the first-party SqliteAgentRunner for
durable or multi-instance deployments. Adds a troubleshooting entry for the
in-memory eviction warning.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 03:06:15 +02:00
Mark 80fa407978 chore(showcase): pin opentelemetry-api<1.44 for pydantic-ai v1
pydantic-ai 1.0.18 imports opentelemetry._events, removed in
opentelemetry-api 1.44.0. Its own floor is an unbounded
opentelemetry-api>=1.28.0, so a fresh resolve of this package's
requirements picks 1.44.0 and import pydantic_ai fails with
ModuleNotFoundError.

The agent image kept building because the Dockerfile copies only
requirements.txt before pip install, so that layer stayed cached from
before 1.44.0 shipped (2026-07-16). Any requirements change or cache
eviction would have surfaced it.

Verified with pip in a clean 3.12 venv, as the Dockerfile installs:
  before: opentelemetry-api 1.44.0, import pydantic_ai raises
  after:  opentelemetry-api 1.43.0, import pydantic_ai OK
2026-08-04 20:34:45 +00:00
David Sanchez 22108c0948 docs(pydantic-ai): constrain the direct dep, not the transitive one
Follows the maintainer's Correction #2 on issue 6363. An exact version in a
docs install command is the same rot as the starlette==0.45.3 pin it replaced:
it goes stale silently and nobody re-resolves prose. The 2.22.0 the docs shipped
was already a version behind current the day it was written.

- docs install lines use pydantic-ai-slim[ag-ui,openai]>=2,<3, which constrains
  the dep the pages actually care about and fails loudly at the v3 boundary
- ag-ui-protocol drops out of the docs lines entirely; no doc snippet imports
  ag_ui, so naming it there was the transitive-dep noise the correction is about
- starlette>=0.46.2 stays, because the v2 snippets import Starlette directly.
  A floor with no ceiling cannot force a downgrade, so it does not recreate the
  silent backtrack
- examples/showcases/pydantic-ai-todos moves to a range in pyproject.toml and
  relocks; the uv.lock is what reproduces
- examples/canvas/pydantic-ai keeps exact pins: it has no lockfile, so
  requirements.txt is its only reproducibility artifact

Smoke-tested the open question from the issue: starlette 1.x works on
pydantic-ai v2. All 8 doc pages pass on 2.23.0 + starlette 1.3.1 and on
2.23.0 + starlette 0.52.1, so Jordan's <1.0 guard can be dropped rather
than raised.
2026-08-04 12:08:20 -05:00
David Sanchez d19bd0f81c docs(pydantic-ai): port integration docs and demos to Pydantic AI v2
Agent.to_ag_ui(), AGUIApp and the pydantic_ai.ag_ui module were removed in
Pydantic AI v2. The docs installed pydantic-ai unpinned, so anyone following
the quickstart got 2.22.0 and failed first at dependency resolution
(starlette==0.45.3 conflicts with the >=0.46.2 the ag-ui extra needs) and then
at AttributeError.

- 8 doc pages under showcase/shell-docs .../integrations/pydantic-ai serve the
  agent from a Starlette route via AGUIAdapter.dispatch_request
- StateDeps moves from pydantic_ai.ag_ui to pydantic_ai.ui
- stateful snippets build StateDeps per request; dispatch_request writes the
  client's state into deps.state, so a shared instance leaks state between users
- install commands exact-pin pydantic-ai-slim==2.22.0 and ag-ui-protocol==0.1.19
- examples/canvas/pydantic-ai and examples/showcases/pydantic-ai-todos ported
  and pinned, todos relocked
- skills/copilotkit-integrations reference updated to the same shape

showcase/integrations/pydantic-ai is deliberately untouched; it is tracked
separately.
2026-08-04 10:51:17 -05:00
Benjamin Taylor a1d27ab017 feat(shell-docs): strip both Channels surfaces to one copy action
**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.
2026-08-04 09:58:54 -05:00
Benjamin Taylor 335b12a96a feat(shell-docs): point the Channels prompt at the hosted guide
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.
2026-08-04 08:22:16 -05:00
Benjamin Taylor afef22d316 feat(shell-docs): restore the prompt disclosure and put it on the docs 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.
2026-08-04 07:44:25 -05:00
Benjamin Taylor 4d5fd3a25e fix(shell-docs): stop a throwing analytics client from faking a copy 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.
2026-08-04 07:17:31 -05:00
Benjamin Taylor 22abe9c034 feat(shell-docs): lead with the copy action, drop the dangling instruction
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.
2026-08-03 21:43:12 -05:00
Benjamin Taylor 3a2af9ee47 feat(shell-docs): add activation impressions, keep the prompt in the 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.
2026-08-03 21:38:43 -05:00
Benjamin Taylor 55790bbdce feat(shell-docs): name the real onboarding skill slug
Points the entry point at setup-slack-channel, the slug the combined onboarding
prompt will occupy in skills/ once it lands from channels-sdk.
2026-08-03 21:11:39 -05:00
Benjamin Taylor e62d950248 feat(shell-docs): give the Channels entry point #6356's featured treatment
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.
2026-08-03 21:08:11 -05:00
Benjamin Taylor 7f493d2527 merge origin/codex/channels-prompt-visual-flare
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.
2026-08-03 20:56:03 -05:00
Sam Julien 7b8034c6d4 docs: highlight Channels setup prompts 2026-08-03 18:49:01 -07:00
Benjamin Taylor db1f877897 feat(shell-docs): route every Channels entry point at one onboarding 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.
2026-08-03 20:42:21 -05:00
Sam Julien 874e650651 docs(channels): add copyable onboarding prompts 2026-08-03 16:08:33 -07: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 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
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
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
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
Tyler Slaton dab3cde9a0 docs(channels): document Teams one-command setup 2026-08-02 16:23:02 -07: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 ff48022bd7 docs(channels): offer the CLI as a peer path to the wizard
The Intelligence Channel walkthrough presented the browser wizard as the only way
to create and configure a Channel. The CLI is now shown alongside it with the
tradeoff stated -- the wizard when you want to watch the Channel while you set it
up, the CLI when you want the configuration in the repository, reproducible on
another machine, or drivable by an agent.

The wizard walkthrough is unchanged and remains fully supported. Both paths
reconcile against the same server state, so either can finish what the other
started; that is stated explicitly, because a reader who has used one needs to
know the other is not a fork.
2026-08-01 14:49:43 -05:00
Mike Ryan 17e7f33876 fix(channels): reconcile identity stack integration 2026-08-01 09:26:39 -07:00
Mike Ryan 561bf19fa6 feat(channels): add explicit identity and memory grants 2026-08-01 09:19:13 -07:00
Tyler Slaton 5ceb53799b fix(channels): complete managed provider parity 2026-08-01 11:10:28 -04:00
Sam Julien ef9250531f feat(shell-docs): promote Channels in docs banner 2026-07-31 22:13:50 -07:00
Sam Julien fc86e66074 feat(shell-docs): add Channels activation strip 2026-07-31 14:11:57 -07:00
Sam Julien 133397191f test(shell-docs): align Channels lifecycle contract 2026-07-31 09:35:07 -07:00
Sam Julien 17572e85f0 docs(shell-docs): add coming-soon channel options 2026-07-31 09:32:23 -07:00
Benjamin Taylor 4d74bdc5c3 feat(runtime): auto-start managed Channels on long-running hosts (refs OSS-641)
Creating a Node listener or an Express handler now STARTS activation of the
runtime's declared managed Channels, so `channels.ready()` becomes
await-and-observe instead of the thing you must remember to call. A declared
Channel connects because it was declared.

The failure mode this removes: forget `ready()` and you get a process that
serves HTTP, looks healthy, and is silently disconnected with zero output.
Auto-start's worst case is an activation error in the logs.

The generic Fetch handler stays LAZY — it is the serverless/edge entry point,
where isolates freeze and recycle per request and separate cold starts would
mint competing listeners for the same Channel. `createCopilotHonoHandler` stays
lazy for the same reason: it is our Next.js App Router surface in practice
(every `examples/showcases/*` route handler builds one at module scope), and its
TSDoc now says so loudly. `activateChannels: false` remains the opt-out that
opens no socket.

Consequence for host code: the shutdown-handler boundary moves earlier. Signal
handlers must be registered before the listener is CREATED, not merely before
`ready()` — otherwise a Ctrl-C during the connect window hits Node's default
handler and leaks a live gateway session. The slack and teams examples and the
docs snippets are restructured accordingly.

Also migrates the seven channel-package README quickstarts off the generic
handler (a request handler a socket-mode bot constructs and never serves) onto
the Node listener, so they inherit auto-start and agree with the docs site.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:37:37 -05:00
Sam Julien 625bcd30cd docs(channels): clarify availability and self-hosting 2026-07-30 15:21:02 -07:00
Mike Ryan 6c7d411f9e test(channels): enforce gateway delivery docs 2026-07-30 12:29:03 -07:00
Mike Ryan e4a105a1c0 docs(channels): describe gateway delivery 2026-07-30 12:23:35 -07:00
Alem Tuzlak 88bef0e27a feat(channels-core): parallel-by-default turn concurrency
Overlapping turns on the same conversation now run concurrently by default
so multi-user Slack threads get parallel replies. Singleton agents are
isolated via clone() per run; store.concurrency serial/drop remain opt-in.
2026-07-30 19:10:18 +02:00
Alem Tuzlak d64ca464dd fix(channels): stream stop, provider error propagation, lock prefix
Stop Slack streams in finally; rethrow ChannelProviderDeliveryError from
postFile; treat delivery join failures as permanent; plumb lockKeyPrefix
into channel canonical locks; export resolveChannelActivationEnv and
treat blank env as unset; align deploy URL guidance.
2026-07-30 12:25:55 +02:00
Alem Tuzlak e2a5dc9219 fix(channels): seal packet path without blocking terminal recovery
Allow a failed/uncertain terminal after effect or complete-terminal push
failures; seal only after a successful terminal apply. Leave Phoenix child
channels on failed join, re-arm delivery handlers on restart, replay
onStateChange health, skip empty Teams stream deltas, and align docs/tests.
2026-07-30 12:09:32 +02:00
Alem Tuzlak 155d488bb7 fix(channels): harden delivery protocol and lock cleanup
Close packet path after permanent push/ack failures so a later effect
cannot mint a new effectId on the same seq. Refresh owner generation on
join_token reconnect, add reconnect backoff, require claimed on claim
assert, reject unknown turn kinds, skip empty Slack stream deltas, and
surface missing file-client attachments instead of dropping them.

Always release the product thread lock after a Channel canonical run.
Align connectTimeoutMs docs, projectId validation, ops error guidance,
and test fixtures with the delivery ID contract.

Note: local lefthook skipped (no node_modules in this worktree); CI will
validate. CR findings addressed from PR #6249 review.
2026-07-30 11:20:44 +02:00
Mike Ryan 8f166577ce feat(channels): replace live sessions with realtime boundary 2026-07-29 22:23:30 -07:00
Tyler Slaton 124f4e29ef fix(channels): expose managed tool status option 2026-07-29 18:14:25 -04:00
Sam Julien 580021f856 docs(channels): clarify managed setup journey 2026-07-29 11:46:06 -07:00
Jordan Ritter 8433f5b118 fix(showcase/ci): make the :latest tag monotonic so an older build cannot overwrite a newer one (#6174)
## The race, with evidence

Three PRs merged within 34 seconds on 2026-07-26. `Showcase: Build &
Push` has **no concurrency group** (deliberately), so all three ran
simultaneously and raced to push the same `:latest` tags.

| run | commit | PR | start → end |
|---|---|---|---|
| `30190815370` | `7b28934387` | #6162 | 06:18:05 → 06:29:44 |
| `30190823203` | `59f275eedc` | #6161 | 06:18:21 → 06:29:44 |
| `30190831480` | `db75a04837` | #6158 | 06:18:39 → **06:29:13** ←
newest, finished FIRST |

The newest commit finished first, so the older builds overwrote its
`:latest`. Per-service job completion times — older beating newer on
every shared slot:

| service | newer (`db75a04837`) | older (`59f275eedc`) | older won by |
|---|---|---|---|
| `shell-dashboard` | 06:25:33 | 06:25:34 | +1s |
| `showcase-harness` | 06:27:51 | 06:27:55 | +4s |
| `shell` | 06:27:17 | 06:27:28 | +11s |
| `shell-dojo` | 06:25:10 | 06:25:26 | +16s |

**All three runs reported `success`.** Staging served pre-#6158 code
while CI, the redeploy gate and deploy verification all looked clean.
Same failure class as #6171: a success that doesn't mean what it says.

## What I verified in YAML vs took on trust

Verified by reading the files / querying the API:

- **Tagging** — `showcase_build.yml` pushed `:latest` **and** `:${{
github.sha }}` in one `depot/build-push-action` step, in both the
`build` and `build-starters` matrices. **A per-commit sha tag already
existed**; confirmed in GHCR (`showcase-shell-dashboard` has digests
tagged `db75a04837…`, `59f275eedc…`, `d28384a2eb…`).
- **Nothing serialized the pushes.** No concurrency group; confirmed the
header comment states this is intentional.
- **Deploy consumes `:latest`** — `verify-railway-image-refs.ts` is the
SSOT assertion: staging is `ghcr.io/copilotkit/<repo>:latest` (mutable),
**prod is `ghcr.io/copilotkit/<repo>@sha256:<digest>` (already immutably
pinned)**. So this race is **staging-only**; prod was never exposed.
- **`Showcase: Verify Deploy` structurally cannot catch it.** It is a
health probe; it asserts no digest or commit provenance anywhere. Its
#6171 per-commit concurrency key is about *which run verifies*, not
*what image is running*. A stale-but-healthy service passes.
- **The racing runs build DISJOINT service sets** (see below) — I pulled
the actual job lists.

Taken on trust: nothing material. The issue description matched the API
on every point I checked.

## Why NOT a concurrency group

`detect-changes` builds a **per-push, path-filtered** matrix, so
concurrent runs build overlapping but **non-identical** service sets:

- `7b28934387` → ag2, agno, built-in-agent, claude-sdk-python,
claude-sdk-typescript, crewai-crews, langgraph-fastapi,
langgraph-python, langroid, llamaindex, mastra, pydantic-ai, shell-docs,
spring-ai, strands (**15**)
- `db75a04837` → crewai-crews, llamaindex, shell, shell-dashboard,
shell-docs, shell-dojo, showcase-harness (**7**)

`cancel-in-progress: true` would have cancelled the `7b28934387` run and
the ~10 services **only it builds would never have shipped** — trading a
stale-image bug for a never-shipped bug. Per [GitHub's
docs](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#concurrency),
`cancel-in-progress: false` is no better: *"Any previously pending job
or workflow in the concurrency group will be canceled"* — with three
rapid merges the **middle** commit's build is dropped outright. GitHub
also does not guarantee FIFO ordering of queued runs.

Concurrent runs here are **not redundant**, so they must not be
cancelled.

**Re the #6171 interaction:** this design does **not** use
`cancel-in-progress`, so no build is ever superseded-and-cancelled and
the cancelled-slot notifier is never tripped by this change. That
interaction stays theoretical — deliberately.

## The design

Make the one shared mutable resource monotonic instead of serializing
the fleet.

1. The build step pushes **only** the immutable `:<sha>` tag, plus an
`org.opencontainers.image.revision` label.
2. A guard resolves the commit behind the current `:latest`, asks GitHub
`compare/<theirs>...<ours>`, and advances `:latest` (registry-side
retag, no pull) **unless ours is `behind`** — i.e. `:latest` already
holds a descendant and moving it would roll staging back.

**Fails open by design.** No `:latest` yet, unlabelled legacy image,
unreachable API, diverged history → advance. A stuck `:latest` is the
very failure being fixed, so it declines only on *positive proof* of
regression.

**Placement:** in `redeploy-staging` / `redeploy-staging-starters`,
immediately before the Railway pull that consumes `:latest` — not per
build slot. Those jobs already have Node (build slots do **not**, so
per-slot would mean an unpinned `npx tsx` fetch on ~50 parallel
runners), and deciding right before the pull makes the window as narrow
as possible. The image list is the **same matrix ∩ build-success
intersection** that decides what gets redeployed, so a failed build can
never move a tag.

Also adds `showcase_build.yml` to `showcase_validate.yml`'s trigger
paths — the new test asserts against that file's live text, and without
the path a PR re-adding `:latest` would never run the test that catches
it.

## Tradeoffs / what stays open

- **Residual sub-second TOCTOU.** GHCR has no compare-and-swap on tags,
so two runs reading `:latest` simultaneously could still both advance.
This narrows the window from the whole build (~10 min) to inspect→retag.
Fully closing it means retiring the mutable staging tag and pinning
staging to digests the way prod already is — a change to the Railway
image-ref SSOT contract, not a workflow change. **Recommended
follow-up.**
- **The guard is inert for one build per image.** Today's `:latest`
images carry no labels (verified: `showcase-shell-dashboard:latest` has
no `Labels` at all), so the first post-merge build fails open and
advances unconditionally — same as today. Protection starts from the
second build of each image.
- Failure to retag exits non-zero, redding the redeploy job and stopping
the deploy. That is intended: redeploying against a tag that did not
move is exactly the silent false green being fixed.

## Proof

**Red/green on the live YAML.** `advance-latest-tag.test.ts` parses the
real `showcase_build.yml` (extending #6171's `redeploy-guard.test.ts`
pattern). Reverting the workflow to its pre-fix state: **14 failed / 23
passed**. With the fix: **37 passed**. Full `showcase/scripts` suite:
**2382 passed, 73 files**.

**The load-bearing predicate, verified live against the real incident
commits:**

```
compare/db75a04837...59f275eedc  => behind     (older run arriving late → DECLINE)
compare/59f275eedc...db75a04837  => ahead      (newer run → advance)
compare/db75a04837...db75a04837  => identical
```

**Label reading, verified against a real multi-platform registry image**
— `docker buildx imagetools inspect ghcr.io/astral-sh/uv:latest` piped
through `extractRevisionLabel()` returns
`3010295ae7ff572de459987ad70db315a62ecd61`, matching `jq` exactly. The
platform-keyed shape is handled.

**Shell/jq transforms** exercised directly, including the empty-CSV edge
case (empty → empty, step skipped by its `if:`).

**Lint:** `actionlint` finding counts byte-identical to the pre-change
baseline (no new findings; the 11 pre-existing are unrelated). `zizmor
--min-severity low` with the repo config: **no findings**.

**Typecheck:** both new files are in `showcase/scripts/tsconfig.json`'s
include set and produce **zero** errors. Worth stating plainly: `nx
run-many -t check-types` **does not reach `showcase/scripts`** — the
project isn't in the nx graph and has no `check-types` target (there are
9 pre-existing type errors in sibling files, which is how I confirmed
it). So the typecheck above is mine, not CI's. The *tests* are gated:
`showcase_validate.yml` runs bare `pnpm exec vitest run` in
`showcase/scripts`, which auto-discovers the new file.

### What I could NOT prove

**I did not construct a real concurrent race on scratch branches.**
Doing it faithfully needs two builds pushing the same GHCR repo with
controlled finish ordering, which means merging to `main` — the only
branch the build workflow triggers on. No run IDs for a live race
demonstration; I am not implying one.

Unproven until this runs on main: that `docker buildx imagetools create`
retags cleanly under the runner's GHCR credentials, and that `npx tsx`
behaves in the redeploy jobs (it is already the established invocation
there — `redeploy-env.ts` — so this is low risk, not zero).

## Normal single-merge builds are unaffected

No concurrency group is added, so nothing queues or cancels. A lone
merge finds `:latest` at its own parent → `ahead` → advances, exactly as
before. Cost is one `imagetools inspect` + one `gh api` + one
registry-side retag per built service, in a job that already exists — no
extra job, no extra checkout, no change to build parallelism.

---

Branched from `db75a04837`; #6156/#6159 landed after, so this will need
main merged in before it goes green.

Probable conflict with the concurrent `git lfs pull` work in
`showcase_validate.yml` — my edit there is only the top-level `on:
paths:` list, so it should merge cleanly, but flagging it.
2026-07-29 11:36:12 -07:00
Sam Julien 10b87af572 docs: organize Channels guides by provider and framework (#6193)
## Summary

- Make Slack and Microsoft Teams the production-ready Channels choices
in the top provider picker, with framework-aware routes under
`/slack/...` and `/teams/...`.
- Put ten task-oriented guides inside every provider/framework journey
and remove the standalone Channels overview from navigation.
- Restore the global Channels SDK reference at `/reference/channels`,
including 34 current core, UI, state, transcript, and direct-adapter
entries.
- Add the complete CopilotKit Intelligence setup walkthrough, product
screenshot, and a stable architecture-diagram slot that Mike's final
artwork can replace in place.
- Qualify Discord and WhatsApp correctly: their direct adapters already
ship, while managed Intelligence support is coming soon.

## Why

Developers should choose their chat provider and agent framework first,
then stay in that context while they build and operate the integration.
The previous structure mixed provider guides, an extra overview layer,
and stale provider-specific reference pages, making it hard to find the
supported path or understand which behavior was managed versus
developer-operated.

This update keeps the guide journey provider-specific while returning
API material to the normal global Reference surface. It also documents
operational boundaries that matter in production instead of adding pages
for their own sake.

## How

- Reuse provider-aware MDX across Slack, Teams, and all 19 public
agent-framework integrations; the built-in agent keeps the shorter route
without a framework segment.
- Organize the sidebar into Getting started, Build, Production, and API
reference with guides for Intelligence, tools, rich and interactive
messages, commands and reactions, files, state, persistence,
transcripts, and operations.
- Pin the verified `@copilotkit/channels@0.4.0` and
`@copilotkit/runtime@1.64.1` pair and align the copy with current SDK
source plus live Intelligence behavior.
- Document managed capabilities and provider-specific realities,
including active/standby runtimes, optional hosted endpoint defaults,
output-free turn finalization, Slack manifest scopes, Teams attachment
shapes and consent, delivery acceptance semantics, and durable state
requirements.
- Preserve useful direct-adapter discoverability for Slack, Teams,
Discord, Telegram, and WhatsApp without restoring obsolete symbol pages.
- Add one-hop redirects for retired routes and cover navigation,
framework selection, raw-doc URLs, search, reference discovery, and
sitemap output.

Validation:

- Full docs suite: 51 files / 348 tests
- Typecheck
- Lint with no errors (existing baseline warnings only)
- Production build: 222 static pages
- Live HTTP checks: Slack Intelligence, Teams + Mastra files, Slack rich
messages, and direct-adapter reference all return 200
- Independent read-only correctness passes against the current Channels
SDK, Runtime, Intelligence, and every public framework setup

Linear:
https://linear.app/copilotkit/issue/OSS-615/channels-sdk-documentation-audit
2026-07-29 08:50:10 -07:00
github-actions[bot] 66b6d3fef3 style: auto-fix formatting 2026-07-29 13:33:01 +00:00