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
## 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)
CopilotChat message wrappers used viewport-keyed `cpk:sm:px-0`, collapsing
horizontal padding to 0 at any viewport >=640px. The message column is
`max-w-3xl` (768px) centered; the design assumes the chat fills the viewport,
so at >=640px the column has side gutters and inner padding can drop to 0.
But when the chat lives in a sub-viewport-width pane (e.g. the threads drawer
rail beside the chat, ~580px on an 820px iPad-portrait viewport), `sm:px-0`
still fires on viewport width while the 768px column overflows the narrow
pane and sits flush against both edges. The input wrapper looked fine because
it is visually inset by its own pill, so only message text appeared broken.
Make the padding container-relative instead of viewport-relative:
- add `cpk:@container` (container-type: inline-size) to the chat root, and
- switch the message/input/suggestion wrappers from `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 own max-width, so the column has real
gutters; in any narrower pane the `px-4` inner padding is retained. React,
Angular, and Vue kept in lockstep.
Note on the breakpoint: Tailwind v4 container-query breakpoints differ from
viewport breakpoints (`@sm` = 24rem/384px, not 640px). A mechanical
`sm:` -> `@sm:` swap would still collapse the ~580px repro pane. `@3xl`
(48rem/768px) is used because it exactly matches the column's `max-w-3xl`,
which is the width at which side gutters first appear.
Verified: full-width desktop chat unchanged (container >=768px -> px-0);
580px pane retains 16px padding; render-prop layouts without a container
ancestor degrade safely to `px-4`; sidebar/popup `data-*` padding overrides
are unaffected.
ENT-1020
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
### What
The Angular `CopilotChatInput.handleKeyDown` submits the message when
Enter is pressed without Shift, but it never checks whether an IME
composition is in progress. When typing CJK text (Japanese, Chinese,
Korean), the Enter that confirms an IME candidate also fires a
`keydown`, so the half-composed text gets sent instead of the candidate
being committed.
The React and Vue bindings of the same v2 `CopilotChatInput` already
guard against this; Angular was the one binding still missing it:
- `packages/react-core/src/v2/components/chat/CopilotChatInput.tsx` —
`handleKeyDown` returns early on `e.nativeEvent.isComposing || e.keyCode
=== 229`
- `packages/vue/src/v2/components/chat/CopilotChatInput.vue` —
`handleKeydown` returns early on `isComposing.value || event.isComposing
|| event.keyCode === 229`, and has a test asserting it does not submit
while composing
### Change
Add the same early return to the Angular `handleKeyDown`, using the
native `KeyboardEvent` (`event.isComposing || event.keyCode === 229`),
which matches the Vue binding's idiom.
### Notes
When composition is not active `isComposing` is `false`, so Enter
submits exactly as before and Shift+Enter still inserts a newline. The
most visible case is Safari with a Japanese IME, where the confirming
Enter reports `key === "Enter"` with `isComposing === true`; the
`keyCode === 229` arm mirrors the sibling guards for browsers that
report the composing key that way.
I verified the handler logic in isolation (Enter while composing no
longer submits; plain Enter and Shift+Enter are unchanged). I did not
run the full Angular suite locally.
Adds /a2ui-catalog page and a runtime endpoint with NO a2ui config, so
A2UI switches on purely from the provider's a2ui.catalog (the #5774
path). Also fixes DemoButtonAgent, which never actually rendered: it
emitted the wrong activity content key (operations -> a2ui_operations)
and a non-canonical operation/component format. Rewritten to the A2UI
v0.9 wire format (createSurface/updateComponents, flat components, root
id "root") so the surface paints and the Confirm round-trip works.
A2UI's catalog-on-provider path needs a catalog to pass to
`a2ui.catalog`, but the library build tree-shook the nested barrel
re-export so vueBasicCatalog was unreachable at runtime (present only in
the .d.ts). Re-export it explicitly from the v2 entry, mirroring React's
`basicCatalog` export from @copilotkit/a2ui-renderer. Add an export test
guarding against the regression.
## Why
The 6 OpenAI drop-in integration examples enabled for the CLI's
**keyless AIMock mock mode** (`langgraph-python`, `langgraph-js`,
`mastra`, `llamaindex`, `agno`, `pydantic-ai`) shipped
`fixtures/default.json` files that **did not cover the prompts each
starter's own UI suggests**. AIMock matches `userMessage` as a
**case-sensitive substring, first-match-wins**, so a keyless first-run
user who clicked the demo's suggestion chips mostly fell through to the
generic catch-all ("I only have scripted replies…") instead of getting a
scripted demo reply.
Two root issues found (audit at `origin/main`):
- **`langgraph-python` was mis-keyed** — fixtures keyed on suggestion
*titles* (`"Pie Chart"`, `"Toggle Theme"`, `"Task Manager"`) while the
UI sends long *message* strings that don't contain those substrings →
all 9 suggestions missed.
- The other 5 shipped only a generic `Hello` (+ a `weather` fixture in
langgraph-js/mastra) → 0–1 of each starter's chips covered.
This PR re-keys/extends each example's fixtures so the surfaced
suggestions return scripted replies. Ordering preserved:
most-specific-first, `{}` catch-all last (unchanged text).
> Tracking: **ENT-1003** (CopilotKit/Intelligence). Sibling to ENT-989
(fixtures for *not-yet-enabled* frameworks). This PR covers the
*already-enabled* 6.
## What changed (per template)
| Template | Suggestions now covered | Tool-call replies | Text replies
|
|---|---|---|---|
| langgraph-python | 9/9 (3 re-keyed) | pie/bar chart
(`query_data`→component), `scheduleTime`, `toggleTheme`, `manage_todos`
| Search Flights, Excalidraw, Calculator, Sales-Dashboard A2UI step |
| langgraph-js | 9/9 | `query_data`, `search_flights`, `generate_a2ui`,
`manage_todos` (schemas verified vs agent) | scheduleTime, Excalidraw,
generateSandboxedUi, toggleTheme |
| mastra | 6/6 | `get-weather`, `setThemeColor`, `go_to_moon` (HITL) | 3
proverb chips (proverbs are agent shared-state, not a tool) |
| llamaindex | 3/3 | — | theme / proverb / weather |
| agno | 4/4 | — | weather / theme / stock / proverb |
| pydantic-ai | n/a (UI surfaces no chips) | — | best-effort free-typer
fixtures: "what can you do" / "proverb" / "weather" |
## Honest caveats (best-effort; draft)
- **Tool-call shapes only where evidenced.** Where a suggestion drives
A2UI streaming, an MCP app (Excalidraw), or a frontend-only tool
(`generateSandboxedUi`, `scheduleTime` in some templates) whose call
shape isn't defined in the template, I used an **on-topic text reply**
rather than fabricating a tool envelope. Those replies beat the
catch-all but won't trigger the live generative UI under mock mode — a
follow-up could record the real shapes.
- **`pydantic-ai` surfaces no suggestion chips** in its UI, so there's
nothing to key on; the real fix is a small UI change (add `suggestions`
to `CopilotSidebar`), which is out of scope for a fixtures-only PR. The
added fixtures are a fallback for free-typing users.
- **Not verified end-to-end here** (authored against `origin/main`, not
run live). Each template's `docker-compose.test.yml` AIMock smoke should
stay green; note its `@chat` test only sends `"Hello"` and asserts a
non-empty reply, so it does **not** validate suggestion-chip coverage —
extending it to assert a non-catch-all reply for a real suggestion would
close that blind spot (also noted in ENT-1003).
## Test plan
- [ ] Per-template `docker-compose.test.yml` AIMock smoke still green.
- [ ] Manual: scaffold/run each keyless, click each suggestion chip,
confirm a scripted reply (not the catch-all).
Same class as the toggle-theme fix: langgraph-js's 'schedule a meeting' chip
returned text, so the meeting picker never rendered, while langgraph-python emits
the scheduleTime tool call. scheduleTime is a registered langgraph-js frontend
tool (reasonForScheduling/meetingDuration) — swap text -> tool call, mirroring
langgraph-python. Because scheduleTime is Human-in-the-Loop (the user's pick
returns as a tool result and re-invokes the LLM), add a terminating
call_schedule_time_001 result fixture in BOTH templates so the turn doesn't
re-match the user message and loop (langgraph-python lacked it too — latent).
Verified both terminate at 2 steps (tool call -> terminating text).
The langgraph-js Toggle Theme chip returned a text response claiming it toggled
the theme, but emitted no tool call -- so the theme never changed, while the same
chip in langgraph-python emits the toggleTheme frontend tool call and works.
Identical chip, different outcome by template. Swap the text response for the
toggleTheme tool call, mirroring langgraph-python (frontend tool, no terminating
result fixture needed -> no loop). Verified aimock now returns the toggleTheme
tool call for the chip.
The Vue CopilotKitProvider gated all A2UI activation on the runtime
signal (core.a2uiEnabled), so passing an a2ui.catalog to the provider
did nothing: surfaces never rendered locally and the runtime was never
told to inject the render tool. React already supports the
catalog-on-provider path (1.61.2).
Mirror React: a provided catalog now activates A2UI locally and forwards
the a2uiCatalogAvailable property so the runtime injects the render tool
without a runtime-side a2ui config. User-provided properties are
preserved alongside the signal.
Fixes#5774
The chart-chain fixtures looped under multi-step tool execution: langgraph-python
emitted a 2nd tool call (pieChart/barChart/dashboard) with no terminating
toolCallId result fixture, so it fell through to the still-matching userMessage
fixture and re-called query_data. langgraph-js had its toolCallId result fixtures
ordered after the userMessage fixtures (first array match wins -> re-call).
Add the missing terminating result fixtures (langgraph-python) and reorder so all
toolCallId fixtures precede userMessage fixtures (langgraph-js). Verified termination
via the 2-3 step multi-turn repro; mastra fixtures already terminated.
The 6 OpenAI drop-in integration examples enabled for the CLI's keyless
AIMock mock mode shipped fixtures that did not cover the prompts each
starter's own UI suggests, so a keyless first-run user clicking the demo
suggestions mostly hit the generic catch-all. Re-key/extend each
example's fixtures/default.json (substring, most-specific-first,
catch-all last) so the surfaced suggestions return scripted replies.
ENT-1003.
Three snapshot tests (lines 751/771/789) call test() while the file uses
explicit vitest named imports. They pass at runtime via vitest's global
but fail tsc check-types (TS2593). Add test to the import.
Memory, NewMemory, MemoryChanges, MemoryKind, and MemoryScope are the
memory types surfaced by the useMemories / injectMemories hook result
shapes, so export them unprefixed as supported public API instead of
behind the ɵ "internal" marker. Internal memory plumbing (MemoryStore,
the selectMemories* selectors, state/context/event types,
createMemoryStore) keeps the ɵ prefix.
Updates the react-core and angular hook signatures, the web-inspector
Memories tab, and the affected tests to the unprefixed names.
## ⚠️ Draft — stacked on #5563
This builds on #5563 (the `oracle-agent-memory` showcase). Since the
showcase isn't on `main` yet, **this PR's diff currently includes
#5563's files** — once #5563 merges, it auto-reduces to just the
checkpointer change below. Keeping it as a **draft** until then; opening
early to stage the work.
## What this adds
An **optional, flag-gated** durable LangGraph checkpointer for the
showcase agent, using Oracle's
[`langgraph-oracledb`](https://github.com/oracle/langchain-oracle/tree/main/libs/langgraph-oracledb)
`AsyncOracleSaver` — so per-thread LangGraph graph state persists in
**Oracle**, **complementing** (not replacing) `oracleagentmemory`. That
rounds out the "whole stack on Oracle" story: durable *memory* **and**
durable *graph checkpoints*.
**Default-safe:** gated behind `LANGGRAPH_CHECKPOINTER` (default
`memory` → behavior unchanged; `oracle` is opt-in), with graceful
fallback to in-memory on any Oracle error — so CI and the default run
path never touch a DB.
## The actual delta (just these agent files)
- `agent/concierge/checkpointer.py` *(new)* — flag-gated resolver:
builds a dedicated async Oracle pool + `AsyncOracleSaver`, runs `await
setup()`, degrades to `MemorySaver` on failure.
- `agent/concierge/server.py` — an import-time monkeypatch injects the
resolved checkpointer past `ag_ui_agentspec`'s hardcoded `MemorySaver`;
the FastAPI lifespan inits/closes it.
- `agent/pyproject.toml` — adds `langgraph-oracledb>=1.0.1`, bumps
`oracledb>=2.2.0`, adds dev `pytest`/`pytest-asyncio`.
- `agent/.env.example` — documents `LANGGRAPH_CHECKPOINTER`.
- `agent/tests/test_oracle_checkpointer.py` *(new)* — a durability
round-trip test (skipped unless `LANGGRAPH_CHECKPOINTER=oracle`).
Mirrors
[jerelvelarde/oracle-cookbook#4](https://github.com/jerelvelarde/oracle-cookbook/pull/4),
where it's reviewed + verified — the durability round-trip passes
against a live Oracle DB, and 5/5 e2e pass with the flag off (no
regression on the default path).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Runnable companion to the #5521 cookbook recipe — a portable Oracle
Agent Spec agent on LangGraph over AG-UI, long-term memory on Oracle AI
Database, and a CopilotKit V2 frontend (generative UI + HITL booking).
Lives beside `daytona-runcode`.
- `agent/` — Python (uv) Agent Spec agent + FastAPI AG-UI server
- `frontend/` — Next.js CopilotKit V2 chat (flight cards, recall chip,
HITL booking)
- `db/` — Oracle AI Database (Free image) + cookbook-user init
- `docker-compose.yml` (local Oracle) + per-service `railway.json`
✅ Live cross-session recall verified working on the hosted demo (a
preference taught in one thread is recalled in a fresh thread).
### Pre-merge follow-ups — resolved ✅
- [x] **Workspace deps** — the frontend builds standalone (`npm ci` +
`next build` ✓ inside the monorepo). It ships its own
`package-lock.json` and is intentionally excluded from the pnpm
workspace (the `daytona-runcode` / `shell-dashboard` convention), so the
published prerelease `@copilotkit/*` resolve and no
`workspace:*`/`catalog:` is needed.
- [x] **Promote SSOT** — the live demo is hosted independently (not part
of CopilotKit's Railway promote fleet), so no
`showcase/scripts/railway-envs.ts` entry is required.
- [x] **`db/build-and-push.sh`** — default image namespace genericized
to an `OWNER` placeholder (still `IMAGE`-overridable).
- [x] **Oracle license** — `db/Dockerfile` and `build-and-push.sh` both
document that the built image must stay in a PRIVATE registry
(re-publishing the Oracle base image violates the license).
> The Vercel preview deploys and `fork-pr-monitor` fail because this is
a cross-fork PR (forks can't access deploy secrets) — not code issues.
`config-allowlist` is fixed.
Docs: CopilotKit#5521.
## Problem
Docs OG image generation was producing unreliable social-preview output
for docs URLs. The route depended on old static OG assets and Inter-era
styling, and the preview did not match the current CopilotKit docs theme
or logo.
## Why
The OG route should render a consistent branded card from page
frontmatter for every docs slug. It also needs local render assets for
request-time reliability: `next/og` does not inherit the app layout
font, and image inputs need to be available as bytes when the route
renders.
## Fix
- Reworked `showcase/shell-docs/src/app/og/[...slug]/route.tsx` to
render a branded 1200x630 card with a tighter layout, CopilotKit theme
colors, Plus Jakarta Sans, and per-page title/description/section
labels.
- Kept `next/font/google` for normal docs pages, and added upstream Plus
Jakarta Sans static TTFs only for the OG renderer. `SOURCE.md` records
the upstream URLs and SHA-256 hashes. The Google Fonts variable TTF was
tested but the bundled `next/og` renderer crashes while parsing its
`fvar` table.
- Added the official CopilotKit full lockup as a real PNG asset. It is
covered by the repo-level `*.png filter=lfs` rule, and the route encodes
the PNG bytes to a data URI only at render time for `ImageResponse`.
- Removed the hardcoded runtime/frontend/agent pills and the yellow
gradient stop from the card.
- Updated the focused OG route test to assert the card dimensions and
bundled Plus Jakarta fonts.
Validation: focused OG test, direct `ImageResponse` render with the
upstream fonts, lint, typecheck, build, and live local OG route checks
passed. Full shell-docs test has unrelated existing failures in public
LFS PNG assets and one docs-render nav expectation.
## What does this PR do?
Follow-up polish on the **OpenBox Governance** cookbook recipe (shipped
in #5686), addressing review feedback from the launch. All changes are
in the single recipe page; no functional/code changes.
| Feedback | Change |
| --- | --- |
| "Make this an actual diagram — hard to read as ASCII" | Replaced the
ASCII flow under **How it works** with a real, color-coded **inline-SVG
architecture diagram** (same self-contained pattern as the Oracle recipe
— no external assets). |
| "Huge, scary warning — condense it" | Cut the wall-of-text
**provisioning** warning down to a few lines while keeping the
essentials (idempotent, which keys it needs, how to verify). |
| "Prompts are unreadable in the chart layout → accordions" | Converted
the **Try it** governance-matrix table into one **accordion per prompt**
(verdict-labeled), so the long prompts are on-demand instead of crammed
into table cells. |
| "Highlight the important stuff — guide the user. Applies to all code."
| Added line highlighting across the bash + TypeScript samples so the
key lines (the runtime wrap, `selfGovernedToolNames`, middleware-first,
the provisioning command, the approval schema) stand out. |
| "Move this to the top top and make it an accordion" | Moved the
**coding-agent prompt** from the bottom to just under the intro,
collapsed in an accordion. |
## Verification
Rendered locally against `shell-docs` (`next dev`) and confirmed
in-browser: the SVG diagram renders, all five accordions expand
correctly, the warning is condensed, and code highlighting shows on both
bash and TS blocks (no leaked `[!code]` markers).
## Related
- Recipe (merged): #5686
- Source-link hotfix (merged): #5767
- Companion showcase (pending): #5685 — once it lands, restore the
direct `tree/main/...` source link in **Get the code**.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Address review feedback on the OpenBox Governance recipe:
- Replace the ASCII flow with a real inline-SVG architecture diagram
- Condense the wall-of-text provisioning warning to a few lines
- Convert the governance-matrix table into per-prompt accordions
- Highlight the key lines across the code samples to guide the reader
- Move the coding-agent prompt to the top in a collapsed accordion
- Revert the v2 useRenderToolCall fallback: when no per-tool or wildcard renderer is
registered, return null instead of auto-painting DefaultToolCallRenderer. Showing the
card is opt-in via useDefaultRenderTool().
- The auto-card default shipped silently in v1.57.2 (commit ba60df5d33, buried in a
showcase per-tool-testids commit, no CHANGELOG entry) and leaked internal tool names +
raw args/result JSON (e.g. generate_a2ui) into every v2 app's chat in production.
- Remove the now-dead defaultToolCallRenderAdapter + __testOnly_ export + unused imports.
- Rewrite the resolver test to assert the 3-state opt-in contract: useDefaultRenderTool()
-> card, useDefaultRenderTool({render}) -> custom, no hook -> nothing.