Commit Graph

13442 Commits

Author SHA1 Message Date
Alem Tuzlak dff80a2a9a fix(bot,bot-slack,bot-intelligence,runtime): address managed-bots SDK review findings
Correctness:
- C1 create-bot: start() is now idempotent — a second start() no longer
  re-resolves the backend / rebuilds Transcripts+Telemetry+ActionRegistry or
  re-connects adapters (which would wipe MemoryStore state and double-bind real
  adapters). stop() clears the flag so start→stop→start is still a real restart.
- S1 bot-slack ingress: a threaded reply that @-mentions the bot is now skipped
  (app_mention handles it) so the managed path no longer double-responds. Matches
  both the plain <@U…> and labeled <@U…|handle> mention forms.
- S2 runtime: CopilotSseRuntime throws if `bots` is passed without intelligence
  instead of silently dropping them (guards a JS/as-any caller past the type).
- S3 bot-intelligence: startManagedBots rolls back — stops already-started bots —
  when a later bot fails to start, instead of leaking listeners/connections.

Lower:
- S4 ingress: stripMentions handles the labeled <@U…|handle> form; DM turns strip
  mentions too (parity with app_mention/thread_reply).
- S5 bot-intelligence: bot-name uniqueness is now case-insensitive.
- S6 runtime: fail fast at construction when a declared bot has no name (full
  shape/uniqueness validation stays at the activation seam — assertValidBotNames —
  because it can't cross into this CJS package from pure-ESM bot-intelligence).
- S7 bot-intelligence: buildActivationMetadata throws on a nameless bot instead of
  silently filtering it out of the activation set.
- S8 bot-intelligence: startManagedBots warns on an empty bots array.
- M1 intelligence-adapter: the per-turn egress seq Map entry is deleted after each
  turn so it can't grow unbounded over a long-running bot.
- M2 intelligence-adapter: an inbound file that fails to fetch degrades to a
  fail-visible text note instead of being silently dropped from model context.
- I2 contracts: dropped the now-dead `duplicate_skipped` RenderAccepted value
  (Intelligence returns duplicate_accepted or a 409 conflict).

Changelog (C2/C3, intended behavior after moving init into start()):
- bot.transcripts now throws before start() (was a concrete property).
- telemetry `oss.bot.configured` now fires at start() rather than construction, so
  a constructed-but-never-started bot no longer emits it.

Not addressed here (cross-repo, tracked on the Intelligence side):
- I1 realtime render-event kind:"file" clause on the gateway validator.
- I3 lease-token fencing on the render-accept path.
2026-07-08 19:18:19 +02:00
Jordan Ritter fe1ad53c44 fix(showcase): require positive integer for numeric config knobs (reject 0 and leading-zero/octal)
The _require_int validator in the langgraph-typescript and strands-typescript
entrypoints accepted '0' and leading-zero/octal forms like '010'/'08'. Operator
typos on any numeric knob then broke a guard:
- SIZE_THRESHOLD_MB=0 kills the agent on cycle 1 (instant restart loop)
- HEALTH_STRIKE_LIMIT=0 kills on first probe miss
- SIZE_CHECK_INTERVAL=0 / HEALTH_CHECK_INTERVAL=0 busy-spin on 'while sleep 0'
- '010' is read as OCTAL (8) in arithmetic; '08'/'09' abort under set -e

Tighten the predicate to accept only a positive integer with no leading zero
([1-9][0-9]*). Invalid values keep the existing fail-safe behavior: WARN and
fall back to the documented default. Helper stays byte-identical across both
files.
2026-07-08 10:09:15 -07:00
Jordan Ritter 3d1d0a4240 fix(showcase): validate all numeric config overrides and route every wrapped-PID kill through guarded tree-kill
CLASS 1 (guard silently disabled by a bad numeric override): add a reusable
_require_int validator and run it at startup over EVERY operator-overridable
numeric knob in both entrypoints (size threshold/interval, startup grace,
health-probe interval, strike limit). A non-integer/empty override now WARNs
and falls back to the documented default instead of breaking a sleep/loop/
arithmetic test. Closes instance #3 (LANGGRAPH_SIZE_CHECK_INTERVAL='60s'
killing the size-monitor loop on its first iteration).

CLASS 2 (wrapped-PID orphan + kill-0 footgun): route the cleanup() NEXTJS_PID
kill through _kill_agent_tree (it is process-sub-wrapped like the agent, so a
bare kill orphaned the real Next.js node server holding $PORT across redeploy).
Harden _kill_agent_tree and _agent_descendants to refuse a PID that is empty,
non-numeric, 0, or 1 (fail closed), making kill -9 0 / kill -9 1 structurally
impossible. Remove the ${AGENT_PID:-0} sentinel in the --check-size-once seam;
skip with a warning when AGENT_PID is unset instead of defaulting to 0.

Shared helper code kept byte-identical between the two entrypoints.
2026-07-08 09:59:43 -07:00
Tyler Slaton 97058dc00f docs(shell-docs): update Slack and Teams agent framing (#5789)
## Summary

- Backport the website messaging from CopilotKit/website#398 into the
shell-docs Slack and Microsoft Teams frontend pages.
- Replace the stale waitlist/managed-only framing with "get early
access" copy that presents CopilotKit Enterprise Intelligence as the
self-hosted or cloud-hosted production layer around the open source Bot
SDK.
- Frame Slack and Teams as frontends for agents built on any harness or
framework, while reserving production-layer terminology for CopilotKit
Enterprise Intelligence.
- Address browser review annotations on both pages: remove filler in the
opener, avoid setup-heavy lead copy, use "open source" without a hyphen,
add the full CopilotKit Enterprise Intelligence name to the CTA titles,
and keep CTA telemetry surfaces intact.
- Update the shell-docs nav test expectation so it matches the current
root IA, where Threads lives under Build Chat UIs rather than the
generated Intelligence Platform section.

## Validation

- `npm run lint` from `showcase/shell-docs` (passes with existing
warnings)
- `npm run typecheck` from `showcase/shell-docs`
- `npm run test` from `showcase/shell-docs`
- `npm run build` from `showcase/shell-docs`

## Notes

- Hydrated Git LFS assets locally with `git lfs pull` so the shell-docs
public asset tests could read real PNG bytes.
2026-07-08 09:54:30 -07:00
David McKay 5245634c62 chore(banking): add stop-demo.sh teardown companion to run-demo.sh (#5877)
## What

Adds `examples/showcases/banking/stop-demo.sh` — the teardown companion
to the existing `run-demo.sh`.

## Why

`run-demo.sh` detaches everything except the Next.js dev server:

- `docker compose up -d --wait` (detached stack)
- native Metal TEI via `nohup … & disown` (Apple Silicon only)
- `exec pnpm dev` (the only foreground process)

So Ctrl-C stops *only* the dev server and silently leaves the docker
stack (`banking-memory`) and the host embedder on `:7067` running. There
was no one-command way to bring those down. This script fills that gap
and mirrors `run-demo.sh`'s conventions (same `say`/`ok` helpers, same
header-comment style, idempotent).

## What it does

Tears down, idempotently, in order:

1. Next.js dev server on `:3000` (defensive — usually already gone via
Ctrl-C)
2. docker compose stack (project `banking-memory`), **containers only**
by default so a re-run of `run-demo.sh` reuses the built composite image
+ seeded Postgres
3. native Metal TEI on `:7067` (Apple Silicon; the host process docker
doesn't manage) — SIGTERM, then SIGKILL for anything that ignores it

## Flags

- `--purge` — also delete the docker volumes (postgres/redis/minio/tei
model cache) for a full clean-slate reset
- `--keep-tei` — leave the slow-to-warm native embedder running when
only bouncing the stack

## Testing

- `bash -n stop-demo.sh` — syntax clean
- `shellcheck stop-demo.sh` — clean, no warnings
- `./stop-demo.sh --help` renders the banner correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 09:53:44 -07:00
Ben Taylor 04461409e5 fix(bot): move runStateStoreConformance to @copilotkit/bot/testing subpath (#5875)
## Problem

`@copilotkit/bot`'s package entry re-exports `runStateStoreConformance`
from `./testing/state-store-conformance`, which does `import { describe,
it, expect, ... } from "vitest"` at module top-level. In ESM a static
re-export **eagerly evaluates** the re-exported module, so a plain:

```ts
import { createBot } from "@copilotkit/bot";
```

drags `vitest` into the consumer's runtime module graph and throws
`ERR_MODULE_NOT_FOUND: Cannot find package 'vitest'` for any consumer
that doesn't have vitest installed (i.e. every production consumer).
`vitest` is only a devDependency.

Surfaced while smoke-testing the package rename (OSS-438) — but it's a
pre-existing bug on `main`, independent of that rename.

## Fix

- **Drop the re-export from `src/index.ts`** → the package entry is now
vitest-free.
- **Publish the helper under a `./testing` subpath**
(`@copilotkit/bot/testing`) — test tooling lives off the runtime entry,
the standard pattern.
- **Declare `vitest` as an optional `peerDependency`** so consumers of
`/testing` get the right signal.
- **Docs** updated to `import { runStateStoreConformance } from
"@copilotkit/bot/testing"`.

Only the import path of the test-only conformance helper changes; the
runtime API is untouched.

## Verification
- Static import trace: the entry graph is 12 runtime modules, **none**
import vitest; every vitest importer is a `.test.js` (not in the graph)
or `testing/state-store-conformance.js` (only reachable via `/testing`).
- `@copilotkit/bot` builds; **143/143** tests pass; `publint` + `attw`
clean (the internal conformance test imports the helper by relative
path, unaffected).

## Coordination
Touches `packages/bot` on `main`. The OSS-438 rename PR (#5849) renames
this package to `@copilotkit/channels`; that PR re-derives from `main`
before merge, so it will absorb this fix automatically. If #5849 merges
first, this rebases onto `packages/channels` mechanically.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 11:52:49 -05:00
Maxim 95430de51a chore(banking): add stop-demo.sh teardown companion to run-demo.sh
run-demo.sh detaches everything except the Next.js dev server (docker
compose up -d, native Metal TEI via nohup/disown, then exec pnpm dev), so
Ctrl-C on the dev server leaves the docker stack and the host embedder
running. stop-demo.sh brings those leftovers down in one command.

Tears down, idempotently:
  - the Next.js dev server on :3000 (defensive; usually gone via Ctrl-C)
  - the docker compose stack (project banking-memory), containers only by
    default so a re-run reuses the built image + seeded data
  - the native Metal TEI on :7067 (Apple Silicon; the host process docker
    doesn't manage), SIGTERM then SIGKILL

Flags: --purge also drops volumes for a clean slate; --keep-tei leaves the
slow-to-warm embedder running when only bouncing the stack.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 18:49:34 +02:00
Benjamin Taylor 224587101f fix(bot): move runStateStoreConformance to @copilotkit/bot/testing subpath
The package entry (@copilotkit/bot) re-exported runStateStoreConformance from
./testing/state-store-conformance, which imports vitest at module top-level.
An ESM re-export eagerly evaluates that module, so a bare
`import { createBot } from "@copilotkit/bot"` dragged vitest into every
consumer's runtime graph and threw ERR_MODULE_NOT_FOUND when vitest wasn't
installed (i.e. any production consumer).

- Drop the re-export from src/index.ts (entry is now vitest-free)
- Publish the conformance helper under the ./testing export subpath
- Declare vitest as an optional peerDependency (documents the /testing need)
- Update docs to import from @copilotkit/bot/testing

Names/behavior of the runtime API are unchanged; only the import path for the
test-only conformance helper moves.
2026-07-08 11:44:01 -05:00
Mike Ryan 25339b0d07 chore: release monorepo v1.62.3 (#5876)
## Release monorepo v1.62.3

**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.62.3`
   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.62.3`
   - Creates git tag `monorepo/v1.62.3`
   - 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.62.3
2026-07-08 09:35:28 -07:00
Jordan Ritter ed44611263 fix(showcase): capture wait -n exit code under set -e so restart diagnostics aren't dead code
Both entrypoints run under set -e. The tail `wait -n $AGENT_PID $NEXTJS_PID`
returns non-zero on the PRIMARY designed exit path (137 = size-gate/watchdog
SIGKILL of the agent tree, or an agent crash), so set -e aborted the script AT
that line — making EXIT_CODE=$?, the entire 'which process exited with code N'
diagnostic, and the final `exit $EXIT_CODE` dead code on exactly the
interesting exits. Capture the code with `EXIT_CODE=0; wait -n ... || EXIT_CODE=$?`
so the diagnostic and explicit exit run and preserve the exact code (incl. 137);
the container-restart path is unchanged.

Same class: langgraph's LANGGRAPH_SIZE_THRESHOLD_MB was used in
`[ "$DIR_SIZE_MB" -ge "$threshold" ]` with no numericity guard, so a
non-integer operator override made the test error and silently no-op the size
gate every cycle. Validate the threshold the same way DIR_SIZE_MB already is
(numeric case guard + 'size guard inactive' WARNING, then skip safely).
2026-07-08 09:25:04 -07:00
tylerslaton 4394f9c81d chore: release monorepo v1.62.3 2026-07-08 16:17:36 +00:00
Alem Tuzlak 2330dca267 fix(bot-slack,runtime): align tests with renderer status + AbstractAgent.run
Two pre-existing test failures on this branch, surfaced by CI's unit +
check-types jobs once main was merged:

- bot-slack event-renderer: the non-pane thread tool-call test still
  asserted the old "no composer status" behavior. Commit 13248dda0b
  deliberately drove setStatus on ANY thread anchor (not just panes), so
  the test now expects both the 🔧 row and the "is using…" status.
- runtime in-memory-runner: HangingAgent/AbortableAgent extended
  AbstractAgent but omitted the abstract run() member (@ag-ui/client
  0.0.57), failing tsc on the test tsconfig (TS2515). Add the same
  run() => EMPTY stub the sibling test agents use.
2026-07-08 18:15:29 +02:00
Jordan Ritter 5c5e139484 fix(showcase/strands-typescript): add startup-grace window to health watchdog for parity with langgraph
The health-watchdog armed its 3-strike/~90s kill counter immediately with no
startup-grace window. langgraph-typescript has a 180s grace precisely to keep a
slow cold start from being killed mid-boot into a restart loop. Now that the
tree-kill makes the strands kill effective (the orphan bug previously made it
cosmetic), a slow tsx cold start (>90s) would be genuinely killed and loop.

Port langgraph's grace mechanism verbatim (GRACE=180, no env override, poll
every 5s, exit 0 on agent death during startup, arm anyway if grace elapses),
adapted to strands' :8000/health probe.
2026-07-08 09:13:48 -07:00
Jordan Ritter 31a9d072e8 fix(showcase/langgraph-typescript): harden size-watchdog against non-numeric du and transient errors
Two tightly-related defects in the size-gated restart machinery in
entrypoint.sh:

1. _watchdog_check_size_once validated the du/awk result only for
   emptiness, not numericity. A non-integer value (junk du output, a
   transient read error, a test-seam stub) reached the
   `[ "$DIR_SIZE_MB" -ge ... ]` comparison and threw "integer expression
   expected"; sitting inside an `if`, set -e was suppressed so the test
   evaluated false and the size gate was SILENTLY skipped with no
   warning (unlike the empty-string branch). Now match ^[0-9]+$ via a
   case and emit the same "size guard inactive" WARNING, so the gate can
   never silently disappear.

2. The size sub-loop used `_watchdog_check_size_once || break`, treating
   ANY non-zero (including a transient check error) as a kill and
   permanently ending the monitor for the container's lifetime. Now
   break ONLY on the real-kill signal (rc==1) — preserving the
   kill -> wait -n -> container-restart -> boot-purge contract — while a
   transient non-zero keeps the monitor live and re-checks next cycle.

Verified RED->GREEN against the real entrypoint in node:22-slim with a
stubbed du seam: non-numeric du now warns and keeps the gate active; a
transient error no longer permanently disables the loop.
2026-07-08 09:13:43 -07:00
Jordan Ritter 31a4377853 fix(showcase): bounded re-scan in _kill_agent_tree so mid-walk forks can't escape
The tree-kill enumerated agent descendants in a single /proc snapshot then
killed. A child that forks a new child (or reparents) between the scan and the
kill escaped the walk, reparented to PID 1, and kept the agent port bound —
defeating the tree-kill's whole purpose of freeing the port before the
container restart.

Replace the single snapshot with a BOUNDED re-scan loop: keep the root alive as
the walk anchor, re-enumerate and SIGKILL live descendants deepest-first each
pass (up to 5 passes, 0.2s apart) until a scan comes back empty, then kill the
root last. Killing the root FIRST would immediately reparent every descendant to
PID 1 and make them unreachable by the root-anchored PPID walk, so root-last is
required for the re-scan to reap late/mid-walk descendants. A descendant that
fully daemonizes (double-fork to PID 1) before we reach it remains out of reach
— documented as an inherent limit of PPID-based reaping without job control; the
agent's npm->node tree does not daemonize.

Also document why the ${stat##*) } PPID parse is safe against a comm containing
") " (longest-prefix to the last ") " always lands on the true terminator).

Applied identically to langgraph-typescript (:8123) and strands-typescript
(:8000). Proven via local RED-GREEN in node:22-slim: pre-fix leaks a mid-walk
escapee (port stays bound), post-fix reaps it (0 orphans, port freed).
2026-07-08 09:13:38 -07:00
Jordan Ritter a5f082f50c fix(showcase): tree-kill agent in cleanup() EXIT trap to prevent shutdown orphan
The cleanup() EXIT/SIGTERM trap in both langgraph-typescript and
strands-typescript entrypoints did a bare `kill $AGENT_PID`. Because
$AGENT_PID is the outer process-substitution subshell (not the real
npm->node server), this reaped only the subshell and orphaned the node
server (reparented to PID 1, still holding :8123 / :8000) on every
graceful/SIGTERM shutdown -- e.g. every Railway redeploy/rollover.

Route cleanup() through the existing _kill_agent_tree helper (as the
size-watchdog and health-strike kill sites already do), and move the
_agent_descendants/_kill_agent_tree helpers above cleanup()/the trap so
they are defined whenever the trap can first fire.
2026-07-08 09:11:29 -07:00
Sam Julien 39a86447db docs(shell-docs): update Slack and Teams agent messaging 2026-07-08 09:04:47 -07:00
Benjamin Taylor 71a4ac42e4 Merge origin/main into alem/oss-360-sdk-foundations
Brings the 499-commit-stale foundations branch up to date with main so #5761
has a clean diff and no stale reverts (e.g. forwardHeaders). Conflicts:
- CopilotThreadsDrawer.tsx: took main's (main renamed CopilotDrawer -> ThreadsDrawer
  + added the collapse feature; the branch's edit was a no-op import-type split).
- pnpm-lock.yaml: regenerated with the pinned pnpm 10.33.4 (adds @copilotkit/bot-intelligence).
2026-07-08 11:01:58 -05:00
Jordan Ritter 93f2abeeb5 fix(showcase/strands-typescript): tree-kill agent so health-strike restart actually fires
strands-typescript/entrypoint.sh carries the identical latent trap fixed in
langgraph-typescript by this branch. The agent is launched through a process
substitution — `cd /app/src/agent && npm start &> >(awk …) &` — so
$AGENT_PID (=$!) is the outer subshell wrapping that pipeline, NOT the npm→node
tree it forks (`npm start` runs `node --import tsx server.ts`, which stays a
child of npm, not an exec-replacement). The health-strike `kill -9 $AGENT_PID`
therefore reaps only the subshell; npm and node reparent to PID 1 and KEEP
RUNNING, still bound to :8000. `wait -n` never observes the real server die →
container never restarts → the frontend proxies to a dead-but-not-restarted
agent forever (edge 502s). strands-typescript has no size-gate, so this only
fires after the 3-strike (~90s) health counter exhausts, but it is a real
latent footgun with the same root cause.

Fix: reuse the /proc-based `_kill_agent_tree` helper (node:22-slim ships
neither ps nor pgrep, and job control is off so a group kill would take out
the whole entrypoint) at the single health-strike kill site. The whole
npm→node tree now dies, :8000 is freed, `wait -n` returns and the container
restarts.

Red-green proven in a real node:22-slim container against the exact
process-sub → npm → node structure: RED (bare kill -9 $AGENT_PID) leaves node
orphaned and :8000 still LISTENing; GREEN (_kill_agent_tree) reaps the tree,
0 orphans, port freed.
2026-07-08 08:51:15 -07:00
Jordan Ritter e9284090c1 fix(showcase/langgraph-typescript): tree-kill agent so size-watchdog restart actually fires
The size-gated watchdog added in ef103f5f5 does `kill -9 $AGENT_PID`
expecting the container to exit and Railway to restart (re-running the
boot-purge). But $AGENT_PID is the process-substitution subshell wrapping
`cd /app/src/agent && npm start &> >(awk …)`, NOT the npm→node tree it
forks. A single-PID kill reaps only that subshell; npm and node reparent
to PID 1 and keep running, still bound to :8123 with the bloated in-memory
state resident. The frontend then proxies to a dead-but-not-restarted
agent and the edge 502s forever.

Fix: add a /proc-based `_kill_agent_tree` (node:22-slim ships neither ps
nor pgrep, and job control is off so a group kill would take out the whole
entrypoint) and use it at both watchdog kill sites (size gate + health
strikes). The whole npm→node tree now dies, :8123 is freed, `wait -n`
returns, the container restarts and re-runs boot-purge — preserving the
original RangeError-prevention intent.
2026-07-08 08:46:57 -07:00
Ben Taylor e283629046 docs: CopilotThreadsDrawer guide + Threads updates + React reference (closes ENT-1021) (#5780)
Documents the new **CopilotDrawer** prebuilt threads drawer (component
PR #5746).

## Changes
- **Guide** — `docs/prebuilt-components/copilot-drawer.mdx` (new) +
registered in `prebuilt-components/meta.json`. React drop-in
(`<CopilotDrawer />` beside `<CopilotChat />`, zero active-thread
wiring), props table, customization (slots / `::part`s /
`--cpk-drawer-*` tokens), Angular usage, license.
- **Threads how-to** — `snippets/shared/threads/threads.mdx` (shared →
propagates to ~10 integration threads pages + `/threads`): a callout
recommending `CopilotDrawer` as the prebuilt path and framing the
`useThreads` steps as the **headless** alternative (kept, not removed) +
Next-steps cross-links.
- **Reference** — `reference/components/CopilotDrawer.mdx` (React) and
`reference/angular/components/CopilotDrawer.mdx` (Angular),
hand-authored with `<PropertyReference>`.

## Validation
`npm run build` in `showcase/shell-docs` is green locally — compiled
successfully, 215/215 static pages generated, no MDX errors. All
internal links verified to resolve on `main`.

## Why draft / release-gated
Merges **with or after** the drawer release
(`@copilotkit/web-components` published +
`react-core`/`@copilotkit/angular` > 1.61.2 containing `CopilotDrawer`).
Until then the documented imports don't exist in a published package.

## Follow-ups (out of scope)
- Live `<InlineDemo>` of the drawer (needs a demo-registry entry) — uses
plain code blocks for now.
- Angular `injectThreads` / `CopilotChatConfiguration` reference pages
(named without links here; they don't exist on `main` yet).

Spec: https://app.notion.com/p/38f3aa381852814e8884e3153d3d4688 ·
Tracking: ENT-1021

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 10:43:42 -05:00
Alem Tuzlak d541d900f4 feat(bot): SDK realtime render-event streaming + Bolt-free Slack renderer (OSS-402 / OSS-403) (#5786)
Stacked on #5761 (base: `alem/oss-360-sdk-foundations`). The SDK side of
the Hosted Bots **realtime** path, per the source-of-truth Notion plan
and the OSS-395 frozen contract.

## What this delivers

### OSS-403 — extract a Bolt-free Slack renderer
(`@copilotkit/bot-slack/render`)
`createRunRenderer` no longer takes a `WebClient`; it takes an injected
`SlackRenderTransport` (the 3 credentialed ops it actually calls —
`setStatus` / `postMessage` / `updateMessage`; the 4 native streaming
ops were already injected via `NativeStreamTransport`). New `./render`
subpath export exposes the transport-agnostic renderer plus the
already-pure IR→Block Kit / modal / mrkdwn helpers, so the gateway-side
**Connector Outbox (OSS-404)** can drive the *identical* renderer for
managed replies — 1:1 UX parity without forking. The native Slack
adapter wraps its own `WebClient` into the transport.
- **Zero behavior change** to the native bot — 272 `bot-slack` tests
pass, check-types green.

### OSS-402 — stream RenderEvents over the realtime gateway
The managed adapter (`@copilotkit/bot-intelligence`) now mints semantic
render frames (`run_started` / `text_delta` / `text_end` / `tool_start`
/ `tool_end` / `interrupt` / `run_error` / `finalize`) and streams them
through a new `RenderEventSink`, assigning a monotonic `seq` per
`(turnId, slot)` and awaiting a durable `render_accepted` receipt for
each. Ordering is guaranteed by a serial chain with `seq` fixed at
enqueue time (so it holds regardless of AG-UI callback scheduling); a
rejected push surfaces at drain and nacks the delivery.

`PhoenixRealtimeTransport` (implements `DeliverySource` +
`RenderEventSink`) speaks the frozen OSS-395 contract exactly:
`render_event` frames → `render_accepted` receipts, then
`delivery.complete_requested` (completion **intent**, carrying
`acceptedThrough` high-water pointers) — **never** a committed
`delivery.ack` (app-api owns ack), and **no Slack credentials** in the
SDK. The Phoenix `Socket`/`Channel` boilerplate is expressed against a
minimal injected `HostedBotChannel` so the protocol is fully unit-tested
with a fake channel.

When no realtime sink is wired, the renderer falls back to translating
frames into `post` ops on the `EgressSink`, so the existing HTTP demo
keeps posting plain-text replies.

## Tests
- `@copilotkit/bot-slack`: 272 pass (native renderer behavior preserved
through the transport swap).
- `@copilotkit/bot-intelligence`: 43 pass (37 baseline + 6 new) — frame
ordering + `turnId:slot:seq`, completion-after-receipt, and the
**never-self-ack** invariant.
- `@copilotkit/runtime` check-types green (fixes the pre-existing
`get-runtime-info` `bots` red flagged on #5761).

## Follow-ups (not in this PR)
- **Gateway `complete_requested` handler (Intelligence #466):**
`sdk_channel.ex` currently routes `render_event` / `ack` / `fail` but
not `complete_requested`; it needs a `handle_in` clause +
`app_api_client.complete_delivery` + a matching app-api completion
route. Coordinating with @TylerSlaton since it's his file / shared
branch (no local Elixir toolchain to verify here).
- **OSS-404 Connector Outbox** consumes `@copilotkit/bot-slack/render`
to render frames → real Slack messages; end-to-end realtime parity isn't
demoable until it lands.
- Discrete `EgressSink` posts (command/interaction replies) →
`post`/`update` render frames (needs a per-`(turn, slot)` seq shared
with the run renderer).
2026-07-08 17:15:16 +02:00
Ben Taylor 2b5f856692 feat(suggestions): stateless suggestion generation via /suggest (ENT-1018) (#5799)
> **Update (SSE rework — addresses review):** `/suggest` now **streams
AG-UI SSE** instead of buffering a JSON `{ messages }` response, and the
run **forwards the consumer's messages + state**. Server-side it reuses
`createSseEventResponse` (the runner's event pipeline minus
`GLOBAL_STORE` persistence, `captureTelemetry:false`); client-side it
drives a stock `HttpAgent` at `/agent/:id/suggest`, so chips fill in
progressively via `onMessagesChanged` and the run never touches the
Intelligence websocket delegate. The stateless and fallback paths now
share one `runAgent` flow (net −68 LOC of production code). The `## How`
/ behavioral notes below are updated to match.

## What & why

Dynamic suggestions were implemented as a **full shadow-thread agent
run** (`SuggestionEngine.generateSuggestions` clones the provider agent
onto a throwaway `threadId` and calls `runAgent`). Against a
thread-persisting backend (CopilotKit Intelligence) every reload
materialized a **listed, auto-named thread** (plus gateway events + a
run lock), flooding the thread drawer. Root cause is the design: a
suggestion is a **stateless structured completion**, not a persisted
conversation.

This makes it one. A suggestion now runs the provider agent **directly**
behind a dedicated `POST /agent/:agentId/suggest` handler — no thread,
lock, gateway, runner-store, or name-gen — for **all**
CopilotKit-runtime backends. The debris is structurally impossible
rather than hidden.

## How

- **`get-runtime-info`** advertises a `suggestions` capability on `GET
/info` (`RuntimeInfo.suggestions`).
- **`handleSuggestAgent`** (new) runs the resolved provider agent
directly and **streams its AG-UI events as SSE** via
`createSseEventResponse` — the runner's event pipeline
(`agent.runAgent({ onEvent })` + `finalizeRunEvents`) **minus** the
`GLOBAL_STORE` persistence that backs SSE-mode thread listings.
`captureTelemetry:false` and no debug bus keep suggestions out of run
telemetry/the inspector. Header-forwarding only; no middleware.
- **Router**: `POST /agent/:agentId/suggest` wired through
`fetch-router`/`fetch-handler` (multi- and single-route), with
`assertNever` exhaustiveness guards.
- **core `SuggestionEngine`**: when the runtime advertises `suggestions`
(and transport isn't single-route), it seeds a stock `HttpAgent`
(pointed at `/agent/:id/suggest`, credentials via a fetch wrapper) with
the consumer's deep-cloned messages + state and `runAgent`s it — a plain
`HttpAgent` only speaks REST SSE, so it never routes through the
Intelligence websocket delegate. Otherwise it falls back to the
clone+`runAgent` path. Both paths share one flow; abort-aware; failures
logged.

## Testing

Unit + integration across both packages (**504 core + 1587 runtime
green**, `check-types` clean): the **no-thread-leak** proof (real
`InMemoryAgentRunner`, asserts the suggest thread never appears in the
live listing), progressive-streaming + state-forwarding assertions,
cross-mode SSE handler behavior, error/abort robustness (Safari
`DOMException` + undici non-`AbortError`), header/credential forwarding,
multi-route `200` + `GET→405`, and single-route/`suggestions:false`
fallback.

Validated end-to-end against a running runtime + real `CopilotKitCore`
client over HTTP (local built packages):
- **In-memory managed threads:** a normal `/run` creates exactly 1
listed thread; **4 dynamic-suggestion reloads add 0**. Chips stream
progressively (`onSuggestionsChanged` 0→1→2); the `/suggest` request
carries the consumer's state.
- **Live hosted CopilotKit Intelligence** (`mode: "intelligence"`, real
gateway): `/info` advertises the capability, suggestions stream via the
stateless path, and the **hosted thread listing is byte-identical
(50→50) after 6 reloads** — the drawer's actual data source is
untouched.

## Compatibility

**Migrations:** none. No schema/DB change; self-hosted deployments need
no coordinated update — the change is entirely in `@copilotkit/core` +
`@copilotkit/runtime`. No release flag.

**Additive + capability-gated — version skew is safe both directions:**
- **New core + old runtime** (no `/suggest`, capability absent):
`core.suggestions` is `undefined` → client uses today's clone+`runAgent`
path.
- **Old core + new runtime**: the new optional `RuntimeInfo.suggestions`
field is ignored; the `/suggest` endpoint sits unused.
- **New + new**: stateless `/suggest`.

`RuntimeInfo.suggestions` is a new **optional** field and the
`/agent/:agentId/suggest` route (+ `RouteInfo`/`METHOD_NAMES` entries)
are purely additive. No exported symbol was removed or renamed.

**Behavioral notes (matched new versions):**
- Dynamic suggestions **stream progressively** over SSE (chips fill in
as the provider emits them), matching the pre-`/suggest` UX.
- Suggestion runs no longer flow through the run handler, so they create
**no thread** (the fix) and won't appear in `oss.runtime.*` run
telemetry.
- Single-route transport and non-CopilotKit AG-UI backends keep the
clone+`runAgent` fallback.

**⚠️ Deploy note:** a reverse proxy that path-allowlists CopilotKit
routes must add `POST /agent/:agentId/suggest`. If the capability is
advertised but that path is blocked, suggestions silently no-op — the
client does **not** fall back on a `/suggest` error (by design, to avoid
re-introducing the thread flood).

## Review

Landed through a full CR loop (3 rounds / 21 agent-reviews + a
bucket-(c) promotion audit) — bucket (a) converged to zero. Known
pre-existing follow-ups tracked separately (out of scope):
`resolveLicenseStatus` grace-period ordering and
`AgentRegistry.getAgent` using `in` vs `hasOwnProperty` for reserved
ids.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-08 08:47:31 -05:00
Alem Tuzlak 7e74416951 feat(bot-teams): add ./render export for managed reuse 2026-07-08 13:48:25 +02:00
QA Agent 2b0983b1fa docs: add zodState and proper node context to Deep Agents state rendering
- Add zodState import and wrapper in TypeScript state schema example
- Show state emission as chatNode function with proper signature
- Align Deep Agents pattern with LangGraph state rendering docs
- Fixes FAC-63: ensures custom state properties appear in STATE_SNAPSHOT events
2026-07-07 18:53:45 -07:00
QA Agent 5ccb64b679 docs(deep-agents): add missing zodState wrapper in shared-state TypeScript examples
- Add zodState import to in-app-agent-read.mdx and in-app-agent-write.mdx
- Wrap language field with zodState() to ensure it appears in AG-UI and useAgent state
- Matches pattern used in predictive-state-updates.mdx

Fixes: FAC-48
2026-07-07 18:12:36 -07:00
QA Agent 2249efa114 docs(showcase): fix Python state streaming examples in Deep Agents and LangGraph docs
- Replace self.state with state in manual predictive state updates example
- Add missing colon in step_progress_tool function definition
- Align Python examples with working code patterns from examples/v1/travel/agent

Fixes FAC-53
2026-07-07 17:50:53 -07:00
QA Agent 42f611794c docs(deepagents): add complete custom graph example with StateGraph setup
- Replace createMiddleware pattern with Annotation.Root for custom graphs
- Add complete chatNode function showing copilotkitEmitState usage
- Include full StateGraph setup: creation, node addition, edges, compilation
- Add explanatory callout distinguishing custom graphs from prebuilt agents
- Resolves FAC-52
2026-07-07 17:17:32 -07:00
Benjamin Taylor 3efa375afb docs(drawer): re-capture preview with the real default components (#5780 review)
The prior preview paired the real <copilotkit-threads-drawer> with a hand-coded
mock chat panel (from the screenshot harness), so the chat half wasn't an actual
CopilotKit component — which is what the reviewer flagged. Re-captured with the
real default <CopilotThreadsDrawer> + <CopilotChat> together, stock (untheme d)
light styling, showing a thread's replayed conversation.
2026-07-07 17:54:26 -05:00
Benjamin Taylor 5994bfe482 Merge origin/main into ben1/ent-1018-stateless-suggestions
Syncs the branch with main (304 commits) to resolve CI type-check failure.
main changed extractForwardableHeaders to require a forwarding policy and
added the mergeForwardableHeaders helper (#5712); handle-suggest now uses
mergeForwardableHeaders(agent.headers, request, runtime.forwardHeadersPolicy ??
resolveForwardHeadersPolicy(undefined)) to match the run handler — fixing the
drift and adopting the server-headers-win / infra-header denylist behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:40:50 -05:00
Benjamin Taylor 9f938e89cf refactor(suggestions): stream stateless /suggest over SSE and forward consumer state
Rework the stateless /suggest transport to reuse the AG-UI SSE pipeline
instead of a buffered JSON response, resolving the streaming + state review
feedback:

- server runs the provider agent directly and streams its events via
  createSseEventResponse (the runner's event pipeline minus GLOBAL_STORE
  persistence), gated with captureTelemetry:false so suggestions stay out of
  run telemetry
- client drives a stock HttpAgent against /agent/:id/suggest, so chips stream
  progressively via onMessagesChanged and the run never routes through the
  Intelligence websocket delegate (still no thread persistence)
- forward the consumer's deep-cloned messages + state onto the suggestion run
  (was state: {}), matching the clone fallback

Net -68 LOC of production code; the stateless and fallback paths now share one
runAgent flow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:24:46 -05:00
Benjamin Taylor af10862e12 docs(threads): retitle /threads to 'Headless Threads' + surface CopilotThreadsDrawer on prebuilt pages (#5780 review)
- /threads page title -> 'Headless Threads' to distinguish the headless
  useThreads path from the prebuilt drawer (slug kept, so no inbound links
  break; sidebar label follows the frontmatter title). (samjulien #9)
- prebuilt-components index: the 'saved conversations' line now leads with the
  drop-in CopilotThreadsDrawer and offers Headless Threads as the DIY path, plus
  a companion-sidebar mention in 'Pick a surface' — the drawer was absent from
  the prebuilt landing page.
- chat page: same, point at the prebuilt drawer first, headless second.
2026-07-07 17:04:28 -05:00
Benjamin Taylor 3c174edcfd docs(drawer): rename is headless-only, complete the CSS parts list, clarify the zero-wiring line (#5780 review)
- Remove 'rename' from the prebuilt CopilotThreadsDrawer capability claims
  (guide, React reference, shared Threads callout) — the row kebab only does
  archive/unarchive + delete. Add an explicit note that rename is available via
  the headless useThreads path. (MikeRyanDev)
- Reference CSS parts list now matches the shipped element: adds row/row-active,
  collapse-toggle, close-toggle, backdrop, launcher-cluster, launcher-new-thread,
  load-more, fetching-more, fetch-more-error, fetch-more-retry, licensed,
  licensed-cta; grouped by area. (MikeRyanDev)
- Rewrite the 'no threadId state / no onSelect plumbing' line to stand on its own
  by contrasting with a hand-rolled sidebar. (samjulien)
2026-07-07 17:04:27 -05:00
Benjamin Taylor f4ec551809 docs(drawer): wrap drawer + chat in a shared CopilotChatConfigurationProvider so selection drives the chat [ENT-1051] 2026-07-07 17:04:27 -05:00
Benjamin Taylor e95cc3a5ca docs(drawer): document restored collapsible + onCollapseChange; fix stale 'no collapse control' prose [ENT-1051] 2026-07-07 17:04:27 -05:00
Benjamin Taylor 68c9ac84fc docs(drawer): refresh preview screenshot to show the desktop collapse control [ENT-1051] 2026-07-07 17:04:27 -05:00
Benjamin Taylor 0929ba16fa docs(drawer): refresh preview with aligned New Conversation row [ENT-1051]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:04:27 -05:00
Benjamin Taylor c321a65854 docs(drawer): refresh preview (persistent sidebar, no search/collapse chrome) [ENT-1051]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:04:27 -05:00
Benjamin Taylor 10861da99d docs(drawer): remove collapse API; persistent sidebar on desktop, off-canvas on mobile [ENT-1051]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:04:26 -05:00
Benjamin Taylor 4c101a3cd0 docs(drawer): refresh preview screenshot (search removed) [ENT-1051]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:04:26 -05:00
Benjamin Taylor df74bcebd5 docs(drawer): remove search; document collapsible + collapse-change [ENT-1051]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:04:26 -05:00
Benjamin Taylor b3d473226f docs(drawer): update CopilotThreadsDrawer reference/guide for the UX redesign [ENT-1051]
New parts, recentLabel, and search; header/kebab/funnel changes.
2026-07-07 17:04:26 -05:00
Benjamin Taylor 9137887352 docs(drawer): refresh CopilotThreadsDrawer preview for the UX redesign [ENT-1051]
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 17:04:26 -05:00
Benjamin Taylor 3532370de9 docs(drawer): add CopilotThreadsDrawer preview screenshot
Stand-in for a live showcase example (out of scope for this PR): a real
screenshot of <CopilotThreadsDrawer> beside <CopilotChat>, rendered from the
v2 react demo against the Intelligence platform. Embedded as a <Frame> preview
right under the intro.
2026-07-07 17:04:26 -05:00
Benjamin Taylor 808e0256a1 docs(drawer): rename CopilotDrawer -> CopilotThreadsDrawer
Match the final component name. Renames the guide and reference pages
(copilot-drawer.mdx -> copilot-threads-drawer.mdx, CopilotDrawer.mdx ->
CopilotThreadsDrawer.mdx), their slugs/URLs, the nav meta entry, the
data-testid default, the <copilotkit-threads-drawer> element mention, and
all prose/import references. The --cpk-drawer-* CSS tokens and ::part names
are unchanged.
2026-07-07 17:04:25 -05:00
Benjamin Taylor 799be82723 docs(drawer): desurface the license-gate API; one neutral locked-view line
Per review: remove the onUnlicensed prop, the unlicensed slot, and the
unlicensed/unlicensed-cta parts from the guide + reference so neither
humans nor agents surface them. Replace with a single neutral line:
threads require Intelligence; a locked view shows without a license key.
2026-07-07 17:04:25 -05:00
Benjamin Taylor 29e4f66d44 docs(drawer): rename upsell -> unlicensed (match the renamed API) 2026-07-07 17:04:25 -05:00
Benjamin Taylor 31ab23ddc5 docs(drawer): sign-up CTA + guide rewording; threads pages for 4 frameworks
- Guide: lead with the user benefit (less reference-y opening), reframe the
  headless useThreads alternative to stand alone, add the OpsPlatformCTA
  sign-up callout (per review).
- Add threads.mdx (shared-snippet include) for a2a, adk, agent-spec,
  deepagents + register each under Intelligence Platform in meta.json.
2026-07-07 17:04:25 -05:00
Benjamin Taylor 5fb4802b88 docs(drawer): remove Angular content for now (React-only)
Per review: defer the Angular drawer docs. Removes the Angular reference
page, the guide's Angular section, and Angular cross-links; keeps the
React guide + reference + the Threads how-to callout.
2026-07-07 17:04:25 -05:00
Benjamin Taylor 6ec4124323 docs(drawer): document the limit prop (thread pagination / Load more) 2026-07-07 17:04:25 -05:00