Commit Graph

2626 Commits

Author SHA1 Message Date
Tyler Slaton 50245e56de test(react-core): correct feedback memoization coverage 2026-08-20 12:55:47 -07:00
Ben Taylor 6c955686eb feat(react-core): expose AG-UI raw event to feedback callbacks (#6289)
## Summary

React v2 feedback callbacks receive an assistant message without the
trace metadata carried by the direct AG-UI event that created it. This
slice exposes that metadata to thumbs callbacks without changing
canonical messages or future run inputs.

## Root cause

AG-UI keeps `rawEvent` on events while reducer-created assistant
messages remain protocol-clean. `StateManager` sees the direct start
event but previously discarded its correlation before
`CopilotChatMessageView` forwarded the message to feedback callbacks.

## Changes

- Store defined direct `TEXT_MESSAGE_START.rawEvent` metadata by agent,
thread, and message.
- Replace repeated scoped entries and prune them with message removal
and lifecycle cleanup.
- Return a cloned sidecar value through
`CopilotKitCore.getRawEventForMessage`.
- Enrich only thumbs-up and thumbs-down callback arguments at click time
across flat and virtualized rendering.
- Add production-path regressions and document the callback-only type.

## Out of scope

Canonical messages, future `RunAgentInput.messages`, render props,
message identity, stream ordering, snapshots, transformed chunks,
persistence, GraphQL, legacy React, Vue, Angular, and standardized trace
semantics remain outside this slice.

## Related PRs and Issues

Addresses #3039.

The callback-only scope follows
https://github.com/CopilotKit/CopilotKit/issues/3039#issuecomment-5086936452.
Related trace-correlation contract: #4634.

## Test plan

- [x] StateManager sidecar tests, 10 passed. Covers direct capture,
falsey values, replacement, scope isolation, cleanup, snapshots, and
chunks.
- [x] React v2 feedback tests, 4 passed. Covers real callback routing,
canonical and outbound cleanliness, render identity, and flat/virtual
paths.
- [x] Full package suites, 625 core tests, 1,475 React Core tests, and 2
script tests passed.
- [x] Typecheck, formatting, lint, and whitespace validation passed;
lint reported five pre-existing warnings.
- [ ] CI green (`static / quality`, `test / unit` on Node 20/22/24).

## Notes

The clean-base behavioral half of the reproduction remains unproved
because temporary worktree setup hung behind unrelated Git processes.
The PR makes no base execution claim for that half.
2026-08-20 14:17:24 -05:00
Ben Taylor d30983f406 refactor(vue): make tool-call memoization lint-valid (#5932)
## What does this PR do?

Makes the existing Vue tool-call memoization lint-valid without changing
rendering behavior.

The existing `v-memo` placement on the fallback renderer inside the
tool-call loop violates the `vue/valid-v-memo` placement constraint.
This refactor introduces a Vue-valid component boundary while preserving
the optimization contract:

- Named `#tool-call-<toolName>` and generic `#tool-call` consumer slots
remain reactive.
- Only the registered fallback renderer remains memoized.
- The fallback memo boundary matches the React counterpart's
renderer-level optimization.

The focused tests act as behavior-preservation and regression guards for
slot updates and fallback rerender prevention.

## Related PRs and Issues

- None.

## Verification

- `CI=1 pnpm exec nx run @copilotkit/vue:test -- --run
src/v2/components/chat/__tests__/CopilotChatToolCallsView.test.ts
--reporter=dot` — 14/14 passed.
- `CI=1 pnpm exec nx run @copilotkit/vue:check-types` — passed.
- `CI=1 pnpm exec nx run @copilotkit/vue:build` — passed.
- Direct ESLint from `packages/vue` on all touched source/test files —
passed.
- `git diff --check upstream/main...HEAD` — passed.
- `CI=1 pnpm exec nx run @copilotkit/vue:lint` remains blocked by 172
pre-existing errors in unrelated files; no package-wide lint cleanup is
included.
- The broad commit-hook suite encountered an unrelated SSR timeout; the
final tree was not changed afterward.

Coverage preserves generic and named slot updates, unchanged fallback
rerender prevention, tool-name changes, agent-specific renderer
selection, status/result behavior, and renderer precedence.

## Scope and exclusions

This is limited to the Vue tool-call rendering boundary, its parity
note, and focused tests. It does not change React behavior, package-wide
lint errors, attachment work, or unrelated rendering paths.

## Checklist

- [x] Contribution guide and package instructions reviewed.
- [x] Relevant Vue parity documentation updated.
- [x] Allow edits by maintainers is enabled.
2026-08-20 14:11:46 -05:00
Ben Taylor 27431412e6 fix(react-core): stop the compat CopilotKit wrapper pinning useSingleEndpoint (#6605)
Refs [OSS-888](https://linear.app/copilotkit/issue/OSS-888).

## The failure

A correctly assembled v2 integration 404s on its first browser request
while every static check passes and `GET /info` returns 200.

`packages/react-core/src/v2/index.ts:28` re-exports the **v1-compat**
`CopilotKit` wrapper, so it is the provider most integrations reach for.
That wrapper pinned:

```tsx
useSingleEndpoint={props.useSingleEndpoint ?? true}
```

which overrode the core's `"auto"` negotiation and forced single-route
transport. But **every** v2 handler defaults to `mode: "multi-route"`
(`endpoints/hono.ts:95`; `createCopilotEndpoint` is an alias at `:90`).
Nothing serves the single-route envelope the client sends, so the
runtime 404s while the provider looks connected.

## What this is *not*

The library defaults do not actually disagree. `CopilotKitProvider` (the
real v2 provider) leaves the flag undefined → `"auto"`, which probes
`GET /info` and falls back to the single-route envelope
(`core/agent-registry.ts` `fetchRuntimeInfoAutoDetect`) — it works
against **either** handler mode. Only the compat wrapper defeated that.

So this is one line of override, not a defaults mismatch needing a
direction chosen.

## Why four onboarding runs hit it, not one

The library bug alone doesn't explain a 100% failure rate. The shipped
`react-core` skill does:

`packages/react-core/skills/react-core/references/provider-setup.md` —
bundled in the npm tarball (`files: ["dist","skills"]`) — **mandated**
the compat wrapper, **forbade** `CopilotKitProvider` as "a subset of the
functionality", and mentioned `useSingleEndpoint` **zero times** across
~10 code samples. An agent following it wrote the 404 configuration
every time.

Meanwhile `skills/copilotkit-setup/SKILL.md` got it right, so the two
shipped skills contradicted each other and nothing gated either against
the code.

## The change

**Commit 1 — the library fix.** The prop already arrives through
`v2Props`, so dropping the override lets it stay `undefined` and inherit
`"auto"`. An explicit `useSingleEndpoint` still wins in both directions.

**Commit 2 — the docs and skills.** Correcting the default made ~15
pages' explanations false. Code samples that pass `{false}` stay valid
(they pin what negotiation would find anyway), so this corrects the
*explanations* rather than the samples — keeping every page true both
before and after release. Includes dropping the now-false causal claim
from the single-route-envelope diagnostic added in #6579.

## Compatibility

Safe for existing v1 apps. A v1 app on a single-route-only handler
(`copilotRuntimeNextJSAppRouterEndpoint` and friends) now does one `GET
/info` that 404s, then falls back to single-route and works. Cost is one
extra request on connect.

One edge case worth a reviewer's eye: if a deployment's `runtimeUrl` +
`/info` returns 200 from something that is *not* a multi-route
CopilotKit runtime (a catch-all proxy serving HTML, say), `"auto"` would
resolve to `rest`. Setting `useSingleEndpoint` explicitly remains the
escape hatch.

Conventional-commit note: this lands as `fix`, but it *does* change a
public default. Flag if you'd rather it carried a minor bump.

## Tests

- New `copilotkit-transport-default.test.tsx` — omitted → `"auto"`,
`{true}` → `"single"`, `{false}` → `"rest"`. Confirmed RED first
(`expected 'single' to be 'auto'`).
- `CopilotChat.readinessGate.test.tsx` depended on the old default to
avoid a REST probe. Single-route transport is a **precondition of that
fixture**, not the behaviour under test, so it now pins the flag
explicitly and its stale comments are corrected. Its coverage (readiness
gate across the real SSE boundary) is unchanged.
- `react-core` 1512 passed · `runtime` 2073 passed · `core` 668 passed.
- `pnpm check:plugin-skills` in sync (`skills/react-core/` is the
generated mirror).

`showcase/shell-docs`'s own vitest suite fails to load 35 files with
`Cannot find package 'react/jsx-dev-runtime'` — reproduced identically
on unmodified `origin/main`, so it is environmental in this checkout and
unrelated. All 183 tests that do run pass.

## Not addressed here

Nothing gates a shipped skill against the code it documents, which is
why `provider-setup.md` could contradict both the library and the
sibling skill indefinitely. Worth its own ticket.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-20 14:09:18 -05:00
Mike Ryan b8b19834a2 fix(runtime): unify the Intelligence key name and publish the wiring (refs OSS-881) (#6595)
## What does this PR do?

Closes the naming and documentation half of
[OSS-881](https://linear.app/copilotkit/issue/OSS-881). Paired with
CopilotKit/Intelligence#890, which adds `copilotkit verify` and tightens
the evaluation rubric.

### 1. One name for the Intelligence key

**Three** names for one value were live in CopilotKit's own
documentation, and following the wrong one with a CLI-provisioned
project yields an undefined key:

| Name | Where | Code readers |
| --- | --- | --- |
| `INTELLIGENCE_API_KEY` | what `copilotkit project select` writes; all
34 integration examples; the docs site | 34 |
| `COPILOTKIT_INTELLIGENCE_API_KEY` | 7 Channels package READMEs +
packaged skills | **0** |
| `COPILOTKIT_API_KEY` | `examples/slack`, `examples/teams`, and the
TSDoc on `CopilotKitIntelligence` itself | 2 |

`INTELLIGENCE_API_KEY` wins — it is the name the CLI provisions, and
changing it would break every scaffolded project in the wild.

- `COPILOTKIT_INTELLIGENCE_API_KEY` is **retired outright**. Nothing
ever read it, so there is nothing to keep compatible.
- `COPILOTKIT_API_KEY` stays **readable as a deprecated alias** in the
two examples that consume it, so an existing `.env` keeps working, and
is documented as deprecated everywhere it appears.

The third name was the worst placed: it was in the TSDoc on
`CopilotKitIntelligence`, which is what an IDE shows on hover.

This was not only untidy. The CLI's own `channels-preflight` accepts
`INTELLIGENCE_API_KEY` or `COPILOTKIT_API_KEY` — **not**
`COPILOTKIT_INTELLIGENCE_API_KEY`, the name the Channels READMEs told
people to set. So following a Channels README verbatim made `copilotkit
channels` warn that no runtime API key was present while the key sat
visibly in `.env`. After this PR the documented name is one preflight
accepts.

> [!NOTE]
> `NEXT_PUBLIC_COPILOTKIT_API_KEY` is a **different value** — the legacy
Copilot Cloud public key — and is deliberately left alone.

### 2. A real defect, not just naming skew

`skills/runtime/references/intelligence-mode.md` documented
`organizationId` as a `CopilotKitIntelligence` option, sourced from two
further env names (`COPILOTKIT_INTELLIGENCE_ORG_ID`,
`COPILOTKIT_ORG_ID`).

`CopilotKitIntelligenceConfig` has no such field — the copy-pasteable
sample it appeared in **would not compile**. Removed from the samples,
and the prose telling readers to fetch a value for it corrected. That
file is the only place those two names ever existed, which is very
likely why the failing validation run reported that "the runtime reads
`COPILOTKIT_INTELLIGENCE_API_KEY` and `COPILOTKIT_INTELLIGENCE_ORG_ID`".

### 3. Publish the Intelligence wiring

The wiring instructions existed only inside
`node_modules/@copilotkit/runtime/skills/`, and the only docs pages
mentioning `CopilotKitIntelligence` at all were the two Channels
frontends — so a developer on the plain web path had no page to reach it
from.

Adds **`/premium/connect-your-runtime`**: the wiring itself, how to
confirm the credential is actually consumed, the self-hosted
both-URLs-or-neither rule, and a troubleshooting table. Linked into both
navs, and the skills reference now points at the published page.

### 4. A guard so it cannot drift back

`scripts/validate-intelligence-env-names.ts` (`pnpm
check:intelligence-env-names`), wired to lefthook and a new workflow.

The workflow is **intentionally unfiltered**. The two workflows that
would otherwise cover this both filter: `plugin-skills-check` by
`paths:`, and `static/quality` by `paths-ignore: examples/**` — which is
exactly where the deprecated alias lives. Scoping the job would re-open
the hole it exists to close. Legitimate alias sites live in
`ALIAS_ALLOWLIST`.

## Related PRs and Issues

- [OSS-881](https://linear.app/copilotkit/issue/OSS-881) — needs
**both** PRs; neither closes it alone
- CopilotKit/Intelligence#890 — items 1 and 4 (`copilotkit verify` +
rubric contract 1.3.0)

## Verification

- Full lefthook pre-commit ran green: `check-plugin-skills`, `lint-fix`,
the new `check-intelligence-env-names`, and `test`/`publint`/`attw`
across **25 projects**.
- `examples/slack` `managed.test.ts` extended to cover **both** the
canonical name and the alias fallback, and proven non-vacuous — removing
the fallback turns the new test red.
- The drift guard proven non-vacuous the same way: reintroducing a
retired name fails it, exit 1.
- `oxfmt` and `oxlint` clean on every file touched (0 errors).

## 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

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-20 12:00:52 -07:00
Benjamin Taylor f30d3bfae5 docs: correct the useSingleEndpoint default across docs and shipped skills
The compat `<CopilotKit>` wrapper no longer pins `useSingleEndpoint` to `true`,
so every statement that it "defaults to single-route" or that a multi-route
backend "needs `{false}`" is now wrong. Code samples that pass `{false}`
explicitly stay valid — they pin what negotiation would find anyway — so this
corrects the explanations rather than the samples, keeping the pages true both
before and after the release.

The shipped `react-core` skill is the load-bearing one. `provider-setup.md`
mandated the wrapper, forbade `CopilotKitProvider` as "a subset of the
functionality", and never mentioned `useSingleEndpoint` across ~10 samples — so
an agent following it wrote the 404 configuration every time. It now documents
the transport and stops steering readers off the negotiating provider.

Also drops the false causal claim from the runtime's single-route-envelope
diagnostic (added in #6579), which named the wrapper's old default as the cause.

`skills/react-core/` is the generated mirror of `packages/react-core/skills/`,
synced with `pnpm sync:plugin-skills`.

Refs OSS-888.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 13:54:44 -05:00
Benjamin Taylor dc73af1dc4 fix(react-core): stop the compat CopilotKit wrapper pinning useSingleEndpoint
The `CopilotKit` wrapper is re-exported from `@copilotkit/react-core/v2`
(`v2/index.ts`), so it is the provider most integrations reach for. It pinned
`useSingleEndpoint={props.useSingleEndpoint ?? true}`, which overrode the core's
`"auto"` negotiation and forced single-route transport.

Every v2 handler defaults to `mode: "multi-route"`, so the pinned default made
the first browser request 404 while `GET /info` still returned 200 and looked
healthy. Four independent onboarding runs hit it and all four fixed it the same
way, with `useSingleEndpoint={false}`.

The prop already arrives through `v2Props`, so dropping the override lets it
stay `undefined` and resolve to `"auto"` — probe `GET /info`, fall back to the
single-route envelope — which works against either handler mode. An explicit
`useSingleEndpoint` still wins in both directions.

`CopilotChat.readinessGate.test.tsx` depended on the old default to avoid a REST
probe. Single-route transport is a precondition of that fixture rather than the
behaviour under test, so it now pins the flag explicitly and its comments are
corrected.

Refs OSS-888.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 13:42:13 -05:00
Ben Taylor 6787b30203 fix(core): preserve run IDs across SSE connect replay (#6253)
## What does this PR do?

- Uses each `RUN_STARTED.runId` during Runtime SSE connect replay.
- Preserves the first run association when later message snapshots are
cumulative.
- Keeps live message events able to correct an earlier provisional run
association.
- Adds `/connect` and StateManager regression coverage for multiple
server runs.

## Why?

A single `/connect` stream can contain multiple runs. This affects
custom Runtime streams and the built-in in-memory runner, which replays
historic runs through one reconnect stream. StateManager previously
stored replayed state under the connection input ID and reassigned
earlier snapshot messages to the latest run.

## Related PRs and Issues

- Closes #6252

## Validation

- `@copilotkit/core` tests: 59 files, 661 tests passed
- `@copilotkit/core` type check
- Pre-commit lint, test, publint, attw, and commitlint

## Checklist

- [x] I have read the Contribution Guide
- [x] Documentation is not required for this internal bug fix
- [x] Allow edits by maintainers is enabled
2026-08-20 13:03:50 -05:00
BenTaylorDev aa3fb29dce chore: release monorepo v1.68.3 2026-08-20 10:27:07 -07:00
David McKay 16e2c3a69a fix(runtime): send global telemetry properties on the field the sink reads
Folded into `properties`, they arrived in the per-event slot. That works and it
is the wrong place: the sink treats `global_properties` as the pass-through bag
for `oss.runtime.*` and spreads it into the analytics event, and v1's client
sends package name and version there for the same reason.

Sending them as their own field keeps a process-level fact separable from an
event's own properties the whole way to the warehouse.

It also moves conflict resolution. Two fields cannot collide in the SDK, so a
shared key survives on both and the sink decides, spreading the global bag last
and therefore letting the global win. That is the opposite of what most readers
expect from the word global, so the field docs now say not to reuse a key an
event already sets, and a test pins the behaviour rather than leaving it to be
discovered.
2026-08-20 10:19:12 -07:00
David McKay 86a9f9b016 feat(runtime): let a caller name itself on the telemetry it already sends
The v2 telemetry client sends exactly the properties each call site passes, so
there is no way for a product built on this runtime to be told apart in the
events that already go. The only route open to one was to send its own events,
which is a second pipeline describing the same runs.

`telemetryProperties` on the runtime is merged into every event. Set beside the
license token and for the same reason: it describes the caller rather than the
call, so every event should carry it whichever handler fired.

Per-event properties win on conflict. A call site describing one event knows
more than a value set once at construction, and letting the general overwrite
the specific would be the wrong way round.

No behaviour change when unset, and nothing is sent when telemetry is off, so
this adds no egress on its own.
2026-08-20 09:49:36 -07:00
Ben Taylor 9df63beeef fix(runtime): name useSingleEndpoint when a single-route envelope hits a multi-route runtime (#6579)
Closes
[OSS-882](https://linear.app/copilotkit/issue/OSS-882/add-to-existing-journeys-reach-for-the-v1-compat-copilotkit-wrapper).

## The failure

The v1-compatible `<CopilotKit>` provider pins `useSingleEndpoint` to
`true`
([`copilotkit.tsx:108`](https://github.com/CopilotKit/CopilotKit/blob/main/packages/react-core/src/components/copilot-provider/copilotkit.tsx#L108)),
so its startup handshake POSTs `{ method: "info" }` at the base path. A
multi-route runtime — the default — matches no route for that path and
answered a bare `{"error":"Not found"}`, indistinguishable from a wrong
`basePath` or an unmounted handler.

Two independent onboarding validation runs hit this on their first
browser attempt and each had to guess the cause. Both were
*add-to-existing-app* journeys; the greenfield one reached for
`CopilotKitProvider` and never saw it.

## What changed

**The runtime says what happened.** `detectSingleRouteEnvelope`
recognises a POST whose JSON body carries a `method` the single-route
endpoint accepts, and the multi-route handler uses it at the one point
routing gives up. The 404 now carries a `code` and a message naming the
prop, plus a `logger.warn` so it lands in the dev-server terminal too.
Deliberately conservative — wrong verb, non-JSON, unknown method, or a
JSON POST that isn't an envelope all stay ordinary 404s, unchanged in
status and shape.

**The client stops discarding it.** All four `/info` callers (two in
`agent-registry.ts`, two in `agent.ts`) threw away the response body and
reported only the status, so a server-side diagnosis reached nobody.
They now go through `runtimeInfoError`, which folds a string `message`
from the body into the thrown error. Any future server-side diagnosis
reaches the developer for free.

**Docs.** Five pages paired a v2 multi-route handler with `<CopilotKit>`
and never mentioned the prop. Rather than a warning under a snippet that
is still wrong to copy, the snippets themselves now pass
`useSingleEndpoint={false}`, with a short callout linking to the
provider/handler mapping.

Two pages were deliberately left alone: `backend/runtime-endpoints.mdx`
already documents the pairing in full, and `cookbook/arcade.mdx` uses
`mode: "single-route"` on purpose and already explains it.
`backend/copilot-runtime.mdx` keeps its snippet as-is — it pairs with
the v1 endpoint, where the default is correct — and gains the caveat
only on its "switch to v2 handlers" note.

Option 3 in the issue (reconsidering the compat default) is **not** in
this PR.

## Testing

### Both halves connect, end to end

Real `createCopilotRuntimeHandler` + real `CopilotKitCore` configured
the way the v1 wrapper configures it — no mocks on either side:

```
code   : runtime_info_fetch_failed
message: Runtime info request failed with status 404: Received a single-route
         request envelope ({ method: "..." }) but this runtime is mounted in
         multi-route mode, so the request matched no route. If the frontend uses
         <CopilotKit> from @copilotkit/react-core/v2, pass useSingleEndpoint={false}
         — that provider defaults it to true. Otherwise mount the runtime with
         mode: "single-route" to serve this envelope.

PASS — the diagnostic reached the client
```

The server-side `logger.warn` fired in the same run, carrying `{ url,
path, method: 'info' }`.

### Unit tests

`packages/runtime` — `single-route-envelope-diagnostic.test.ts` (2
positive, 5 control):

```
 ✓ src/v2/runtime/__tests__/single-route-envelope-diagnostic.test.ts (7 tests) 26ms
      Tests  7 passed (7)
```

`packages/core` — `runtime-info-error-detail.test.ts` (2 positive, 5
control):

```
 ✓ src/__tests__/runtime-info-error-detail.test.ts (7 tests) 267ms
      Tests  7 passed (7)
```

### Mutation checks

Every new test was verified to fail when its mechanism is broken, in
both directions.

Detector forced to `return null` — the two positives die, the four
controls hold:

```
   × names useSingleEndpoint when the envelope is an info call
   × diagnoses every method the single-route envelope accepts
   ✓ leaves an ordinary unmatched route as a plain 404
   ✓ leaves a JSON POST that is not an envelope as a plain 404
   ✓ leaves an unrecognized method name as a plain 404
   ✓ does not diagnose a non-JSON POST
```

Detector forced to `return "info"` — the controls die instead, proving
they are not vacuous:

```
   ✓ names useSingleEndpoint when the envelope is an info call
   ✓ diagnoses every method the single-route envelope accepts
   × leaves an ordinary unmatched route as a plain 404
   × leaves a JSON POST that is not an envelope as a plain 404
   × leaves an unrecognized method name as a plain 404
   × does not diagnose a non-JSON POST
```

`runtimeInfoError` with the detail dropped, then with the `typeof
message === "string"` guard removed — each kills a different pair:

```
mutation: detail dropped              → 2 failed | 5 passed
mutation: accept any message field    → 2 failed | 5 passed
restored                              → 7 passed
```

### Full suites, builds, docs

| Check | Result |
|---|---|
| `packages/core` full suite | `Test Files 60 passed (60)` / `Tests 662
passed (662)` |
| `packages/runtime` full suite | `Test Files 142 passed (142)` / `Tests
2067 passed (2067)` |
| `packages/core` `tsc --noEmit` | clean |
| `packages/runtime` `tsdown` | `416 files` — build complete |
| MDX compile, 5 edited pages | all `OK` |
| pre-commit `nx run-many -t test,publint,attw` | passed across affected
projects |
| CI on `f94d1ab0` | 72 pass, 3 skipping, 0 fail |

Both suites are fully green. An earlier revision of this description
reported 6
runtime failures as pre-existing on `main`; they were not. They were
artifacts
of a worktree whose `node_modules` had been assembled by hand, and a
proper
`pnpm install` cleared all of them along with the inspector-metadata
failures
from a stale `@copilotkit/shared` dist. `main` is clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-20 10:37:40 -05:00
Benjamin Taylor f36deaaa61 fix(docs): keep the useSingleEndpoint guidance mode-aware on multi-mode pages
Two pages document both transport modes and carry a single "point your
frontend at it" snippet serving every front door on the page. Baking
`useSingleEndpoint={false}` into those snippets traded one silent mismatch for
its mirror image: correct for the multi-route majority, wrong for anyone who
followed the `mode: "single-route"` example.

Both now state the rule conditionally next to the snippet instead of asserting
one side of it. Pages with a single handler mode (auth, custom-agent) are
unambiguous and keep the prop inline.

Also pins the one path where the diagnostic could have cost more than it gives:
`clone()` throws once a before-request middleware has drained the body, so the
detector must return null and let the plain 404 stand rather than surfacing a
500. The guard existed; nothing held it in place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 09:39:21 -05:00
Rainer Hahnekamp bf18677145 preserve interrupted run IDs when resuming Angular agents 2026-08-20 15:37:07 +02:00
Rainer Hahnekamp 736742f6f9 test(angular): document unobservable thread changes 2026-08-20 15:37:07 +02:00
Murat Sari ba41a31d7c feat: implement interrupt handling in AgentStore and add injectInterrupt function 2026-08-20 15:37:07 +02:00
JYbill 61ac927bae Merge branch 'main' into fix/state-manager-run-id 2026-08-20 20:01:26 +08:00
contextablemark b0233c4eb0 chore: release monorepo v1.68.2 2026-08-20 02:19:06 +00:00
Mark bef2c440ba fix(react-core): gate CopilotChat submission on runtime readiness (empty assistant response) (#6576)
## Problem

Fleet-wide "empty assistant response": the assistant-message container
mounts but never receives text. #5801 (first released 1.63.0) deferred
the runtime `/info` call to a React effect, widening the "provisional
agent" window; 1.63.2 exposed an `isReady` signal on `useAgent` but
`CopilotChat` never consumed it. A chat submitted during the provisional
window is committed to the provisional agent and then lost when `/info`
swaps in the real agent — the user message and streamed assistant text
disappear, so the assistant bubble renders empty.

This was confirmed with a controlled SSE A/B: stock 1.68.1 does forward
`TEXT_MESSAGE_START → TEXT_MESSAGE_CONTENT → TEXT_MESSAGE_END` (the
runtime is fine — not the in-memory runner / #5837), but the `/info`
agent swap drops the rendered messages; restoring a readiness guard
makes the identical SSE render correctly.

## Fix

`CopilotChat` now consumes `isReady` from `useAgent` and withholds
`onSubmitMessage` until the runtime is ready:

```
- const { agent } = useAgent({ ... });
+ const { agent, isReady } = useAgent({ ... });
...
- onSubmitMessage: onSubmitInput,
+ onSubmitMessage: isReady ? onSubmitInput : undefined,
```

`CopilotChatInput` already derives `canSend` (and its Enter handler)
from `onSubmitMessage`, so withholding it while not-ready (a) disables
the send control and (b) makes Enter a no-op that **preserves** the
composer text — the message can't be committed to the doomed provisional
agent. No runtime/runner changes; no fixture re-recording.

## Red–green proof

New test `CopilotChat.readinessGate.test.tsx` drives the real readiness
race against the real `CopilotChat` submit path: holds the runtime in
Connecting (deferred `/info`), sends during the provisional window, then
resolves `/info` (the real status-change re-render that flips `isReady`)
and asserts the message survives to render an assistant response.

- **RED** (fix reverted): the chat body contains only chrome text — no
user message, no assistant response (the empty-container symptom).
- **GREEN** (fix applied): assistant text renders; passes 3×
consecutively (deterministic).
- Mutation-verified: reverting the fix reproduces RED.

## Verification

- react-core: **1468 tests pass** (0 regressions; 3 pre-existing
web-inspector `localStorage` jsdom-env file errors are unrelated and
present with and without this change).
- react-ui: **69 tests pass**.
- react-core typecheck (`tsc --noEmit`): **0 errors**.

## Follow-up (not in this PR)

The showcase D4 probe driver
(`showcase/harness/src/probes/drivers/d4-chat-roundtrip.ts`) should wait
for the send control to be enabled before pressing Enter (poll
`[data-testid="copilot-send-button"]` `disabled === false` after
typing). Omitted here because it can't be red-green'd without a live
showcase backend. Note this fix makes the follow-up more relevant: with
send gated, a probe that types + Enters during the provisional window
now silently no-ops.

Ref: #5801

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_017t7HsmM31NHNmUQrHF47pm
2026-08-19 19:09:31 -07:00
Jordan Ritter e34bdb9fc3 fix(react-core): hide suggestion pills until ready; rework SSE test to real wrapper
Withholding onSelectSuggestion left the pill visually enabled but inert,
silently dropping a click during the provisional (!isReady) window. Hide
the pills until isReady (pass an empty suggestions list so the view's
existing hasSuggestions gate keeps them off-screen); retain the handler
gate as defense-in-depth for custom chatView slots. The suggestion-gate
test now asserts the pill is absent while provisional and appears/works
once ready.

Rework the production-shaped SSE regression to render the real public
CopilotKit wrapper (components/copilot-provider/copilotkit) with
runtimeUrl + agent="agentic_chat", advertising agentic_chat in the mocked
single-endpoint info response and relying on the wrapper's default
useSingleEndpoint=true — removing the synthetic GET /info 404 fallback so
the test exercises the same provider chain and POST info/agent-run path
as Showcase.
2026-08-19 16:52:47 -07:00
Benjamin Taylor 6f58b2c6a4 fix(runtime): unify the Intelligence key name and publish the wiring (refs OSS-881)
Three names for one value were live in CopilotKit's own documentation, and
following the wrong one with a CLI-provisioned project yields an undefined
key:

- `INTELLIGENCE_API_KEY` — what `copilotkit project select` writes, used by
  all 34 integration examples and the docs site.
- `COPILOTKIT_INTELLIGENCE_API_KEY` — the seven Channels package READMEs and
  the packaged skills. Nothing ever read it.
- `COPILOTKIT_API_KEY` — the Slack and Teams examples, and the TSDoc on
  `CopilotKitIntelligence` itself, which is what an IDE shows on hover.

`INTELLIGENCE_API_KEY` wins, because it is the name the CLI provisions and
changing it would break every scaffolded project in the wild.
`COPILOTKIT_INTELLIGENCE_API_KEY` is retired outright — no code read it.
`COPILOTKIT_API_KEY` stays readable as a deprecated alias in the two
examples that consume it, so an existing `.env` keeps working, and is
documented as deprecated everywhere it appears.

The skills reference also documented `organizationId`, sourced from a fourth
and fifth env name, as a `CopilotKitIntelligence` option. It is not one:
`CopilotKitIntelligenceConfig` has no such field, so the copy-pasteable
sample it appeared in would not compile. Removed from the samples, and the
prose that told readers to fetch a value for it corrected.

The Intelligence wiring itself was published only inside
`node_modules/@copilotkit/runtime/skills/`, and the only docs pages showing
`CopilotKitIntelligence` were the two Channels frontends — so a developer on
the plain web path had no page to reach it from. Adds
`/premium/connect-your-runtime`, which covers the wiring, how to confirm the
credential is actually consumed, and the self-hosted two-URL rule.

`scripts/validate-intelligence-env-names.ts` keeps this from drifting back.
It runs unfiltered in CI on purpose: the two workflows that would otherwise
cover it filter paths, and static/quality ignores `examples/**` — exactly
where the deprecated alias lives.
2026-08-19 17:50:09 -05:00
Benjamin Taylor f94d1ab0fb fix(runtime): name useSingleEndpoint when a single-route envelope hits a multi-route runtime
The v1-compatible `<CopilotKit>` provider pins `useSingleEndpoint` to `true`,
so it POSTs `{ method: "info" }` at the base path. A multi-route runtime — the
default — matches no route for that path and answered a bare `{"error":"Not
found"}`, which is indistinguishable from a wrong `basePath` or an unmounted
handler. Two independent onboarding validation runs hit this on their first
attempt and had to guess the cause.

The runtime now recognises the envelope at the one point multi-route routing
gives up, and answers the 404 with a message naming the prop, plus a
`logger.warn` so it also lands in the dev server terminal. Status and shape are
unchanged for every other miss.

That message was reaching nobody: all four `/info` callers threw away the
response body and reported only the status. They now route through
`runtimeInfoError`, which folds a string `message` from the body into the
error — so any future server-side diagnosis reaches the developer too.

Docs: five pages paired a v2 multi-route handler with `<CopilotKit>` without
mentioning the prop. Their snippets now pass `useSingleEndpoint={false}` and
link to the provider/handler mapping. `backend/runtime-endpoints.mdx` already
documents the pairing and is untouched; `cookbook/arcade.mdx` deliberately uses
single-route mode and already explains it.

Closes OSS-882

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 17:40:31 -05:00
Jordan Ritter f8160e09b9 fix(react-core): gate suggestion submission on readiness + production-shaped SSE regression test 2026-08-19 14:33:25 -07:00
Tyler Slaton 9087121bd5 fix(web-inspector): preserve state across pop-out 2026-08-19 14:03:12 -07:00
Alem Tuzlak 1ef16b6789 feat(web-inspector): pop the Inspector into its own window
Keep the same live Inspector session in a named browser popup, restore it when the popup closes, and document the workflow.
2026-08-19 14:03:12 -07:00
Jordan Ritter 9b0e3dc88a fix(react-core): gate CopilotChat submission on runtime readiness (empty assistant response) 2026-08-19 13:37:03 -07:00
Tyler Slaton f1b26e4aa8 fix(react-core): hide local inspector action in production 2026-08-19 13:28:26 -07:00
Tyler Slaton 367e7bda15 feat(react-core): add local message inspector links 2026-08-19 13:28:26 -07:00
Benjamin Taylor 4df1e3dccd docs(a2ui): require a literal-or-binding union for bound props (refs OSS-857)
Three findings from a LangGraph TypeScript onboarding run, plus the
supporting re-export.

The A2UI binder decides whether to resolve a `{ path }` binding by
inspecting the prop's Zod type: `scrapeSchemaBehavior` classifies a
`ZodUnion` containing an object with a `path` key as DYNAMIC and
everything else as STATIC, and STATIC returns the value untouched. A
bound prop declared as a plain `z.string()` therefore reaches the
renderer as the raw `{ path: "/origin" }` object, and the first thing
that renders it as text throws React error #31. The fixed-schema page
said the opposite — that renderer props are "plain z.string(), not a
path-or-literal union" — so the obvious declaration produced an opaque
crash. The reference cell already declares the union and carries a
comment explaining why, but that comment sits outside the
`definitions-types` region marker and so never reaches the page.

`DynamicStringSchema` is real; it lives in `@a2ui/web_core`, which is a
transitive dependency of `@copilotkit/a2ui-renderer` and so not
reliably importable from application code. Re-exported here with its
numeric/boolean/list siblings and their types.

The LangGraph quickstart's troubleshooting advice told everyone with a
connection problem to swap `localhost` for `0.0.0.0` or `127.0.0.1`.
That is backwards for the Node runtime: `langgraphjs dev` defaults to
`--host localhost`, which Node resolves to IPv6 and binds `::1` only,
so `127.0.0.1` is refused by the same running server. The Python CLI
defaults to `--host 127.0.0.1` and behaves the other way, so the advice
is now split across the page's existing Python/TypeScript language tabs
instead of stated once in shared prose.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 14:48:49 -05:00
Tyler Slaton f8b5de55ab feat(runtime): report managed Channel drops and recoveries as telemetry (refs OSS-825) (#6465)
## Why

A managed Channel that loses its gateway link is invisible outside the
host process. The only trace is the injected `log` seam, wired to
`logger.warn` — which for a self-hosted or Railway-hosted runtime
reaches nobody who can act on it.

## What

- `oss.runtime.channel_session_dropped` — carries the cause already
computed for the log line (`reason`, and the transport `code` when the
transport named one).
- `oss.runtime.channel_session_recovered` — carries `downForMs`, so
outage duration is measurable rather than inferred from log timestamps.
- The drop cause is now replayed on every "still down" reminder. In prod
those lines read `still down after 233134s; Phoenix is retrying` with
**no cause at all**, so an operator had to scroll back to the first line
— 15 minutes earlier, or hours, given the exponential backoff — to learn
it was an HTTP 502.

## Deliberate choices

- **No Channel name in the events.** It is a customer-chosen identifier
that can carry business meaning, so it stays out of anonymous OSS
telemetry. No message content or credentials either. Per-channel
aggregate counts still work without it.
- **An `online` transition with no preceding drop emits nothing** — a
session can report online without having dropped, and that is not a
recovery.
- **Capture is fire-and-forget with failures swallowed**, the same
contract `fireInstanceCreatedTelemetry` uses. The `try` also covers a
`capture` that throws synchronously. Telemetry must never break a live
session.
- The `gave_up` line's OSS-670 wording is untouched — it deliberately
says retries continue, and that is now accurate.

## Testing

Three tests added to `channel-manager-reconnect.test.ts`, each watched
fail first: the dropped event with its cause, the recovered event with a
positive duration, and the no-bogus-recovery guard. A fourth pins the
cause on the repeat log line.

```
✓ src/v2/runtime/core/__tests__/channel-manager-reconnect.test.ts (11 tests)
Tests  11 passed (11)
```

Wider run: 106 tests pass across `core/__tests__` and `telemetry`. Two
notes, both verified pre-existing by stashing this branch's changes and
re-running:

- `channel-manager-recovery.test.ts` fails to *load* in my worktree
(`Cannot find package '@copilotkit/channels-slack/render'`) — a
subpath-export resolution artifact of a worktree with symlinked
`node_modules`, identical with these changes stashed.
- `tsc --noEmit` reports 11 errors, the same 11 before and after this
change, none in the files touched here.

`oxfmt` and `oxlint` clean on all three files. Lefthook was bypassed on
the commit because of the same worktree `node_modules` symlinking; I ran
both tools manually over exactly the staged files instead.

Refs OSS-825.
2026-08-18 17:49:55 -07: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
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
xiaoqinvar 2f2481a048 Merge branch 'main' into codex/pr-6253-review-fix 2026-08-18 17:56:16 +08:00
xiaoqinvar 08f05b39f4 fix(core): preserve live message run reassignment 2026-08-18 17:28:33 +08: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
Lukas Moschitz d45a478a83 fix(channels-slack): rename decimal_allowed to Slack's is_decimal_allowed
`SlackNativeProps` declared `decimal_allowed`. Slack's `number_input` field is
`is_decimal_allowed`. Slack accepts a message whole or not at all, so the
unrecognised key refused the entire `chat.postMessage` call with
`invalid_blocks: invalid field at /blocks/N/element` and ended the Channels
delivery that carried it — no exception reached the caller and nothing arrived
in the channel.

The typed path was therefore the broken one: a developer who bypassed our types
and hand-wrote `is_decimal_allowed` got it right, while everyone using
`Slack.Element.NumberInput` silently lost the message.

Verified live against Slack, one delivery per case:

- `decimal_allowed` alone — refused, `invalid_blocks: invalid field at
  /blocks/0/element`
- `is_decimal_allowed` alone — delivered
- both names present — refused with the same error, which isolates the unknown
  key as the cause: Slack's required field is satisfied and the payload is
  otherwise identical to the one that delivers

Not treated as a breaking change and no alias is kept. The old name never
worked, so no caller can depend on its behaviour, and keeping it would preserve
the trap. Removing it converts a silent message loss into a compile error that
names the right field.

Also adds a test comparing every field name `native.ts` declares against
Slack's own Block Kit declarations in `@slack/types` (already a dependency),
parsed with the TypeScript parser rather than matched as text. Names
`@slack/types` does not cover — `container`'s `blocks`, `rich_text_list`'s
`offset`, `card`'s `slack_icon` and `subtext`, and the whole
`data_visualization` vocabulary — are listed individually with the reason, so
the check states its coverage instead of implying completeness. Floors on the
vocabulary size and on the number of names actually compared keep a collapsed
comparison from passing as success.

Sweeping the remaining names turned up no other mismatch.
2026-08-17 12:49:10 +02:00
Murat Sari 703944880d feat(angular): expose agent capabilities 2026-08-16 22:11:36 +02:00
Murat Sari 5720cd7fdc feat(angular): support fetch credentials 2026-08-16 21:03:52 +02:00
JYbill 12e89e60fa Merge branch 'main' into fix/state-manager-run-id 2026-08-15 17:48:03 +08:00
tylerslaton 1f9b60b231 chore: release monorepo v1.68.1 2026-08-14 21:05:45 +00:00
tylerslaton f8cb4d2447 chore: release channels v0.9.0 2026-08-14 20:42:01 +00:00
tylerslaton e6864b6bdd chore: release monorepo v1.68.0 2026-08-14 20:11:31 +00:00
Lukas Moschitz f248a7eb30 feat(channels-slack): render table cells as rich_text when they carry markup
Portable <Cell> content was always emitted as a Slack `raw_text` cell, which
is literal: markdown links, Slack link syntax and bare URLs all rendered as
plain characters, so there was no way to get a clickable link or bold text
into a table cell through the portable vocabulary.

Body cells whose content contains a link, bold, italic, strikethrough or
inline code are now emitted as a `rich_text` cell. Plain content still
produces the byte-identical `raw_text` payload, and header cells always stay
`raw_text` (Slack renders them bold already, and `rich_text` is not allowed
in a `data_table` header cell).

The conversion reuses `markdownToMrkdwn` — the package's single source of
truth for the portable dialect — and tokenizes its `mrkdwn` output into
rich-text runs, so the package keeps one markdown parser. The 2000-char cell
budget now applies to the visible text of a rich cell.
2026-08-14 11:52:53 -07:00
Maximiliano Korp eb3f430ae1 feat(runtime): mark Learning config experimental 2026-08-14 10:43:51 -07:00
Mike Ryan 99da13de53 fix(runtime): preserve Learning compatibility contracts 2026-08-14 10:34:44 -07:00
Mike Ryan a9f283ab55 feat(runtime): assign threads to Learning Containers 2026-08-14 10:34:27 -07:00