Commit Graph

12905 Commits

Author SHA1 Message Date
Benjamin Taylor 2f85aa7bc1 feat(vue): add isMobileViewport helper for drawer mobile mutual-exclusion
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 10:40:07 -05:00
Ran Shemtov 01354148b1 fix(react-core): render nothing for unhandled tool calls instead of auto default card (#5769)
## What

Revert the v2 tool-call resolver so an **unhandled** tool call (no
per-tool or wildcard renderer registered) renders **nothing** again,
instead of auto-painting the built-in `DefaultToolCallRenderer` card.

Showing the card is opt-in, exactly as before:

| Call | Result |
|---|---|
| `useDefaultRenderTool()` | built-in default tool-call card |
| `useDefaultRenderTool({ render })` | the caller's custom UI |
| neither | nothing renders |

## Why

The auto-card default was introduced in commit `ba60df5d33`
(2026-05-07), buried as the **last bullet** of a commit titled
`feat(showcase/langgraph-python): add per-tool testids`. It shipped
silently in **v1.57.2** with **no CHANGELOG entry** and is still live
through 1.61.2.

Effect: in v2, **every** agent tool call that surfaces in chat without a
registered renderer paints a card exposing the tool name, raw arguments
JSON, and raw result JSON — in production, for all customers. This is
how internal A2UI plumbing tools (`generate_a2ui`, `render_a2ui`,
`log_a2ui_event`) leaked into deployed showcase demos, and it equally
leaks any customer's own un-rendered tools.

Pre-1.57.2 behavior was `return null` (invisible). This restores that
opt-in contract.

## Changes

- `use-render-tool-call.tsx`: resolver returns `null` when no
per-tool/wildcard renderer matches.
- Removed the now-dead `defaultToolCallRenderAdapter` + `__testOnly_`
export + unused imports.
- Rewrote `use-render-tool-call.test.tsx` to drive the real
`useDefaultRenderTool` → real `useRenderToolCall` through a notifying
mock core, asserting all 3 states (card / custom / nothing).

## Showcase impact

None required. The opt-in `tool-rendering-*-catchall` demos already call
`useDefaultRenderTool()`, so their cards (and e2e specs) are unchanged.
No a2ui/declarative spec asserts the card — those demos simply stop
leaking it in prod.

## Verification

- `nx test react-core`: 1368 pass (107 files), incl. the new 3-scenario
suite.
- `nx check-types react-core`: clean.
- `nx build react-core`: clean.
- oxfmt `--check`: clean.
2026-07-01 16:48:26 +02:00
Ran Shemtov d0f4f27458 Merge branch 'main' into claude/happy-dirac-f6ed5e 2026-07-01 16:48:12 +02:00
Alem Tuzlak 7fe51c49ae feat(bot-intelligence): render discrete thread.post as Block Kit, not flattened text
adapter.post/update now stream post/update render frames carrying the IR (via a
shared per-turn seq with the run renderer) when a render sink is wired, so the
Connector Outbox renders full Block Kit — rich JSX (sections, buttons, images)
from thread.post is preserved instead of irToText-flattened. Falls back to the
egress op path when no render sink (in-memory tests). 46 tests pass.
2026-07-01 14:46:29 +02:00
lukasmoschitz 62c7f11394 feat(web-inspector): rename the Memories tab to Learning (#5787)
## What

Renames the inspector's user-facing feature name from **Memories** to
**Learning**:

- The menu tab label (`Memories` → `Learning`).
- The enabled-state panel heading (`Memory store` → `Learning`).

## What's intentionally left unchanged

- The internal `"memories"` menu key, the `Brain` icon, and the REST
wiring.
- Entry-level copy that refers to individual records (the `Search
memories…` box, the `No memories yet` empty state) — the *feature* is
"Learning" but the *entries* are still memories.

## Notes

- Not touched here: the locked-teaser copy (`Long-term memory` / "isn't
enabled on this deployment") — flagged for a separate decision on
whether the teaser should also say "Learning".
- Updated the tab-presence test accordingly; `web-inspector` tests pass
(71).

Targets `mme/memory-core`.
2026-07-01 14:39:11 +02:00
Lukas Moschitz e481011efa feat(web-inspector): rename the Memories tab to Learning
Rename the inspector's user-facing feature name from Memories to Learning:
the menu tab label and the enabled-state panel heading. The internal
"memories" menu key, the Brain icon, the REST wiring, and entry-level copy
(search box, empty states) are unchanged. Update the tab-presence test.
2026-07-01 14:26:11 +02:00
Alem Tuzlak 5ef2f6ce0a feat(bot-intelligence): HTTP render-frame streaming + live Phoenix channel (OSS-402)
Complete the SDK realtime path so managed replies reach 1:1 bot-slack UX end to
end, over either transport:

- HttpRenderEventSink streams render frames to app-api's durable accept route
  (/api/bots/deliveries/:id/render-events/accept), reusing HttpDeliverySource's
  per-delivery scope (now carried on the claim response). This lets the whole
  loop run over HTTP against app-api directly — no realtime gateway required —
  which the Connector Outbox then renders to Slack.
- intelligenceAdapter defaults its render sink to the HTTP one when it builds
  the HTTP transports; injected in-memory sources still use the egress-backed
  fallback.
- connectPhoenixHostedBotChannel wires the real Phoenix Socket/Channel behind
  the HostedBotChannel contract for the production realtime-gateway path
  (joins hosted_bots:project:{id}, push→ok/error, on-push), backed by phoenix@1.8.

Tests: 45 bot-intelligence pass (HttpRenderEventSink scope-echo + no-scope guard);
check-types green.
2026-07-01 14:05:45 +02:00
Alem Tuzlak 5d4b7ff393 feat(bot-intelligence): stream RenderEvents over the realtime gateway (OSS-402)
The managed run renderer now mints semantic render frames (run_started /
text_delta / text_end / tool_start / tool_end / interrupt / run_error /
finalize) and streams them through a RenderEventSink, assigning a monotonic
seq per (turnId, slot) and awaiting a durable render_accepted receipt for each
via a serial chain (seq fixed at enqueue so order holds regardless of AG-UI
callback scheduling). Add PhoenixRealtimeTransport (DeliverySource +
RenderEventSink) speaking the frozen OSS-395 contract: render_event frames ->
render_accepted receipts, then delivery.complete_requested (completion INTENT
with acceptedThrough high-water pointers) — never a committed delivery.ack
(app-api owns ack), and no Slack credentials in the SDK. When no realtime sink
is wired the renderer falls back to translating frames into post ops on the
EgressSink so the HTTP demo keeps posting plain-text replies.
2026-07-01 12:29:50 +02:00
Alem Tuzlak 409935d4a1 feat(bot-slack): extract Bolt-free run renderer behind ./render (OSS-403)
Replace the WebClient dependency in createRunRenderer with an injected
SlackRenderTransport (setStatus / postMessage / updateMessage) — the 4 native
streaming ops were already injected via NativeStreamTransport. Add a ./render
subpath export exposing the transport-agnostic renderer plus the pure IR->Block
Kit / modal / mrkdwn helpers, so the gateway-side Connector Outbox can drive the
identical renderer for managed replies (1:1 UX parity) without forking it. The
native Slack adapter wraps its WebClient into the transport; behavior unchanged.
2026-07-01 12:29:14 +02:00
Alem Tuzlak a03cd7db8b test(runtime): supply bots:[] in the intelligence runtime-like factory
CopilotIntelligenceRuntimeLike.bots became required with the managed-bots
runtime work (OSS-360); the get-runtime-info test factory still omitted it,
failing check-types. Add the missing field.
2026-07-01 12:28:20 +02:00
Ran Shemtov c26b89dc51 fix: inspector shows no AG-UI events for per-thread agent clones (#5784)
Fixes #5773, a regression of #3872. The inspector event timeline stays
empty during runs — no `RUN_STARTED`, `TEXT_MESSAGE_*`, `TOOL_CALL_*`,
etc. — while connection status, agents, context and tools still
populate.

The cause is the same one #3872 fixed and that has since been dropped:
`useAgent` runs a per-thread *clone* of the registry agent, and clones
are never added to the registry. The inspector only subscribes to AG-UI
events via `onAgentsChanged`, so it never sees the clone that actually
runs, and every `recordAgentEvent` sits on the wrong instance. The
`onAgentRunStarted` bridge #3872 added is no longer declared on
`CopilotKitCoreSubscriber`.

Re-landing it against the current run path: `RunHandler` fires
`onAgentRunStarted` with the real run instance at the start of
`runAgent` (top-level only, so recursive tool follow-ups don't
re-subscribe) and `connectAgent`, and the inspector subscribes to that
instance. `subscribeToAgent` already keys by `agentId` and replaces, so
the clone cleanly supersedes the registry subscription and there's no
leak. Per-thread cloning is untouched.

Added a core regression test driving a run on a clone that isn't in the
registry and asserting the event fires once with that instance, plus
that recursive follow-up runs don't re-notify. Core and web-inspector
both typecheck; core suite green.
2026-07-01 10:29:29 +02:00
Ran Shemtov 55e0a884b3 Merge branch 'main' into fix/inspector-subscribe-per-thread-clone-events 2026-07-01 10:10:12 +02:00
Ran Shemtov e1321406bc fix(vue): enable A2UI via catalog-on-provider path (#5777) 2026-07-01 08:33:53 +02:00
Ben Taylor cbb24952c5 feat(web-inspector): add unified thread debugger (#5768)
## Summary
- Adds the unified web-inspector thread debugger surface as a standalone
`cpk-thread-inspector` custom element backed by a
`ThreadDebuggerProvider` contract for thread metadata, messages, AG-UI
events, and state.
- Keeps the existing full Web Inspector shell compatible by making the
internal `cpk-thread-details` element extend the new shared inspector
implementation.
- Normalizes persisted AG-UI event history into a first-class Timeline
view with run/message/tool/state/raw rows, source-event links into the
Raw AG-UI Events tab, metadata pills, and raw-event preservation for
top-level fields.
- Improves timeline/state loading behavior for standalone and
runtime-backed usage: state stays lazy-loaded, provider/runtime/header
swaps refetch safely, stale async responses are ignored, and
message-backed or raw-only threads no longer show an empty first tab.
- Adds a standalone web-inspector dev harness with AG-UI events,
Messages only, and Raw event only scenarios for shared UI validation
outside an app shell.
- Updates package/dev configuration for the harness (`dev:standalone`,
Vite dev dependency/config allowlist) and bumps
`@copilotkit/web-inspector` to `1.61.3`.

## Tests and Checks
- Expanded `packages/web-inspector` tests for the provider contract,
normalized timelines, raw event rendering, lazy state loading, stale
request handling, header-driven refetches, runtime fallbacks, and
first-tab timeline population.
- PR checks currently passing include build, check-types, format,
oxlint, package-quality, unit on Node 20/22/24, Python unit tests, shell
script tests, Validate Showcase, bundle-size, config/binary checks, and
integration starter/showcase jobs.
- Manual validation path documented in
`packages/web-inspector/README.md`: `pnpm nx run
@copilotkit/web-inspector:dev:standalone`, then validate AG-UI events,
Messages only, Raw event only, source-event navigation, and State at
`http://127.0.0.1:5177/`.

## Companion PR
- Intelligence managed debugger integration:
https://github.com/CopilotKit/Intelligence/pull/461

<img width="928" height="724" alt="Screenshot 2026-06-29 at 2 50 47 PM"
src="https://github.com/user-attachments/assets/c7936f1c-0780-4c0c-b5d2-1890db611460"
/>
<img width="927" height="1325" alt="Screenshot 2026-06-29 at 2 49 50 PM"
src="https://github.com/user-attachments/assets/48134189-7def-4312-b4d1-4bad28a93a12"
/>
e733" />
<img width="927" height="408" alt="Screenshot 2026-06-29 at 2 49 58 PM"
src="https://github.com/user-attachments/assets/1773b1f7-10a1-4905-ab6e-418d13031a49"
/>
<img width="929" height="583" alt="Screenshot 2026-06-29 at 2 50 07 PM"
src="https://github.com/user-attachments/assets/8f38f91d-296b-41dd-8b50-8c60739b387d"
/>
2026-06-30 21:50:44 -05:00
serhiizghama f9bba02c20 fix(web-inspector): subscribe to the running clone via onAgentRunStarted
The inspector subscribed to AG-UI events only through onAgentsChanged, which
skips per-thread clones, leaving the event timeline empty during runs
(regression of #3872). Subscribe to the run instance reported by
onAgentRunStarted so its events are recorded.
2026-07-01 08:23:53 +07:00
serhiizghama 7f0ca7eabf fix(core): notify subscribers of the agent instance at run start
Add an onAgentRunStarted subscriber event fired from RunHandler.runAgent and
connectAgent with the instance that actually runs. Per-thread clones (from
useAgent) are not in the agent registry, so onAgentsChanged never fires for
them and subscribers that only track registry agents can't observe their run.
2026-07-01 08:23:53 +07:00
Jordan Ritter 7d6a2b2a9a docs(runtime): correct forwardHeaders allowlist JSDoc accuracy
Empty/whitespace-only allow/deny/denyPrefixes entries are trimmed and
dropped before the allowlist-mode decision, so allow: [""] stays in
denylist mode rather than switching on exclusive allowlist mode. Also
document that allowlist mode bypasses the built-in default denylist, so
integrators must not allow-list protected/platform headers unintentionally.
2026-06-30 17:23:05 -07:00
Jordan Ritter dde79d1c89 fix(runtime): make forwardHeaders deny authoritative in allowlist mode
`shouldForwardHeader` returned early on `policy.allow` and silently ignored
the integrator's `deny`/`denyPrefixes`, so a header listed in BOTH `allow`
and `deny` still forwarded — a footgun on a security feature.

Rework the predicate so the integrator's own `deny`/`denyPrefixes` (exact,
case-insensitive, and prefix) always strip, including in allowlist mode:
`allow` selects the candidate set, `deny` subtracts from it. The built-in
default denylist is unchanged and still applies only in denylist mode (an
explicit `allow` is a deliberate opt-in), so only the integrator's OWN deny
subtracts from an allowlist.

Also harden `resolveForwardHeadersPolicy`: trim and drop empty/whitespace-only
entries from `deny`/`denyPrefixes`/`allow`. A stray `denyPrefixes:[""]` made
`startsWith("")` true for every header (silently denying ALL forwarding), and
`allow:[""]`/`allow:[" "]` seeded the exclusive allowlist with an entry that
could never match — both integrator typos that now can't silently break
forwarding. Entries are lowercased consistently with existing handling.
2026-06-30 17:12:55 -07:00
Martha Schumann 5df64923cc fix: stabilize inspector live event caches 2026-06-30 17:03:25 -07:00
Jordan Ritter cf951e04f2 test(runtime): harden header-forwarding coverage — clone isolation, case-collision, allowlist boundary
- agent-utils: assert configureAgentForRequest does not mutate the shared
  registered agent; only the per-request clone carries merged inbound headers
  (guards against a cross-request bearer-token leak, #5712).
- header-utils: direct mergeForwardableHeaders unit test for server Authorization
  vs inbound lowercase authorization with a different value — exactly one
  authorization-family key survives carrying the server value (#5712).
- header-utils: shouldForwardHeader boundary tests for the bare 'x' name and the
  empty-string name under both denylist and allowlist policies.
2026-06-30 16:58:11 -07:00
Jordan Ritter 4cdc1e16f5 fix(runtime): make forwardHeadersPolicy optional on CopilotRuntimeLike
The published CopilotRuntimeLike interface (v2 export surface) added
forwardHeadersPolicy as a REQUIRED field, which breaks any external
implementor of the interface — inconsistent with the minor-release
classification. The /run (agent-utils) and /connect (sse/connect) read
sites dereferenced runtime.forwardHeadersPolicy with no coalesce, so a
policy-less object crashed with "Cannot read properties of undefined
(reading 'allow')".

Make the field optional on the interface and coalesce both read sites to
the default resolved policy (resolveForwardHeadersPolicy(undefined),
default-on denylist) when absent. Concrete runtimes (BaseCopilotRuntime)
still always resolve and set it, so behavior is identical for all real
runtimes; the interface is now non-breaking and crash-proof.

Adds a red-green test driving configureAgentForRequest with a runtime
whose forwardHeadersPolicy is undefined: asserts no throw and that the
default denylist applies (x-forwarded-for dropped, custom x-* and
authorization forwarded).
2026-06-30 16:58:06 -07:00
Jordan Ritter 62ed6fa045 test(runtime): cover header-forwarding denylist + config policy on both paths (#5712)
- header-utils.test.ts: new coverage for the default denylist (exact names +
  prefix families, case-insensitive), custom x-* still forwarding, config
  overrides (useDefaultDenylist:false, deny, denyPrefixes, allow allowlist mode),
  and the breadth/precedence interaction. Migrate the inverting assertions
  (x-request-id / X-Forwarded-For now stripped; extract result drops x-request-id)
  and add the new required policy arg to all call sites.
- agent-utils-header-forwarding.test.ts + sse-connect-agent-id.test.ts: /run and
  /connect integration coverage — denylisted infra/platform headers dropped,
  custom x-* + authorization still forward, and a runtime-supplied forwardHeaders
  policy is actually applied (plumb-through). Swap denylisted filler headers for
  non-denylisted custom headers in the precedence regression tests.
- handle-run / handle-connect / intelligence-run-telemetry / get-runtime-info:
  add the resolved forwardHeadersPolicy to mock runtimes that route through the
  header merge so they satisfy the now-required policy.
2026-06-30 16:40:51 -07:00
Jordan Ritter 462fa7ad58 feat(runtime): configurable inbound-header forwarding policy with default infra/platform denylist
Tighten which inbound HTTP headers the v2 runtime forwards onto the outgoing
agent call. The old `authorization` + `x-*` allowlist leaked infrastructure,
proxy, and platform headers (x-forwarded-*, x-real-ip, x-amzn-trace-id,
x-vercel-*, and the Copilot Cloud platform key x-copilotcloud-public-api-key)
to arbitrary configured agent URLs (#5712, breadth half).

- header-utils.ts: add DEFAULT_DENY_HEADER_NAMES + DEFAULT_DENY_HEADER_PREFIXES
  constants and a policy-aware shouldForwardHeader; thread ResolvedForwardHeadersPolicy
  through extractForwardableHeaders and mergeForwardableHeaders. Add the public
  ForwardHeadersConfig and resolveForwardHeadersPolicy (useDefaultDenylist defaults
  to true; deny/denyPrefixes extend the default; allow switches to allowlist mode).
  Server-wins precedence and server-self case-dedup are unchanged.
- runtime.ts: add forwardHeaders?: ForwardHeadersConfig to BaseCopilotRuntimeOptions,
  resolve it once in the constructor into forwardHeadersPolicy (mirroring the
  debug -> ResolvedDebugConfig resolve-once), expose it on CopilotRuntimeLike /
  BaseCopilotRuntime, and add a passthrough getter on the CopilotRuntime shim.
- Apply the resolved policy at both call sites: /run (configureAgentForRequest)
  and /connect (handleSseConnect), so the two paths can never diverge.

Default-on in a minor with { useDefaultDenylist: false } as the documented opt-out.
2026-06-30 16:40:27 -07:00
Martha Kelly Schumann f0eb837dad Merge branch 'main' into codex/unified-thread-debugger 2026-06-30 15:53:41 -07:00
Mike Ryan 2d318c6937 feat: CopilotThreadsDrawer + active-thread configuration (#5746)
## What

The **Angular `<CopilotDrawer>` feature** for the CopilotKit SDK — a
ready, usable threads drawer for Angular apps, plus the active-thread
foundation it sits on. (This PR merges the originally-staged PR1+PR2 so
it ships a working feature, not just plumbing.)

**Foundation — active-thread coordination (no new store):**
1. **`CopilotChatConfiguration`** — an injectable, signal-based service
mirroring React's `CopilotChatConfigurationProvider`: owns
`agentId`/`threadId` resolution (controlled prop → override → minted
fallback) + the `hasExplicitThreadId` welcome flag; `setActiveThreadId`
/ `startNewThread` setters that no-op when host-controlled;
single-instance via `useExisting`.
2. **`connectActiveThread`** (internal connector) — reactively pins the
resolved thread onto `agent.threadId` and owns the connect lifecycle
(per-run `AbortController` on `HttpAgent`s, a single caught connect
chain, a staleness-guarded loading cursor, abort+`detachActiveRun()`
teardown), mirroring the standalone `connectToAgent` path. Clears
messages only on a genuine new-thread transition (never on
mount/agent-swap).
3. **`CopilotChat`** consumes the ambient config when present
(input-first precedence: `[agentId]`/`[threadId]` win), seeds the config
from a set `[threadId]`; standalone `[threadId]` usage unchanged when no
provider is present.

**The drawer — `<copilot-drawer>`:**
4. A standalone `OnPush` wrapper around the framework-agnostic
`copilotkit-drawer` Lit element (`@copilotkit/web-components`). Events
bind declaratively; element properties are set imperatively via
`viewChild`+`effect` (the `a2ui-activity-renderer` precedent). Routes
the element's events
(`thread-selected`/`new-thread`/`archive`/`unarchive`/`delete`/`filter-change`/`retry`)
to the config + `injectThreads` mutations (delete-of-active resets to a
fresh thread).
5. **`CopilotDrawerRow`** directive for per-row custom content
(`slot="row:{id}"`), `onThreadSelect`/`onNewThread` host escape-hatch
callbacks, and `<ng-content>` slot passthrough
(`launcher-icon`/`memories`).
6. **`listError`** added to `injectThreads` — a filtered error (genuine
list/mutation errors only, excluding developer/config errors like
"Runtime URL is not configured") so the drawer's error panel never shows
dev strings to end users. Mirrors react-core's `useThreads`.

**Always-licensed:** Angular SDK licensing is no longer a thing, so the
wrapper does not wire the element's `licensed`/`upsell` — no upsell
path. **Inline-chat-only:** no Layer-2 open-state coordination (the
element self-provides its mobile launcher).

## Why

Angular had no active-thread provider (only `injectThreads`) and no
drawer component. This lands both so Angular apps get thread switching +
a usable threads drawer at parity with the React vertical, reinterpreted
for Angular's inline-chat-only surface.

## Testing

`@copilotkit/angular`: tsc clean, `oxlint` 0 errors, `ng-packagr` build
green, **152 tests pass** (full package suite). New coverage: config
precedence/controlled-vs-uncontrolled + single-instance identity;
connector connect-on-switch / clear-only-on-real-transition / cursor
staleness / no-unhandled-rejection / abort+detach teardown (verified
against a real `HttpAgent`); drawer prop binding, all 7 event routings
(incl. delete-active reset), escape-hatch overrides, `renderRow`
projection, `<ng-content>` slot passthrough, and the `listError`
dev-error-exclusion.

Hardened through a full `cr-loop` (foundation: 5 rounds; wrapper: 2
rounds) — the wrapper CR caught + fixed a real bug (the error panel was
leaking dev/config errors; fixed via the filtered `listError`).

## Dependency / release gate

Depends on `@copilotkit/web-components` (the `copilotkit-drawer`
element) being published — the same release gate as the React drawer
work (#5707). `packages/angular` publishes alongside.

## Follow-ups (out of scope, tracked)

- A demo `routes/threads` in `examples/v2/angular/demo` (kept separate
to keep this a clean SDK-only change).
- Pre-existing OSS packaging: `zod` is in `devDependencies` but imported
by production source — should move to `dependencies`/`peerDependencies`.
- `packages/angular/src/lib/threads.spec.ts` uses `describe/it` vs the
flat-`test` convention of sibling specs (pre-existing).
- Minor polish backlog from CR (renderRow multi-row tests, a docblock
tidy) — non-blocking.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-30 15:46:35 -07:00
Benjamin Taylor 74b8a1981f feat(drawer): default Upgrade CTA URL + Angular license gate
Add a `licenseUrl` property to the shared element (default
https://docs.copilotkit.ai/intelligence). The locked view's Upgrade CTA now
dispatches a cancelable `licensed` event carrying the url and, unless the
host calls preventDefault(), opens it in a new tab; a blank url suppresses
navigation. React and Angular wrappers expose an optional `licenseUrl` prop.

License-gate the Angular drawer like React: surface `licenseStatus` as a
signal on the CopilotKit service and gate the wrapper on
status valid|expiring && checkFeature("threads"), skipping the thread fetch
while unlicensed and showing the loading state (not the locked view) until
the status resolves. Reverses the earlier always-licensed Angular call.

Closes ENT-1027.
2026-06-30 17:34:39 -05:00
Benjamin Taylor f53a70c691 refactor(drawer): rename to CopilotThreadsDrawer / copilotkit-threads-drawer
Final naming decision. Renames the public component + element across the
board: React/Angular CopilotDrawer -> CopilotThreadsDrawer, the Lit element
copilotkit-drawer -> copilotkit-threads-drawer (tag, CopilotKitThreadsDrawer
class, COPILOTKIT_THREADS_DRAWER_TAG, defineCopilotKitThreadsDrawer), the
@copilotkit/web-components/drawer subpath -> /threads-drawer (+ src dir),
the CopilotThreadsDrawerRow directive / copilotThreadsDrawerRow input, and
all prose/test references. Generic types (DrawerThread, DrawerFilter),
--cpk-drawer-* tokens, and ::part names are unchanged. Behavior unchanged.
2026-06-30 17:34:39 -05:00
Benjamin Taylor eb3c20e865 refactor(drawer): rename unlicensed -> licensed across the public surface
Per review, settle the locked-view affordance on a neutral 'license'
name. Renames the event (unlicensed -> licensed), React prop
(onUnlicensed -> onLicensed), slot/part/class (licensed, licensed-cta),
LicensedDetail type, data-testid, render method, and identifier-referencing
comments/test names. The existing 'licensed' boolean gate is unchanged;
prose describing the not-licensed state still reads 'unlicensed'/'locked
view'. Behavior unchanged; Angular unaffected.
2026-06-30 17:34:39 -05:00
Benjamin Taylor d7cb0fded1 refactor(drawer): rename upsell -> unlicensed across the public surface
Per review, drop the 'upsell' monetization jargon. Renames the event
(upsell -> unlicensed), the React prop (onUpsell -> onUnlicensed), the
slot/part/class (upsell -> unlicensed, upsell-cta -> unlicensed-cta),
the UnlicensedDetail type, the data-testid, and all comments/test names.
Behavior unchanged. Angular is unaffected (always-licensed, no gate).
2026-06-30 17:34:39 -05:00
Benjamin Taylor cf4f00ecc1 feat(drawer): activate thread pagination (Load more + limit prop)
Wires up the previously-dormant pagination plumbing (ENT-1016):

- Element: render a 'Load more' button at the list bottom when hasMore
  (and not fetching / not errored), emitting a new load-more event
  (LoadMoreDetail). Distinct from retry{scope:'fetch-more'} (error
  recovery); both advance pagination.
- React CopilotDrawer: add a limit prop (forwarded to useThreads) and
  route load-more to fetchMoreThreads.
- Angular CopilotDrawer: add a limit input (forwarded to injectThreads)
  and route load-more to fetchMoreThreads.

Tests: element load-more render + emit + precedence; React limit
forwarding + load-more routing; Angular load-more routing.
2026-06-30 17:34:38 -05:00
Benjamin Taylor f1cc30cda0 feat(react): add configurable label prop to CopilotDrawer
Mirrors the Angular wrapper + the copilotkit-drawer element's label
property: sets the drawer region aria-label and default header text,
defaulting to the element's built-in "Threads" when omitted.
2026-06-30 17:34:38 -05:00
Benjamin Taylor 0183a30369 fix(web-components): scroll the drawer thread list when it overflows (:host height + .list min-height:0) 2026-06-30 17:34:38 -05:00
github-actions[bot] 3c8e330fcb style: auto-fix formatting 2026-06-30 17:34:38 -05:00
Benjamin Taylor 10a5315c10 feat(angular): forward a label input to the drawer element 2026-06-30 17:34:38 -05:00
Benjamin Taylor 787b9ad382 feat(web-components): make the drawer label/aria-label configurable (label property, defaults to Threads) 2026-06-30 17:34:37 -05:00
github-actions[bot] b5580e32b8 style: auto-fix formatting 2026-06-30 17:34:37 -05:00
Benjamin Taylor d8a8dd4fe6 feat(angular): add <CopilotDrawer> wrapper for the copilotkit-drawer element
Standalone OnPush Angular component wrapping the framework-agnostic
copilotkit-drawer Lit element. Binds live thread state from injectThreads onto
the element (imperative viewChild+effect, per the a2ui-activity-renderer
precedent) and routes the element's DOM events to the ambient
CopilotChatConfiguration + thread mutations. Ships a CopilotDrawerRow directive
for per-row slot projection, host onThreadSelect/onNewThread escape-hatch
callbacks, and <ng-content> slot passthrough (launcher-icon/memories). Uses the
filtered listError for the error panel. Always-licensed (no licensed/upsell
wiring); inline-chat-only (no Layer-2 open-state coordination). Exported from
public-api.
2026-06-30 17:34:37 -05:00
Benjamin Taylor a7c25d9800 feat(angular): expose filtered listError from injectThreads
Adds a listError signal that returns only genuine list/mutation errors,
excluding developer/config errors (missing runtime URL, runtime without thread
endpoints) so consumer error UIs do not surface dev strings to end users.
Mirrors react-core's useThreads listError; `error` is unchanged (additive).
2026-06-30 17:34:37 -05:00
Benjamin Taylor 7a08fa2cc0 chore(angular): add @copilotkit/web-components dependency
Adds the workspace dep (consumed by the <CopilotDrawer> wrapper) and allows it
as a non-peer dependency in ng-package.json so ng-packagr packages cleanly.
2026-06-30 17:34:37 -05:00
Benjamin Taylor 01cd9eeabc feat(angular): CopilotChat consumes ambient CopilotChatConfiguration
CopilotChat reads the ambient config when a provideCopilotChatConfiguration
provider is in scope: input-first precedence ([agentId]/[threadId] win over the
config, matching React), seeding the config from a set [threadId], and driving
the connector with loading-cursor hooks. Standalone <copilot-chat [threadId]>
usage is unchanged when no provider is present. Exports CopilotChatConfiguration
from the package entry point.
2026-06-30 17:34:36 -05:00
Benjamin Taylor 4400a7c081 feat(angular): active-thread connector wiring config to the live agent
connectActiveThread reactively pins the resolved thread onto agent.threadId.
On an explicit switch it connects the agent, owning the loading-cursor + abort
+ detach lifecycle of the standalone connectToAgent path (per-run AbortController
on HttpAgents, a single caught connect chain so a rejecting connect never leaks
an unhandled rejection, and a staleness-guarded cursor settle). On a fresh /
non-explicit switch it clears messages only on a genuine new-thread transition,
never on mount or a same-thread agent swap. Injection-context-only; not exported
on the public surface.
2026-06-30 17:34:36 -05:00
Benjamin Taylor 35671a688f feat(angular): CopilotChatConfiguration service for active-thread resolution
Injectable service mirroring React's CopilotChatConfigurationProvider. Owns
agentId/threadId resolution (controlled prop > override > options > minted
fallback) and the hasExplicitThreadId welcome-screen flag, exposes
setActiveThreadId/startNewThread setters that no-op when the config is
host-controlled, and reserves drawerOpen/registerDrawer hooks for a future
popup/sidebar layer. provideCopilotChatConfiguration aliases the token to a
single instance via useExisting.
2026-06-30 17:34:36 -05:00
Jordan Ritter 636bcad058 fix(runtime): de-duplicate server-vs-server case-collision headers in mergeForwardableHeaders
When an agent is configured with both case-variants of the same header
in agent.headers (e.g. Authorization and authorization), the prior
{ ...base } spread kept both keys — the exact undici comma-join hazard
the function guards against for inbound collisions. Collapse server-self
case-collisions to a single first-occurrence-wins entry; server-wins-over
-inbound and case-insensitive inbound suppression are unchanged.
2026-06-30 15:00:16 -07:00
Jordan Ritter 8bdb3a1c3f test(runtime): cover header precedence — /run + /connect collision, x-* uniqueness, agent-undefined forwarding
Cover the #5712 header-precedence behavior across both paths:

- agent-header-precedence.test.ts: server-configured agent.headers win
  over forwarded inbound headers on collision (case-insensitive), with
  single-key uniqueness assertions for both authorization and the x-*
  family (exactly one surviving key carrying the SERVER value).
- agent-utils-header-forwarding.test.ts: the /run path merges via
  mergeForwardableHeaders so server values are authoritative and inbound
  headers fill only unset keys.
- sse/__tests__/sse-connect-agent-id.test.ts: the /connect path applies
  the same merge, plus the agent-undefined case (no server agent.headers)
  degrades to forwarding allowlisted inbound headers only and does not
  crash.
2026-06-30 14:39:51 -07:00
Jordan Ritter abb85c727d refactor(runtime): thread merged headers into runner.connect() as forward-looking plumbing
The /connect path now builds the same server-wins merged headers as the
/run path and passes them into runner.connect(). This does NOT fix
connect-path auth: no shipped runner consumes the headers field of
AgentRunnerConnectRequest today. The in-memory, intelligence, telemetry,
and sqlite runners all read only threadId from the connect request and
ignore headers entirely. The real outbound header forwarding lives on the
/run path, where agent.headers is mutated before the agent runs.

Passing merged headers here is the correct argument shape for a future
outbound-connecting runner, and keeps the connect path's merge semantics
consistent with /run. The comments and JSDoc are rewritten to state this
plainly rather than implying an active auth fix: the connect-site
cloneAgentForRequest call is documented as the sole agentId-existence
guard (the intelligence branch never re-validates the id), and
cloneAgentForRequest's AbstractAgent | Response (404) dual-return contract
that both callers depend on is now documented.
2026-06-30 14:39:38 -07:00
Jordan Ritter bc5e56a295 fix(runtime): server-configured agent headers take precedence over forwarded inbound headers
When a request hits the /run path, inbound headers are forwarded to the
agent. Previously, forwarded inbound headers could clobber the
server-configured agent.headers on a key collision, letting a client
override server-set values (e.g. authorization). This is the #5712 bug.

Introduce mergeForwardableHeaders (header-utils.ts): a case-insensitive
merge where server-configured agent.headers always win on collision,
regardless of header-name casing. agent-utils.ts now uses this helper on
the /run path so server-configured values are authoritative and inbound
headers only fill keys the server did not set.

Fixes #5712
2026-06-30 14:39:21 -07:00
Mike Ryan ac703dc6d1 fix(chat): keep message padding in sub-viewport panes via container query (ENT-1020) (#5778)
## What & why

Fixes **ENT-1020**. In a side-by-side layout (the threads drawer rail
next to the chat), at iPad-portrait / tablet widths the chat **message
text sat flush against both pane edges** — no horizontal padding — while
the input stayed correctly inset. Surfaced while manually testing the
Angular `CopilotDrawer` (#5746), but it is **not a drawer bug**: it
reproduces in any layout that puts `CopilotChat` in a pane narrower than
the viewport.

### Root cause
The message column is `max-w-3xl` (768px) centered; its wrappers used
`cpk:px-4 cpk:sm:px-0` — 16px below 640px, then **0 at viewport
≥640px**. There was no `container-type` on the chat root, so the `sm:`
variant keyed on the **viewport**, not the chat's own width. In a
sub-viewport pane (~580px chat on an 820px viewport) `sm:px-0` still
fired (viewport ≥640) while the 768px column overflowed the pane
edge-to-edge → flush text.

## The fix (robust / container-relative)
- Add `cpk:@container` (`container-type: inline-size`) to the chat root
(React ×2 render paths, Angular, Vue).
- Switch every message / input / suggestion wrapper from viewport
`cpk:sm:px-0` to the **container** variant `cpk:@3xl:px-0`.

Padding now tracks the chat's **own** width and drops to 0 only once the
container is at least as wide as the column's `max-w-3xl`, i.e. once the
column has real side gutters. In any narrower pane the `px-4` inner
padding is retained. **React, Angular, and Vue kept in lockstep.**

### Why `@3xl`, not the `@sm` the ticket suggested
Tailwind v4 **container-query** breakpoints are a *different scale* from
viewport breakpoints: `@sm` = **24rem/384px** (viewport `sm` = 640px). A
mechanical `sm:` → `@sm:` swap would still collapse the ~580px repro
pane (580 ≥ 384). `@3xl` = **48rem/768px**, which exactly matches the
column's `max-w-3xl` — the width at which gutters first appear — so it
is the semantically correct breakpoint.

> Vue was not named in the ticket scope but shares the identical
`sm:px-0` pattern; left unfixed it would reproduce the bug there, so it
is included for true framework lockstep. web-components only *hosts* the
chat (no `sm:px-0`), so it is correctly untouched.

## Testing

**Browser behavior — real built CSS, exact DOM
(`copilotKitChat`/`@container` root → `cpk:max-w-3xl cpk:mx-auto` →
`cpk:px-4 cpk:@3xl:px-0` message wrapper),
`getComputedStyle().paddingLeft`:**

| Scenario | Result | Expectation |
|---|---|---|
| **580px narrow pane** (the repro) | `padding-left: 16px` | ✅ `px-4`
retained — text no longer flush |
| **900px wide pane** (full desktop) | `padding-left: 0px` | ✅ `px-0` —
column has gutters, behavior preserved |
| **No `@container` ancestor** (render-prop path) | `padding-left: 16px`
| ✅ graceful `px-4`, no flush |

**Generated CSS confirmed** (Tailwind v4.1.18): root emits
`container-type: inline-size`; wrapper emits `@container (min-width:
48rem) { padding-inline: 0 }` (verified in both react-core and angular
builds — a true container query, not a media query).

**Unit/component tests (pass):**
- react-core — full chat suite: **645 tests / 39 files** green (incl.
`CopilotChatCssClasses`).
- angular — `copilot-chat-view` + `copilot-chat-input` specs: **10**
green.
- vue — `CopilotChatView.connectingGate` +
`CopilotChatSuggestionView.slots.e2e`: **30** green.
- pre-commit `test-and-check-packages` (test + publint + attw across all
4 affected projects) passed.

No tests assert these class strings and no snapshots capture them, so
nothing needed regenerating.

## Acceptance criteria
- [x] Messages retain horizontal padding when the chat is in a narrow
(<~768px) pane at viewports ≥640px.
- [x] Full-width chat behavior preserved (container ≥768px → `px-0`),
verified in React, Angular, Vue.
- [x] No regression to input / disclaimer / suggestion alignment with
the message column (all share the same `@3xl` switch).

Closes ENT-1020

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-06-30 14:37:04 -07:00
Nathan 🔶 Tarbert b50e2888a7 test(react-core): cover useCapabilities chat-config agent inheritance (#5533) 2026-06-30 16:43:45 -04:00
Nathan 🔶 Tarbert e55c958393 Merge remote-tracking branch 'origin/main' into fix/issue-5533-agentid-runtime-sync 2026-06-30 16:33:10 -04:00
Martha Kelly Schumann 08ed19f96f Merge branch 'main' into codex/unified-thread-debugger 2026-06-30 13:06:42 -07:00