14961 Commits

Author SHA1 Message Date
github-actions[bot] 96c559bbf5 style: auto-fix formatting 2026-08-18 19:56:25 +00:00
copilotkit-qa-bot[bot] 40608dc01d docs: show Claude tool-rendering backend wiring 2026-08-18 12:53:15 -07:00
Martha Kelly Schumann 85642b9172 docs: show Claude fixed-schema backend wiring (#6548)
## Summary

- document the normal fixed-schema request path accurately:
`shouldUseClaudeAgentSdk` → `ClaudeAgentAdapter` → an in-process SDK MCP
server
- show the exact `mcp__copilotkit__display_flight` registration,
`allowedTools`, handler dispatch, and returned A2UI operations
- describe the direct Anthropic Messages loop as the fallback for
aimock, runtime tools, thinking, and structured input
- add regression tests for request-path selection, MCP wiring, generated
setup content, rendered LLM text, the visual `FrameworkSetup` path, and
isolation from every other public framework
- run the backend unit suite during the Claude TypeScript Docker image
build, so the PR showcase build gates this path

## Validation

- `npm run test:unit` in `showcase/integrations/claude-sdk-typescript` —
13 passed
- `npm run typecheck` and `npm run build` in
`showcase/integrations/claude-sdk-typescript`
- focused shell-docs suite — 39 passed
- `npx tsc --noEmit` and `npm run build` in `showcase/shell-docs`
- full shell-docs suite — 406 passed; 1 unrelated Channels dark-image
assertion fails identically on `main`
- GitHub CI is green, including the Claude TypeScript image build that
executes the backend unit suite and the shell-docs image build
- real D6 probe could not start because this isolated worktree has no
`showcase/.env`; aimock also intentionally selects the fallback branch,
so the new unit test covers the production SDK/MCP branch directly

Linear:
[FAC-140](https://linear.app/copilotkit/issue/FAC-140/claude-sdk-fixed-schema-a2ui-backend-example-lacks-mcp-tool-wiring)
2026-08-18 12:45:12 -07:00
copilotkit-qa-bot[bot] 1ea70b0485 chore(showcase): refresh reviewable bot push 2026-08-18 11:53:48 -07:00
copilotkit-qa-bot[bot] 5b99981ad0 chore: retrigger review gate 2026-08-18 11:44:49 -07:00
copilotkit-qa-bot[bot] 35f14ff4d0 fix(showcase): prune Claude test deps and clean snippets 2026-08-18 11:02:30 -07:00
copilotkit-qa-bot[bot] 8d01b1a368 fix(showcase): include Claude SDK tests in image 2026-08-18 10:08:28 -07:00
copilotkit-qa-bot[bot] 778dde6627 test(showcase): cover Claude SDK MCP wiring 2026-08-18 10:01:58 -07:00
copilotkit-qa-bot[bot] 0d528d57cc docs(showcase): expose Claude fixed-schema backend wiring 2026-08-18 09:48:32 -07:00
Ben Taylor e0a2f00cca fix: relax partialjson version constraint in sdk-python (#6123)
## What does this PR do?

Relaxes the over-strict `partialjson` version constraint in
`sdk-python/pyproject.toml` so the CopilotKit Python SDK can consume
newer compatible `partialjson` releases.

**Change:** `sdk-python/pyproject.toml`
- Before: `partialjson = "^0.0.8"`
- After:  `partialjson = ">=0.0.8,<2.0.0"`

## Why

Poetry interprets `^0.0.8` on a `0.0.x` version as `>=0.0.8,<0.0.9`,
which effectively pins the SDK to **exactly** `0.0.8`. This blocks users
from receiving newer, compatible `partialjson` releases (currently
published: `0.0.9`, `0.1.0`, `1.0.0`, `1.1.0`). Relaxing the constraint
lets the SDK pick up fixes and features while still treating the next
major (`2.0.0`) as the breaking-change boundary.

I verified the API the SDK actually uses — `from partialjson.json_parser
import JSONParser` and `JSONParser().parse(...)` — is unchanged across
all published versions up to `1.1.0` (the `JSONParser.__init__` /
`parse` signatures are backward compatible, and the SDK call passes no
arguments). There is no breaking-API risk within the chosen range.

## Test plan

- [x] Validated `sdk-python/pyproject.toml` is well-formed:
`python3 -c "import tomllib,pathlib;
tomllib.load(pathlib.Path('sdk-python/pyproject.toml').open('rb'))"`
- [x] Grepped `sdk-python` for `partialjson` usages — only
`copilotkit/runloop.py` imports/uses it; API is stable across the
allowed range.
- [ ] `poetry lock` / `poetry install` in `sdk-python` resolves a
`partialjson` version within `>=0.0.8,<2.0.0`.

## Changed files

- `sdk-python/pyproject.toml` — single line changed (dependency range
only). No source, example, or lockfile changes.

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] No documentation change required (dependency range only)
- [x] "Allow edits by maintainers" is checked

Relates to #4131
2026-08-18 09:56:22 -05:00
Ben Taylor 471d74bad4 feat(react-ui): report feedback state to onThumbsUp/onThumbsDown (#6531)
Closes #2615.

## Problem

The v1 thumbs callbacks were typed `(message: Message) => void`, so a
consumer
received the message but not *what the click did*. A custom
`AssistantMessage`
that keeps its own toggle state — the case in the issue — had nowhere to
put
that value:

```tsx
onClick={() => {
  setReactionValue((v) => (v === "like" ? null : "like"));
  onThumbsUp?.(message); // 👈 no way to say whether this applied or retracted
}}
```

The only workaround was counting clicks per message.

The built-in path had the matching gap. `Chat.tsx` already tracked
`messageFeedback`, but `handleThumbsUp` unconditionally wrote
`"thumbsUp"`, so
the state was write-only: clicking an active button re-applied the same
value
and there was no way to un-vote.

These callbacks are live API — `RenderMessage` forwards `onThumbsUp`,
`onThumbsDown` and `feedback` to whatever component is passed as the
`AssistantMessage` prop, which is exactly the customisation the issue
describes.

## Fix

Add an optional second argument reporting the state the click
transitions to:

```ts
onThumbsUp?: (message: Message, isActive?: boolean) => void;
```

- The built-in `AssistantMessage` derives it from the message's current
`feedback`, so a second click on an active button now reports `false`
and
  **retracts** the feedback rather than re-applying it.
- A custom `AssistantMessage` can pass its own value straight through.
- The parameter is optional and appended, so existing one-argument
handlers
  keep type-checking and keep working unchanged.

`onFeedbackGiven` fires only on the applying click — its signature is
`(messageId, "thumbsUp" | "thumbsDown")` and has no way to express a
retraction, so reporting one would be a lie.

Note this is a small behavioural change to the built-in buttons:
previously a
repeat click was a no-op, now it clears the vote. That is what makes the
reported state meaningful, and it matches the toggle UX in the issue.

The toggle logic lives in a new `./feedback` module. This package's
vitest
project runs `environment: "node"` and only collects `*.test.ts`, so
there is no
component-rendering harness here — extracting the pure functions is what
makes
the behaviour testable at all.

## Testing

`packages/react-ui` — full suite, including the 10 new cases:

```
 ✓ src/components/chat/feedback.test.ts (10 tests) 3ms
 ✓ src/components/chat/Markdown.test.ts (29 tests) 5ms
 ✓ src/components/chat/Markdown.xss.test.ts (13 tests) 182ms
 ✓ src/css/sidebar-full-height.test.ts (4 tests) 2ms

 Test Files  10 passed (10)
      Tests  68 passed (68)
```

Coverage: activation from no feedback, deactivation on the
already-active
button, switching to the opposite button, retraction removing the map
entry
rather than storing a falsy value, other messages left untouched, no
mutation of
the previous map, referential stability when nothing changes, and a
toggle
round-trip.

**Mutation checks** — broke each mechanism and confirmed the tests fail,
so they
are not self-fulfilling:

| Mutation | Result |
| --- | --- |
| `isActivatingClick` → `return true` | `Tests 1 failed \| 9 passed` |
| `applyFeedbackClick` retraction branch → `if (false)` | `Tests 3
failed \| 7 passed` |

Both were reverted and the suite returned to 68 passing.

**Types/build** — `tsc --noEmit` clean, `tsdown` clean, and the widened
signature reaches the published types:

```
$ grep -n "onThumbsUp" dist/index.d.mts
102:  onThumbsUp?: (message: Message, isActive?: boolean) => void;
185:  onThumbsUp?: (message: Message, isActive?: boolean) => void;
272:  onThumbsUp?: (message: Message, isActive?: boolean) => void;
```
2026-08-18 08:56:55 -05:00
Ben Taylor 2d0b2838c0 fix(react-ui): pad the chat header on mobile viewports (#6530)
Fixes #2493.

## Problem

`.copilotKitHeader` declared its horizontal padding like this:

```css
.copilotKitHeader {
  padding-left: 1.5rem;      /* no padding-right */
  justify-content: space-between;
}

@media (min-width: 640px) {
  .copilotKitHeader {
    padding-left: 1.5rem;    /* duplicate of the base rule */
    padding-right: 24px;     /* only above the sm breakpoint */
  }
}
```

`padding-right` existed **only** inside `@media (min-width: 640px)`. The
header
lays out `space-between`, so below 640px the title sat against the left
edge
(fine — `padding-left` is unconditional) while the controls ran flush to
the
right edge with no gutter at all. That is the cramped mobile header in
the
report.

Worth noting because it misleads when reading the file: the
`.copilotKitHeader > button { position: absolute; right: 16px }` rule
further
down does **not** apply to the close button. `Header.tsx` nests it
inside
`.copilotKitHeaderControls`, so the button is a grandchild and the child
combinator never matches. The button is in normal flow, which is why it
lands
exactly on the padding edge.

## Fix

Move the horizontal padding onto the unconditional rule and drop the
duplicated
declarations from the media query, leaving it responsible only for the
border
radii. `24px === 1.5rem` at the default root font size, so layouts at
640px and
above are byte-for-byte unchanged; only the sub-640px case gains the
gutter it
was missing.

## Testing

Added `src/css/header-padding.test.ts`, which strips every `@media`
block and
asserts the remaining unconditional `.copilotKitHeader` rule declares
both
`padding-left` and `padding-right` — i.e. that the padding is not
breakpoint-gated
again.

Full `react-ui` suite:

```
 ✓ src/esm-compat.test.ts (1 test) 2ms
 ✓ src/css/sidebar-full-height.test.ts (4 tests) 2ms
 ✓ src/css/header-padding.test.ts (1 test) 1ms
 ✓ src/hooks/__tests__/use-push-to-talk.test.ts (2 tests) 2ms
 ✓ src/components/chat/Markdown.test.ts (29 tests) 5ms
 ✓ src/components/chat/Markdown.xss.test.ts (13 tests) 182ms

 Test Files  10 passed (10)
      Tests  68 passed (68)
```

**Mutation check** — removed `padding-right: 1.5rem` from the base rule
and
re-ran, confirming the new test fails rather than passing vacuously:

```
     37|     expect(base).toMatch(/padding-left:/);
     38|     expect(base).toMatch(/padding-right:/);
       |                  ^
 Test Files  1 failed (1)
      Tests  1 failed (1)
```

**Build** — `tsdown` succeeds and the compiled `dist/index.css` carries
the
declaration:

```
.copilotKitHeader {
  ...
  padding-left: 1.5rem;
  padding-right: 1.5rem;
  ...
}
```

**Visual** — rendered the real `Header.tsx` markup against the compiled
stylesheet at a 390px viewport, with one copy re-gating `padding-right`
behind
the breakpoint to reproduce the previous rule. Before, the close button
sits
flush on the right border; after, it clears it by 24px, matching the
existing
left gutter. (Screenshot to follow in a comment.)
2026-08-18 08:53:16 -05:00
Ben Taylor def14dcd1c docs(channels): document the shipped Teams setup flow (#6522)
The Teams docs described a setup flow that no longer exists: register an
app by
hand in Microsoft Entra, point an Azure Bot at the Intelligence
messaging
endpoint, paste a client ID, tenant ID, and secret into the wizard, then
download a package to upload. Setup is now a single CLI command that
creates a
Teams-managed bot in the reader's own tenant, registers the endpoint,
and
produces the app package the reader uploads.

This rewrites the three pages a reader hits while connecting Teams,
repoints the
doc test that pinned the old prose, and rolls in OSS-833 (refs OSS-833)
— naming
which kind of Microsoft bot identity setup creates.

All three pages were reviewed rendered, not just as a diff.

## What changed

**`channels/intelligence.mdx`** — the Connect Microsoft Teams step is
now the
command the wizard shows you (`channels add --adapter teams
--provision`), with
a note to use the copy button because the project and channel ids bind
the
Microsoft app to that exact Channel. Documents the package hand-off: the
command
writes `<channel-name>-teams-app.zip`, prints the upload steps, and
blocks until
you confirm, because Microsoft exposes no install API. The upload must
come from
the team's own Apps tab — the personal Apps section installs to personal
scope,
which cannot be promoted to a Team afterwards. Adds a section on the two
permissions and one on what **Created and installed** does and does not
prove.
Drops the Entra walkthrough and the credential entry.

**`frontends/teams.mdx`** — "managed Azure Bot" → "managed Microsoft
Teams bot",
and states up front that the bot lives in the reader's own tenant with
no Azure
subscription involved. This is the one place that sentence survives; the
Intelligence step no longer repeats it. The "Teams cannot reach the bot"
accordion now points at
Teams Developer Portal → Bot management, and calls out that Developer
Portal
reports a padded endpoint value as a save failure, which reads like a
portal
fault rather than a bad value. Also: in a team channel you must mention
the bot
— an unmentioned message is ambient and only reaches the agent on a
subscribed
thread.

**`frontends/teams.mdx`, `## What kind of bot this is`** — rolls in
**OSS-833**,
minimally. The docs said only what setup does *not* create, which does
not
answer the tenant administrator who has to account for a new identity in
their
own directory; the only way to learn the answer was to read the CLI
source and
notice `--teams-managed`. One section now names the Teams-managed bot we
create
(credentials in Developer Portal → Tools → Bot management, rotatable by
their
admins, single-tenant, nothing billable, and it stays theirs if they
stop using
CopilotKit), contrasts it with an Azure Bot and a self-managed Entra app
registration, and says the choice is effectively permanent because the
app ID is
the manifest's bot ID. Left as a section on the page that already
carried the
bot-ownership sentence rather than a new page, placed after the
orientation links
so it reads as its own section rather than swallowing them.

**`channels/files-and-multimodality.mdx`** — corrects the file
permission. The
manifest requests the read-only `Files.Read.All`, not the broad
`Files.ReadWrite.All`, and it is optional rather than a gate on the
connection
reporting ready. Skipping it costs only files uploaded to a Team
channel, which
arrive as SharePoint references; 1:1 chat files and pasted images are
unaffected.

## Testing

Every factual claim was checked against the shipped implementation in
the
Intelligence repo, not against the previous docs:

| Claim | Verified at |
| --- | --- |
| `channels add --adapter teams --provision` is the command |
`apps/cli/src/commands/channels.ts:925` quotes it verbatim in its own
error message |
| `--project-id` / `--channel-id` are only valid on that path |
`apps/cli/src/commands/channels.ts:1044` |
| `Files.Read.All` is prompted and defaults to skipping |
`apps/cli/src/commands/channels.ts:381` — prompt is `[y/N]`, "It can be
granted later." |
| `Files.ReadWrite.All` satisfies it without a second grant |
`apps/app-api/src/channels/teams-graph-access.ts:81`;
`apps/cli/src/services/microsoft-teams-graph.ts:107` leaves such an app
untouched |
| Absence omits only the attachment; 1:1 and pasted images unaffected |
`apps/app-api/src/channels/teams-graph-files.ts:460` — the doc prose
matches the shipped warning |
| Team-channel uploads arrive as SharePoint references |
`apps/app-api/src/channels/teams-graph-files.ts:252` |
| `ChannelMessage.Read.Group` is per-Team RSC and required |
`teams-graph-access.ts:88`;
`apps/app-api/src/channels/teams-ingress.ts:1480` |
| The command writes a zip and waits rather than installing |
`apps/cli/src/services/teams-provision.ts:766-803` — "Microsoft's
tooling has no install command", then blocks on
`waitForAction('installation')` |
| Upload from the team's Apps tab, not personal Apps |
`apps/cli/src/commands/channels.ts:407-419` — the printed steps, and why
personal scope is a dead end |
| No upload option ⇒ Ctrl-C, admin installs, re-run resumes |
`apps/cli/src/commands/channels.ts:419` |
| `--teams-package` is browser-built branding, validated then deleted |
`apps/cli/src/index.ts:279`; `channels.ts:533-552` validates before
authorizing removal, `channels.ts:631-633` deletes |
| The wizard really does render a copy button for the command |
`Intelligence:apps/app-frontend/react-shell/src/channels/teams-guided-adapter-setup.tsx:652-674`
|
| We create a Teams-managed bot, registered single-tenant |
`apps/cli/src/services/microsoft-teams-cli.ts:411` passes
`--teams-managed` and `--sign-in-audience myOrg`; `:127` pins
`botLocation: z.literal('teams-managed')` |
| Microsoft's CLI knows only two kinds, so no self-managed Entra app |
`@microsoft/teams.cli@3.0.3` `dist/apps/bot-location.d.ts` —
`BotLocation = 'tm' \| 'azure'` |
| Migration to Azure is one-way and replaces the registration |
`dist/commands/app/bot/migrate.js:15,58,68`; the `bot` command group
contains only `get` and `migrate` |
| The app ID is the manifest's bot ID, so identity changes need a new
package | `Intelligence:libs/channels-setup/src/teams-package.ts:51-53`
(`botId: id`), built by `createTeamsAppPackage(label, clientId)` |

The second commit exists because the first got two of these wrong — it
said the
command "installs it to a Team you choose" and that "there is no package
to
download", which also contradicted the Teams tutorial's own "upload the
complete
zip that setup produced". Both are corrected, and the doc test now pins
the
package hand-off in place of the struck Azure sentence.

Doc test, rebased onto `main` (`4526bb00d2`):

```
$ npx vitest run src/lib/__tests__/channels-docs.test.ts   # showcase/shell-docs
 Test Files  1 failed (1)
      Tests  1 failed | 29 passed (30)

 ❯ src/lib/__tests__/channels-docs.test.ts:97:30
```

The 30th test is new: it pins the bot-identity section, since "name the
kind we
create" is exactly the claim that erodes back into "Azure Bot is not
part of the
normal path".

The one remaining failure is **pre-existing on `main`** and unrelated to
this
PR. It asserts the Channels overview embeds
`channels-architecture-dark.png`; `channels/index.mdx` references
`channels-architecture-light.png` twice and no dark variant, as of
`47a4a84896 docs(channels): re-export architecture diagram at 4000px` —
a commit
on `main` that this branch does not touch. Proof it is not mine: running
main's
version of the test file against this branch's docs gives **2** failures
— line
97 plus line 521, main's now-stale `expect(teams).toContain("Microsoft
Entra")`
— and this PR's updated test file removes only the second.

That pre-existing break is left alone here rather than folded in, since
fixing
it means deciding whether the overview wants a dark diagram or the
assertion is
obsolete.
2026-08-18 08:44:39 -05:00
lukasmoschitz 72d57a41ec fix(channels-slack): rename decimal_allowed to Slack's is_decimal_allowed (OSS-794) (#6519)
OSS-794.

## The defect

`SlackNativeProps` declared `decimal_allowed`. Slack's `number_input`
field is **`is_decimal_allowed`** (confirmed in `@slack/types@2.22.0`,
where it is also *required*).

Slack accepts a message whole or not at all. The unrecognised key
refused the entire `chat.postMessage` call and ended the Channels
delivery that carried it — no exception reached the caller, nothing
arrived in the channel.

So the **typed path was the broken one**. Anyone who bypassed our types
and hand-wrote `is_decimal_allowed` got it right by accident; anyone
using `Slack.Element.NumberInput` silently lost the message. That is the
opposite of what an SDK should do.

## Proof, before the change

Verified live against Slack (private `#bot-test`), one delivery per case
— a refusal ends the whole turn's delivery, so batching would have made
the results meaningless.

| Case | Payload | Outcome |
| --- | --- | --- |
| A | `decimal_allowed: true` (our typed spelling) | **refused** —
`invalid_blocks: invalid field at /blocks/0/element` |
| B | no decimals field at all | **refused** — same error |
| C | `is_decimal_allowed: true` (Slack's spelling) | **delivered** |
| E | *both* names present | **refused** — same error |

Case B refuses for its own reason: `@slack/types` marks
`is_decimal_allowed` **required**, so omitting it is independently
invalid. That makes B a poor control, so case E was added — it satisfies
Slack's required field and is otherwise byte-identical to the payload
that delivers, leaving the unknown `decimal_allowed` key as the only
possible cause. A vs C vs E isolates the name as the whole story.

In the refused threads only the caption arrived; the block itself is
simply absent — the silent-loss shape the defect produces in production.

## Is this breaking?

**No, and no alias is kept.** The old name never worked: every payload
carrying it was refused by Slack, so no caller can be depending on
working behaviour. Keeping `decimal_allowed` as an alias would preserve
the trap. Removing it converts a silent message loss into a compile
error that names the right field:

```
error TS2561: Object literal may only specify known properties, but 'decimal_allowed'
does not exist in type 'SlackNativeProps<unknown>'. Did you mean to write 'is_decimal_allowed'?
```

## Proof, after the change

Built locally, copied over the installed `@copilotkit/channels-slack` in
a real OpenTag runtime, confirmed the process held the new build (`tsc`
reads the copied `.d.ts` — it accepts `is_decimal_allowed` with no cast
and rejects `decimal_allowed` with the error above), then re-ran case A
through the fixed typed surface: **delivered**, block present in the
thread.

## Sibling sweep

Every field name `native.ts` declares was compared against Slack's Block
Kit vocabulary. **No other mismatch was found.** Names absent from
`@slack/types` are absent because `@slack/types` does not model those
blocks, not because they are misspelled — they are enumerated in the
guard.

Two adjacent observations, not fixed here: `dispatch_action`,
`min_length` and `max_length` are Slack fields we do not declare at all
(callers reach them via `as never`). Those are gaps rather than
mismatches, so they are out of scope for this PR.

## The guard

`src/__tests__/native-field-names.test.ts` compares every field name
`native.ts` declares against Slack's own Block Kit declarations in
`@slack/types` — already a direct dependency of this package, so no new
dependency was added. Declarations are read with the TypeScript parser
rather than matched as text.

**It fails when it should.** Temporarily restoring the misspelling turns
it red with an actionable message:

> native.ts declares `decimal_allowed`, which @slack/types does not.
Slack refuses a whole message for one unrecognised key, so a wrong name
here deletes every message using the component. Either correct the name
to Slack's, or — if Slack really does accept it and @slack/types is
simply behind — add it to NOT_COVERED_BY_SLACK_TYPES with the reason.

**It is honest about coverage.** `@slack/types` is incomplete, so a
missing name is not proof a name is wrong. Every uncovered field is
listed individually with its reason rather than being waved through:

- `blocks` — `container`'s child slot; `@slack/types` declares no
container block
- `offset` — documented on `rich_text_list`, absent from `@slack/types`'
`RichTextList`
- `slack_icon`, `subtext` — accepted on `card`, not declared by
`CardBlock`
- `chart`, `segments`, `series`, `axis_config`, `categories`, `x_label`,
`y_label`, `data` — `data_visualization` has no counterpart in
`@slack/types` at all

**It cannot pass trivially.** Floors assert the extracted vocabulary is
real (≥100 names, plus spot checks) and that a meaningful number of
names were actually compared (≥30), so a collapsed comparison fails
instead of looking green. Two further assertions keep the exemption list
from rotting: an exemption `@slack/types` has since started declaring,
or one for a name we no longer declare, both fail.

## Verification

| Command | Result |
| --- | --- |
| `oxfmt --check packages/channels-slack` | clean, 66 files |
| `oxlint` on both changed files | 0 warnings, 0 errors |
| `nx run @copilotkit/channels-slack:check-types` | pass (+ 8 dependent
tasks) |
| `nx run @copilotkit/channels-slack:test` | 31 files, 411 tests passed
|
| `nx run @copilotkit/channels-slack:build` | pass |
| lefthook pre-commit (`test-and-check-packages`, all packages) | pass |
2026-08-18 13:45:40 +02:00
Benjamin Taylor dc2aee1218 docs(channels): name which Microsoft bot identity Teams setup creates
The docs said only what setup does not create — no Azure subscription, no
Azure Bot resource. That answers nothing for the tenant administrator who has
to account for a new identity in their own directory, and the only way to
learn the answer was to read our CLI source and notice `--teams-managed`.

Names the kind we create and contrasts it with the two alternatives: where the
credentials live, who rotates them, that it is single-tenant, and that nothing
is billable. Also states that the choice is effectively permanent, since the
app ID is the manifest's bot ID.

Kept to one section on the tutorial page, which already carried the
bot-ownership sentence, rather than a new page. It sits after the orientation
links as an `h2` alongside the page's other sections; as an `h3` wedged between
the intro and those links, the links read as part of it.

refs OSS-833

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 20:21:04 -07:00
Benjamin Taylor 1b7d56029c docs(channels): correct the Teams package hand-off
The rewrite claimed the command "installs it to a Team you choose" and that
"there is no package to download". Neither is true. Microsoft exposes no
install API, so `runAutomaticTeamsProvision` writes
`<channel-name>-teams-app.zip`, prints upload steps, and blocks on Enter
before verifying the installation. The old prose and the Teams tutorial's
"upload the complete zip that setup produced" also contradicted each other.

Documents what the command actually hands you: the package path, that the
upload must come from the team's own Apps tab rather than the personal Apps
section — personal scope yields a working DM that cannot be promoted — and
that a tenant disallowing custom apps means Ctrl-C, get an administrator to
install it, and re-run to resume rather than create a second app.

Also covers `--teams-package`, which the wizard adds when the reader
customizes branding: the browser builds that file, the CLI validates it,
builds the app from it, and deletes the local copy. It decides how the app
looks and does not replace the Team upload.

Strikes "no Azure subscription and no Azure Bot resource are involved" from
the Intelligence step. The Teams tutorial still says it, once, where a reader
arriving with Azure expectations actually starts.

The doc test pinned that struck sentence. It now pins the package hand-off
instead, which is the claim that was wrong and would otherwise drift back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 20:21:04 -07:00
Benjamin Taylor df8ae72069 docs(channels): document the shipped Teams setup flow
The Teams docs described a flow that no longer exists: registering an app
by hand in Microsoft Entra, pointing an Azure Bot at the Intelligence
messaging endpoint, pasting a client ID, tenant ID, and secret into the
wizard, and downloading a package to upload. Setup is now a single CLI
command that creates a Teams-managed bot in the reader's own tenant,
registers the endpoint, and installs the app.

Also corrects the file permission. The manifest requests the read-only
`Files.Read.All`, not `Files.ReadWrite.All`, and it is optional: skipping
it costs only files uploaded to a Team channel, and no longer holds the
connection back from reporting ready.

The doc test pinned the old prose, so it is repointed at the new
contract — the command's flags, both permissions and which is optional,
and that Entra and Azure Bot appear only to say they are not involved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 20:21:04 -07:00
Ben Taylor 983c205feb fix: stale useCoAgent node name, silent content-part drops, and the LangGraph docs gaps (#6520)
Bundles the CopilotKit-side work from **OSS-769**, **OSS-767**
(partial), and the unshipped remainder of **OSS-609**.

## OSS-769 — `useCoAgent().nodeName` never updates

`useAgentNodeName` tracked the current node in a ref and returned
`nodeNameRef.current`. Mutating a ref schedules no render, so a
component reading `useCoAgent().nodeName` kept showing whichever node
was current at its last render and never updated on its own — it only
appeared to work when something unrelated happened to re-render it.

Backing the value with state fixes it. Five lines, no API change.

## OSS-767 (partial) — silent content-part drops (#1748)

`normalizeMessageContent` handles only `text` and `binary` parts;
anything else — the `{"type": "image", ...}` case from the report — maps
to `""` and is filtered out with no signal, so an agent emitting
structured content sees its output vanish silently.

This makes the drop visible, once per unrecognised part type so
streaming doesn't flood the log. Deliberately **not** the schema change:
carrying structured assistant content needs an `AssistantMessage`
decision upstream in `ag-ui`, which stays open on OSS-767.

## OSS-609 — the five docs gaps that never shipped

Gap #2 shipped in #6403; gaps 1, 3, 4, 5 and 6 did not.

| Gap | Where | Closes |
| --- | --- | --- |
| AWS Lambda self-hosting | `docs/deploy/aws-lambda.mdx` | #1151 |
| Per-user thread authorization | `docs/auth.mdx` (new section) | #2241
|
| Thread persistence without the platform |
`docs/threads-self-managed.mdx` | #6090 |
| DIY guardrails / DLP | `docs/integrations/langgraph/guardrails.mdx` |
#3414 |
| When you need an MCP App | `docs/agentic-protocols/mcp.mdx` (new
section) | #5991 |

The Lambda guide leads with the constraint that actually bites —
streaming is opt-in on every front door, so a chat runtime deployed with
the defaults appears to hang for the whole run and then dumps the reply
at once. It documents the Function URL + `RESPONSE_STREAM` path as the
default, and API Gateway REST + `responseTransferMode: STREAM` for
anyone who needs a REST API in front.

`threads-self-managed` follows the existing shared-snippet pattern with
per-framework wrappers, because two nav contracts require it: every
authored framework must publish the page, and every React destination
must map to an Angular one (`ANGULAR_DOC_REDIRECTS`). All nine wrappers
were confirmed necessary by deleting one and watching the suite fail.

## Review corrections

Two blockers from @MikeRyanDev, both verified against primary sources
before changing anything.

**API Gateway REST APIs can stream**
([`91b3e632d5`](https://github.com/CopilotKit/CopilotKit/commit/91b3e632d5)).
The guide was built on the pre-November-2025 limitation and claimed no
API Gateway type supports response streaming, steering readers to a
buffered `serverless-http` setup. REST gained it via
`responseTransferMode: STREAM`, which also lifts the 10 MB cap and
29-second timeout. REST and HTTP are now split; REST is documented as a
streaming front door (payload-format-1.0 event adapter, `AWS_PROXY`
integration on the `2021-11-15/.../response-streaming-invocations` URI,
CLI/CDK/SAM config), and the buffered fallback is scoped to HTTP APIs
and ALB, which still have no streaming path. Added the constraints that
actually matter for chat: the 30-second idle timeout on edge-optimized
endpoints (5 min Regional), and the console Test tab always buffering so
a working config looks broken.

**`identifyUser` is the platform's thread-scoping binding**
([`91b3e632d5`](https://github.com/CopilotKit/CopilotKit/commit/91b3e632d5)).
The section told every reader to build an ownership table and enforce it
in `onBeforeHandler`. On the Intelligence path the runtime already
resolves `identifyUser(request)` server-side and carries that id to the
platform; `listThreads` is scoped by user *and* filtered by `agentId`,
so the "every user of one project sees that project's threads" claim was
wrong. `identifyUser` is now documented as the binding, and the DIY
pattern is scoped to SSE runtimes, custom stores, and the local
in-memory runner.

**Follow-up correction — two routes are genuinely unscoped**
([`240672ff56`](https://github.com/CopilotKit/CopilotKit/commit/240672ff56)).
My rewrite then over-claimed. `handleGetThreadEvents` and
`handleGetThreadState` resolve the caller and discard it, and the
platform client takes no `userId` on either method
(`client.ts:1113`/`1135`) — unlike `getThreadMessages` at `1063`. Both
hit project-authenticated `_inspect` endpoints, so any caller
`identifyUser` accepts can read the event log and agent state of **any
thread in the project** given its id. The blanket guarantee is replaced
by a per-route table marking those two explicitly unscoped, plus an
`onBeforeHandler` guard narrowed to them.

That is a live gap in shipped runtime code, not a docs error, and it is
tracked as **OSS-851** — a platform-side `_inspect` change plus matching
runtime/client work and tests, out of scope for a docs PR. The interim
callout in `auth.mdx` comes out when OSS-851 lands.

## Not in this PR

**OSS-772** and **OSS-773** are already merged in
`oss-path-to-production` (#237, #238). Both are telemetry-sink changes
with no CopilotKit-side component. OSS-773's remaining half — re-keying
runtime `distinct_id` from email to the Clerk subject — is recorded on
the ticket as an open decision, not a task.

## Testing

**OSS-769.** New `use-agent-nodename.test.tsx`, 5 tests. Against
unmodified `origin/main`, **4 of 5 fail**:

```
× re-renders consumers on every node transition
× reports 'end' when a run errors
× resets to 'start' when a new run begins
✓ unsubscribes on unmount
× carries the agent, thread, and current node
  Tests  4 failed | 1 passed (5)
```

With the fix: `Tests 5 passed (5)`. These assert only re-render
behaviour under normal `act()` flushing — no manufactured intra-batch
window.

**Full react-core suite:** `Tests 7 failed | 1496 passed (1503)`. All 7
failures are **pre-existing** `ResizeObserver is not a constructor`
under jsdom, confined to `CopilotChatView.pinToSend` and
`use-pin-to-send` — neither of which this PR touches.

**Typecheck:** `packages/react-core` → `tsc --noEmit` exit 0, no output.

**OSS-767:** 3 new tests covering the warn, warn-once-per-type, and
no-warn-for-supported-types. `src/graphql/message-conversion/` → `Tests
125 passed (125)`.

**Docs:** `showcase/shell-docs` → `Tests 1 failed | 373 passed (374)`.
The single failure (`channels-docs > publishes the Channels overview
only through provider navigation`) is **pre-existing**; baselining with
all changes stashed reproduces it and nothing else. Re-run unchanged
after both review-correction commits.

**Review corrections.** The AWS rewrite was checked against the AWS
sources rather than written from memory — the REST streaming
announcement, `configuration-response-streaming`,
`response-transfer-mode` (endpoint-type idle timeouts, unsupported
buffered-only features), `response-streaming-lambda-configure`
(CLI/OpenAPI shapes), the CFN `Integration` reference, and the CDK
`ResponseTransferMode` enum. Two details corrected in passing:
`InvokeWithResponseStream` authorizes against plain
`lambda:InvokeFunction` (no new grant, contrary to what the streaming
URI suggests), and ALB still has no Lambda streaming path.

The auth corrections were verified by reading the handlers and the
platform client, not the tests — `resolve-intelligence-user.ts`,
`intelligence/threads.ts` (every `resolveIntelligenceUser` call site),
and `intelligence-platform/client.ts`. The existing tests assert the
`threadId`-only call shape, so they pass under the defect and could not
have surfaced it. Also corrected: there is no `threads/delete` route —
delete is `DELETE` on `threads/update` (`fetch-handler.ts:606`).

Both edited pages MDX-compile clean, and all inbound
`#thread-authorization` anchors still resolve after the h3→h4 demotions.

Two nav tests broke during this work and are fixed rather than papered
over — adding a page to the Rich Threads group violated the
cross-framework ordering contract and the React→Angular parity contract:

```
src/lib/__tests__/docs-render.test.ts
src/lib/__tests__/angular-docs-content.test.ts
  Test Files  2 passed (2)
       Tests  33 passed (33)
```

All 15 internal links in the new pages resolve against the content tree.

Closes #1151, #2241, #3414, #5991, #6090
Refs #1748, OSS-851
2026-08-17 19:17:50 -05:00
Martha Kelly Schumann 1627c50be5 fix(docs): make programmatic control examples complete (#6540)
## Summary

- replace the generated full-chat excerpt with a self-contained
`useAgent` / `useCopilotKit` run-and-stop example
- guard concurrent runs, report rejected runs, and disable Run/Stop
controls when they are not applicable
- remove the redundant incomplete Claude Python and TypeScript setup
excerpts while preserving context-neutral AG-UI guidance on every page
that reuses the setup block
- add renderer regressions for all five affected integration routes and
the three shared Claude setup surfaces

## Root cause

The shared Programmatic Control page extracted a region from the middle
of each integration's full chat component. The displayed region omitted
its imports and local helpers, so otherwise-valid showcase code became
an incomplete documentation sample. The Claude Python and TypeScript
setup content also rendered a second partial excerpt, and its
directional prose was reused on pages where there was no matching
example below.

## Ticket acceptance

| Ticket | Route | Acceptance |
| --- | --- | --- |
| [FAC-166](https://linear.app/copilotkit/issue/FAC-166) | LangGraph
Python | The shared example imports its hooks, adds a message, runs the
agent, handles failure, and exposes state-aware cancellation without
chat-shell helpers. |
| [FAC-149](https://linear.app/copilotkit/issue/FAC-149) | Google ADK |
The complete shared example follows the preserved `AGUIToolset()`
backend setup. |
| [FAC-151](https://linear.app/copilotkit/issue/FAC-151) | AWS Strands
Python | The route renders the same complete shared send/stop example. |
| [FAC-159](https://linear.app/copilotkit/issue/FAC-159) | Claude Agent
SDK Python | The route renders one complete shared send/stop example and
no redundant `createMessageId` setup excerpt. The identical Claude
TypeScript setup defect is fixed in the same change boundary. |

Closes FAC-166
Closes FAC-149
Closes FAC-151
Closes FAC-159

## Validation

- `npm run test -- src/lib/__tests__/llm-text.test.ts`
- `npm run typecheck`
- `npm run build`
2026-08-17 16:37:49 -07:00
copilotkit-qa-bot[bot] eb567d44ae fix(docs): address programmatic control review feedback 2026-08-17 16:31:46 -07:00
copilotkit-qa-bot[bot] 1cc34c641a fix(docs): make programmatic control example self-contained 2026-08-17 16:09:35 -07:00
Martha Kelly Schumann eff9124af8 fix(docs): parse Claude request before streaming (#6539)
## Summary

- parse and validate the Claude Python BYOA request before
`StreamingResponse` starts consuming the response stream
- keep the in-stream `RUN_ERROR` path scoped to agent and streaming
failures
- add verifier and runtime-harness guards that reject request parsing
inside `event_stream()`

Closes
[FAC-129](https://linear.app/copilotkit/issue/FAC-129/claude-agent-sdk-python-quickstart-chat-hangs-in-use-existing-agent).

## Validation

- `npm --prefix showcase/scripts test -- verify-shell-docs.test.ts
--run`
- Claude quickstart check in `npm --prefix showcase/scripts run
verify-shell-docs:fast` (passes; the full command retains unrelated
baseline failures)
- `npm --prefix showcase/shell-docs run typecheck`
- `npm --prefix showcase/shell-docs run build`
- production build route: `/claude-sdk-python/quickstart` returned HTTP
200
- `PYTHON_VERSIONS=3.11,3.12 npm --prefix showcase/scripts run
check-claude-quickstarts:runtime -- --skip-typescript` (with `uv
0.12.5`)
2026-08-17 16:00:16 -07:00
copilotkit-qa-bot[bot] ad5454b1a3 Merge remote-tracking branch 'origin/main' into codex/fac-129-review-feedback 2026-08-17 15:48:36 -07:00
copilotkit-qa-bot[bot] ecaa5c5fd8 fix(docs): validate Claude run input before streaming 2026-08-17 15:47:46 -07:00
Benjamin Taylor 21ab0c6eb6 docs(auth): add agent/stop to the unscoped routes, demote identifyUser
The previous commit claimed every route resolves the caller through
identifyUser. It does not. handleStopAgent never resolves a user at all:
it calls runtime.runner.stop({ threadId }) directly, and
IntelligenceAgentRunner.stop looks the run up by threadId alone. Any
caller who learns an active threadId can abort that run mid-flight, and
an application relying on identifyUser as its authentication backstop is
not covered on this route because the callback is never invoked.

Audited every route on the Intelligence path rather than patching the one
that was reported. agent/stop is the only additional gap: agent/suggest
deliberately skips thread persistence, and threads/clear is a no-op off
the in-memory runner, which is the only runner exposing local thread
endpoints.

Drop the false universal-resolution claim, add agent/stop to the No row
and the targeted guard, and state plainly that onRequest is the
authentication gate while identifyUser only names an already
authenticated caller. The guard uses a switch rather than an array
membership test so route narrows and route.threadId type-checks; the
includes form fails with TS2339, verified against the real RouteInfo.

Refs OSS-851

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:29:17 -05:00
Benjamin Taylor 383c3f697c docs(auth): flag threads/events and threads/state as unscoped
The revised thread-authorization section claimed the Intelligence runtime
scopes every thread-bearing route to the resolved user. It does not.

handleGetThreadEvents and handleGetThreadState resolve the caller through
identifyUser and then discard the result, calling getThreadEvents({
threadId }) / getThreadState({ threadId }). The platform client takes no
userId on either method — both hit project-authenticated _inspect
endpoints — so any caller identifyUser accepts can read the event log and
agent state of any thread in the project given its id.

Replace the blanket guarantee with a per-route table, and document the
onBeforeHandler guard narrowed to those two routes so the Intelligence
path is not told there is nothing to build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor 9ea276b740 docs: correct API Gateway streaming and Intelligence thread scoping
Two review corrections.

API Gateway REST APIs gained Lambda response streaming in November 2025
via responseTransferMode: STREAM, which also lifts the 10 MB payload cap
and the 29-second integration timeout. The guide claimed no API Gateway
type can stream and steered readers to a buffered serverless-http setup.
Split REST from HTTP, document the REST streaming path (payload format
1.0 adapter, AWS_PROXY integration on the response-streaming-invocations
URI, CLI/CDK/SAM config), and scope the buffered fallback to HTTP APIs
and ALB, which still have no streaming path.

The thread-authorization section told every reader to build an ownership
table and enforce it in onBeforeHandler. On the Intelligence path the
runtime already resolves identifyUser(request) server-side and passes
that user id to run, connect, the thread reads, the mutations, and the
subscription token; listThreads is scoped by user and filtered by
agentId. Document identifyUser as the platform binding and scope the DIY
pattern to SSE runtimes, custom stores, and the local in-memory runner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor 6548d59f40 docs(mcp): distinguish when an MCP App is and is not needed
mcp.mdx treated all non-App MCP servers generically, leaving #5991's
question unanswered: readers could not tell whether their server needed a
UI resource at all.

Separates the three cases — read-only context servers (no renderer needed),
action servers (renderer plus human-in-the-loop approval), and interactive
MCP Apps — with the test for choosing between them.

Closes #5991

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor 98e02e9a4c docs(langgraph): add a guardrails and DLP how-to
Only adjacent coverage existed — the AG-UI middleware guide and a
third-party governance cookbook — so #3414's question about input/output
screening had no first-class answer.

Covers the built-in PIIMiddleware first, then custom AgentMiddleware for
input screening (before_model + jump_to), output and DLP screening
(wrap_model_call), and tool-call screening, plus why guardrails must be
ordered ahead of CopilotKitMiddleware.

Closes #3414

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor e996328f15 docs(threads): answer the bring-your-own-thread-backend question
#6090 asked how to point CopilotKit threads at a different database. There
is no such extension point, and nothing said so — the honest answer is that
Rich Threads are an Enterprise Intelligence Platform capability, and the DIY
path is own-the-threadId plus framework-layer persistence.

Documents that path and what it costs relative to Rich Threads. Body lives
in a shared snippet with the usual per-framework wrappers, since the nav
parity contract requires every authored framework to publish the page and
every React destination to map to an Angular one.

Closes #6090

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor 0e724231ec docs(auth): document per-user thread authorization
auth.mdx covered token forwarding and the onRequest gate but stopped at
authentication. Nothing tied a threadId to a user, so a caller who learned
another user's thread id could reach it through every thread route.

Adds the ownership-table pattern and the onBeforeHandler enforcement point,
including which routes carry threadId on `route` versus in the request body,
and why the thread list has to be filtered server-side.

Closes #2241

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor 2e1b125049 docs(deploy): add an AWS Lambda self-hosting guide
The generic adapter guide covers Lambda only by implication, and the part
that actually bites is undocumented: API Gateway and ALB buffer responses,
so a chat runtime behind either appears to hang for the whole agent run and
then dumps the reply at once.

Documents the Function URL + RESPONSE_STREAM path as the default, with the
awslambda.streamifyResponse wiring that bridges the runtime's Fetch Response
into the Lambda response stream, and keeps the serverless-http/API Gateway
path for the cases that genuinely do not stream.

Closes #1151

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor cb11f6fb93 fix(runtime): warn when message content parts are dropped
normalizeMessageContent maps array content and handles only "text" and
"binary" parts. Any other part -- the {"type": "image", ...} case from
#1748 -- maps to "" and is filtered out with no signal at all, so an
agent emitting structured content sees its output silently vanish.

Carrying those parts through needs an AssistantMessage schema change and
is tracked separately in OSS-767. This makes the current drop visible in
the meantime, once per unrecognised part type so streaming does not flood
the log.

Refs #1748

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor ffd15801d6 fix(react-core): re-render consumers when the agent node changes
useAgentNodeName tracked the current node in a ref and returned
nodeNameRef.current. Mutating a ref schedules no render, so a component
reading useCoAgent().nodeName kept showing whichever node was current at
its last render and never updated on its own -- it only appeared to work
when something unrelated happened to re-render it.

Backing the value with state fixes that. Adds the coverage the hook never
had: transitions, run-start reset, run-error, and unsubscribe on unmount.

Note this does NOT explain GH #1426 (interrupt agentMetadata.nodeName
reporting the previous node). useInterrupt only evaluates the `enabled`
predicate from a useEffect/useMemo keyed on its `pending` state, so the
interrupt's own state update always forces a render before the predicate
runs -- and the pre-fix code reads the ref correctly at that point. That
report needs a different explanation; it is left open.

Refs #1426

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 17:25:01 -05:00
Benjamin Taylor c94032f3fa feat(react-ui): report feedback state to onThumbsUp/onThumbsDown
The thumbs callbacks only received the message, so a consumer could not
tell whether a click applied feedback or retracted it. Custom
AssistantMessage implementations that track their own toggle state had
no way to pass that value through, and the built-in feedback state was
write-only: clicking an active button re-applied the same value.

Add an optional second argument reporting the state the click
transitions to. The built-in AssistantMessage derives it from the
message's current feedback, which also makes the built-in buttons
toggle; a custom AssistantMessage may pass its own value. The argument
is optional, so existing one-argument handlers are unaffected.

The toggle logic is extracted to ./feedback so it can be unit tested —
this package's vitest project runs in a node environment and has no
component-rendering harness.

Closes #2615
2026-08-17 17:22:24 -05:00
Benjamin Taylor 1ea18ca864 fix(react-ui): pad the chat header on mobile viewports
The `.copilotKitHeader` rule declared `padding-right` only inside
`@media (min-width: 640px)`, so below that breakpoint the header's
`space-between` children ran flush to the viewport edge and the close
button had no gutter.

Move the horizontal padding into the unconditional rule so it applies at
every viewport. The value matches the 24px the sm breakpoint already
used, so wide layouts are unchanged.

Fixes #2493
2026-08-17 17:22:21 -05:00
Tyler Slaton 69bb0a9814 fix(ci): stop enabling LangSmith tracing in the dojo e2e run (#6534) 2026-08-17 15:19:48 -07:00
copilotkit-qa-bot[bot] e8aa97649c fix(docs): parse Claude request before streaming 2026-08-17 15:12:58 -07:00
Martha Kelly Schumann d429f5d577 Fix Strands TypeScript sub-agent doc snippets (#6537)
## Summary

- include the Strands TypeScript sub-agent tools source in the demo
bundle
- define the canonical setup and supervisor delegation documentation
regions
- add a focused bundler regression for both regions and their boundaries

Linear:
[FAC-142](https://linear.app/copilotkit/issue/FAC-142/aws-strands-ts-sub-agents-docs-show-missing-snippets-in-two-sections)

## Validation

- `npm test -- __tests__/bundle-demo-content.test.ts` in
`showcase/scripts`
- `npm run typecheck` in `showcase/shell-docs`
- `npm run build` in `showcase/shell-docs`
- built `/strands-typescript/multi-agent/subagents` route contains both
snippets and zero `Missing snippet` warnings
- built Strands Python sub-agents route remains healthy as a control
2026-08-17 15:05:40 -07:00
copilotkit-qa-bot[bot] 61fd3c3828 Fix Strands TypeScript sub-agent doc snippets 2026-08-17 14:50:05 -07:00
Mark 23e4709718 chore(showcase): upgrade CopilotKit to 1.68.1 (#6510)
## Summary

- advance the canonical Showcase CopilotKit pin to `1.68.1`
- apply the release consistently across all 21 integrations and the
Showcase shell
- regenerate the affected npm lockfiles, including the LangGraph
TypeScript agent lockfile and the Shell's strict peer-dependency entries

This picks up
[#5837](https://github.com/CopilotKit/CopilotKit/pull/5837), which
bounds the in-memory agent runner to prevent unbounded thread retention
and OOMs.

## Verification

- Showcase pin ratchet passes at the existing 26-failure baseline/hash
- `@copilotkit/showcase-scripts`: 2,511 tests pass
- strict Shell `npm ci --ignore-scripts` succeeds, matching the Showcase
validation workflow
- Shell unit tests pass (241/241) and its production build succeeds
- clean installs and production builds pass for Ag2, Langroid, and
Mastra
- comparison with `origin/main` found no pin-induced TypeScript
diagnostics; the standalone TypeScript failures are pre-existing and
outside the current production build gate
2026-08-17 14:27:57 -07:00
Mark 89993e2d74 feat(showcase): deploy CrewAI conversational flows to staging (#6493)
## Summary

- provision and register the staging-only Railway service for CrewAI
Conversational Flows
- add the integration to the showcase build and redeploy workflow,
matching the existing service fleet
- enable Railway image-reference validation for the service while
validating only its declared staging environment
- keep full-fleet promotion compatible with intentional
single-environment services without weakening drift detection
- update the Railway SSOT, generated artifacts, fixtures, documentation,
and regression coverage

## Railway

- service: `showcase-crewai-conversational-flows`
- staging domain:
https://showcase-crewai-conversational-flows-staging.up.railway.app
- production intentionally not provisioned in this step

## Reviewer follow-up

- set `gateValidated: true` and removed the `gateIgnore` exemption; a
missing staging instance or incorrect GHCR repository/tag now fails
validation
- emit explicit `onlyEnvironment` metadata for SSOT-declared
single-environment services
- exempt only SSOT-declared environment asymmetry during no-argument
full-fleet promotion
- retain fail-closed behavior for unknown live Railway asymmetry and
explicitly targeted staging-only promotion
- add regression tests for staging-only image validation, full-fleet
parity, unknown services, targeted promotion, and generated metadata

## Conflict resolution against current `main`

- merged current `main`, including #6515 and #6516, instead of choosing
either side wholesale
- preserved #6515's canonical attachment fixtures, multimodal probe, and
global ownership regression test byte-for-byte from `main`
- preserved #6516's scripts typecheck fixes in `equivalence-gate.ts` and
`generate-search-index.ts` byte-for-byte from `main`
- reconciled the service registration introduced by #6504 with this PR's
build-matrix changes: there is exactly one SSOT entry, `ciBuilt: true`,
`gateValidated: true`, staging-only metadata, and no borrowed production
domain
- regenerated `railway-envs.generated.json`; it emits `onlyEnvironment:
"staging"` and an empty legacy production domain
- exclude that intentionally empty legacy domain from Ruby's
public-domain preflight set, preventing a permanent blank production
advisory
- the final PR diff against `main` contains only the intended 13
Railway/build/validation files and does not modify attachment fixtures
or probes

## Test plan

- `@copilotkit/showcase-scripts:test` through Nx: 77 files / 2,515 tests
passed
- `@copilotkit/showcase-harness:test` through Nx: 177 files / 3,712
tests passed; 2 files / 18 tests skipped
- focused multimodal harness regression: 11 tests passed
- showcase scripts TypeScript check passed
- showcase harness typecheck and build passed through Nx
- full Railway Ruby 3.2 suite passed: 188 tests / 723 assertions
- generated Railway JSON and promote-dropdown drift checks passed
- formatter and diff checks passed
- pre-commit affected-package checks passed, including Angular
build/package validation and 303 tests
2026-08-17 13:59:40 -07:00
Benjamin Taylor f07a7910d5 fix(ci): mock weather in the dojo e2e run, matching upstream
ag-ui's dojo-e2e sets AG_UI_MOCK_WEATHER=1 because the backend
tool-rendering demos call the live open-meteo API, which rate-limits CI's
shared egress IPs and hangs the suites. Our copy of the workflow drifted
and never picked it up, so those suites hit the live API — a latent flake
source, and a trace-divergence source now that the dojo asserts golden
event traces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 15:12:58 -05:00
Benjamin Taylor f6f6ed1872 fix(ci): stop enabling LangSmith tracing in the dojo e2e run
The dojo's new golden event-trace assertions (ag-ui #2392) compare
STATE_SNAPSHOT metadata exactly, and our e2e run was polluting it.

Injecting LANGSMITH_API_KEY makes langgraph-api force tracing on
(LANGSMITH_CONTROL_PLANE_API_KEY defaults to LANGSMITH_API_KEY, which
sets LANGSMITH_TRACING). The LangSmith client then merges every
LANGSMITH_*/LANGCHAIN_* env var into each run's metadata dict, and
langchain_core hands the tracer the same dict object the run config
streams out — so `langgraph dev`'s LANGSMITH_LANGGRAPH_API_VARIANT=local_dev
leaks into STATE_SNAPSHOT and every same-repo PR fails langgraph-python.

Upstream ag-ui runs these suites keyless against LLMock, which is why its
own CI is green on the same commit. Drop the key to restore parity; the
dojo never needed it, since OPENAI_BASE_URL already points at LLMock.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 15:11:38 -05:00
Ben Taylor fb0e2af897 docs(contributing): fix documentation typo (#6525)
## What does this PR do?

Fixes a typo in the contributor resources section: `documentatation` is
corrected to `documentation`.

This is a documentation-only change and does not affect runtime
behavior.

## Related PRs and Issues

- No related issue; checked the open pull requests for the same typo and
found no duplicate.

## Checklist

- [x] I have read the [Contribution
Guide](https://github.com/copilotkit/copilotkit/blob/master/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation (not applicable; no functionality changes)
- [x] "Allow edits by maintainers" is checked
2026-08-17 15:10:26 -05:00
Ben Taylor 06eb9a9de0 chore(examples): update CLI starter package versions (#6532)
## Summary

- update all 17 first-party starter sources used by the Intelligence CLI
to the current npm `latest` tags for CopilotKit and AG-UI packages
- refresh each affected npm lockfile and keep shared AG-UI/CopilotKit
types deduplicated with targeted overrides
- move the Mastra starter to `@ag-ui/mastra@1.1.2` and remove its
obsolete legacy peer-dependency workaround
- update the LangGraph FastAPI parity sibling so the integration parity
contract remains green

## Why

The CLI checks out these directories directly from this repository.
Several templates still pinned older CopilotKit, Channels, Angular, and
AG-UI releases, which meant newly generated projects started behind the
current published packages.

## Validation

- verified 146 CopilotKit and AG-UI manifest references across 17 CLI
starters against npm `latest` tags
- ran standalone `npm ci --ignore-scripts` for every affected package
manifest
- built every affected frontend plus the nested AgentCore runtime
Lambda, Claude agent, LangGraph agent, and MCP server
- ran all 14 starter channel typechecks
- ran `pnpm parity:check`
- ran targeted `oxfmt`, `oxlint`, and `git diff --check`

One unrelated existing AgentCore CDK root build error remains at
`lib/backend-stack.ts` (`gatewayTarget` and `toolLambda` are undefined);
the changed runtime Lambda itself builds cleanly.
2026-08-17 14:57:34 -05:00
Mike Ryan 5b9776a1a0 chore(examples): update CLI starter package versions 2026-08-17 12:31:41 -07:00
Ben Taylor e6d3629445 fix(sdk-python): emit tool call events for middleware-intercepted SDK Actions (#5372)
## Summary

Frontend SDK Actions intercepted by `CopilotKitMiddleware` could
disappear before the client received a tool-call lifecycle, leaving the
handler uncalled and the conversation without a tool result or follow-up
answer. The bridge now consumes the middleware's authoritative
intercepted-call state and publishes each call through the AG-UI adapter
at most once. The focused intercepted file has 8 tests and the manual
AG-UI file has 30 tests; the combined run is 38 tests, with the
supported-version matrix remaining CI-owned.

## Root cause

`CopilotKitMiddleware.after_model()` removes frontend calls before
LangChain's backend tool node and records those exact calls for
final-message restoration. The previous adapter fallback ignored that
state and reconstructed frontend ownership from `on_chat_model_end` plus
the run's action catalog. That duplicated classification, failed when an
action carried `function: None`, and never ran on Python 3.10 because
the async LangChain path emitted no model callbacks there.

## Changes

- `sdk-python/copilotkit/langgraph_agui_agent.py`
- publish middleware-classified calls from the existing chain-state
event path
- reuse one lifecycle materializer and the parent adapter's per-run ID
ledger
- remove raw model-end/action-schema reconstruction and the duplicate
logger assignment
- `sdk-python/tests/test_intercepted_tool_call_events.py`
- exercise the real `create_agent(..., tools=[])` middleware and AG-UI
path in streaming and non-streaming modes
- cover exactly-once identity, Python 3.10 restoration, multiple IDs,
metadata opt-out, backend negative space, and malformed action/state
shapes

## What is not changed

- backend tools still use LangChain's normal tool node
- `after_agent` still restores intercepted calls into the final
assistant snapshot
- tool-call emission metadata still controls streamed lifecycle
visibility
- explicit `copilotkit_emit_tool_call()` calls keep their existing
caller-controlled semantics

## Related PRs and Issues

Closes #4342.

## Test plan

- [x] Focused intercepted SDK Action regression file, 8 passed on Python
3.12. Covers the real `create_agent(..., tools=[])` path, streaming,
restoration, mixed ownership, chain metadata suppression, malformed
state, and action-shape isolation.
- [x] Existing manual tool-call materialization file, 30 passed on
Python 3.12.
- [x] Combined focused selection, 38 passed on Python 3.12, counted once
rather than adding a second 38-test total.
- [x] Ruff check and format check on both changed files, both passed.
- [ ] Python SDK CI matrix green on Python 3.10, 3.11, 3.12, 3.13, and
3.14
2026-08-17 12:58:37 -05:00
Ben Taylor cd0d38a073 fix(sdk-python): restore frontend tool history faithfully in LangGraph middleware (#5308)
## Description

Fixes the LangGraph middleware "lying" to the agent about frontend tool
execution (#4759). When a model turn calls a `useFrontendTool` handler,
the middleware strips the FE tool calls so the backend `ToolNode` only
runs calls it actually made, then rehydrates them afterward. Previously
the rehydration could leave the restored AI message and its tool history
inconsistent, so providers that require every tool call to have a
matching result would break, and the agent saw an incorrect picture of
what executed.

## Changes

- `_restore_intercepted_tool_call_history` rebuilds the intercepted FE
tool calls with synthetic `ToolMessage` results only for the restored
model history (so providers that require every `tool_call` to have a
result stay valid), and preserves the original AI message's `name`,
`additional_kwargs`, and `response_metadata` on restore.
- Tracks `original_tool_calls` in the CopilotKit private state so the
original call set is restored exactly.

## Testing

`uv run pytest tests/test_copilotkit_lg_middleware.py` — 68 passed,
including new cases asserting the restored AI message keeps its
tool-call ids, name, `additional_kwargs`, and `response_metadata`, and
that tool results line up with their calls.

Closes #4759

AI was used for assistance.
2026-08-17 12:54:26 -05:00
Jordan Ritter 52ce3ef47d Railway cost reduction: langgraph memory caps + harness worker recycle (#6505)
## What & why

Railway **memory** is the showcase project's largest cost line
(~$1.2k/mo; egress was already fixed by the July internal-URL flip).
This PR attacks it two ways:

1. **Cap langgraph integration backend memory.** The three `langgraph-*`
showcase backends run the in-memory `langgraph dev` server and were
drifting to 6–13 GB RSS each. Set `MALLOC_ARENA_MAX=2` +
`MALLOC_TRIM_THRESHOLD_` on the python/fastapi entrypoints (glibc arena
fragmentation on the many-core Railway host) and
`NODE_OPTIONS=--max-old-space-size=1536` scoped to the
langgraph-typescript **agent** process (not the sibling Next.js server)
to cap the V8 heap. All values use `${VAR:-default}` so explicit Railway
overrides win.

2. **Recycle harness workers** after `WORKER_MAX_JOBS` settled jobs
(default 100, per-replica jitter via `WORKER_MAX_JOBS_JITTER`) to
pre-empt slow Chromium/heap growth — the Gunicorn `--max-requests` /
Celery `max-tasks-per-child` pattern. The recycle exits non-zero
(`WORKER_RECYCLE_EXIT_CODE=42`) so Railway's `ON_FAILURE` policy
restarts a fresh container, and routes teardown through the **same
deregister-first `gracefulTeardown` → `drainFleetWorker`** SIGTERM uses
(made idempotent via a once-latch) so a recycling worker deletes its
roster row instead of stranding it ~180s (a transient dashboard
false-red).

## Commits (by area of concern)

- `perf(showcase): cap langgraph integration backend memory`
- `feat(showcase/harness): recycle workers after WORKER_MAX_JOBS to
pre-empt leaks`
- `feat(showcase/harness): deregister workers cleanly on recycle exit`

## Validation

- **207/207** harness tests pass (recycle counter/threshold/jitter,
disabled-when-0, deterministic jitter, idempotent teardown, recycle-exit
deregister — all red-green + mutation-checked).
- oxfmt clean, oxlint 0 errors, typecheck clean (the only `tsc` errors
are 4 pre-existing `frontend-matrix.test.ts` errors from a missing
generated catalog, unrelated to this diff).
- Reviewed via a 4-round code-review loop (converged: 0 mandatory
findings; Procedure-3 promotion audit `PROMOTE_TO_A: 0`).

## ⚠️ Deploy / rollout notes

- **`WORKER_MAX_JOBS` defaults to 100 (recycle ON).** Set
`WORKER_MAX_JOBS=0` to disable if you want a staged rollout. Recommend
enabling on **staging first**.
- **The langgraph memory savings are not yet empirically measured.**
`MALLOC_ARENA_MAX` reclaims glibc fragmentation on the many-core host
but the GB delta must be confirmed by a **staging A/B** (deploy, compare
RSS with/without) before quoting a dollar figure. The change itself is
safe/standard; only the magnitude is unverified.
- Precondition confirmed: harness-workers restart policy is `ON_FAILURE`
(in-repo docs) — the non-zero recycle exit will restart. Recommend
pinning `restartPolicyType: ON_FAILURE` explicitly in IaC as
defense-in-depth (follow-up).

## Follow-up (out of scope — pre-existing, surfaced by review)

The review surfaced a large pre-existing debt backlog in touched files,
none load-bearing on this PR's subject (all deferred). Notable
candidates for a separate PR:
- **Recycle-teardown hardening:** bound `pool.shutdown` in the recycle
path with a timeout so a hard-wedged Chromium can't delay the voluntary
exit (currently backstopped by the `/health` 503 healthcheck); make the
recycle-vs-SIGTERM exit-code deterministic; `latchOnce` sync-throw
guard.
- **Monitor write-only-red:** `onPidSaturation` writes RED with no clear
path (permanent false-red once tripped).
- **Entrypoint robustness:** `wait -n` under `set -e` making the
exit-code/diagnostic block dead code; `kill` of wrapper PIDs orphaning
node children; TS watchdog probing the `:8124` sidecar not `:8123`.
- **Control-plane parity:** `runControlPlane` missing the S3 backup cron
/ `hydrateProbeLastRuns` / `probe_runs` metric wiring that `boot()` has.
- Assorted test-hygiene (afterEach `doUnmock` leaks, a few vacuous
assertions, fixed-timer flakes).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-17 09:42:52 -07:00