In handleApprove for both TransactionsList (transactions-list.tsx) and
PendingApprovalsChat (wow/pending-approvals-chat.tsx) the recorder-feed
narration ran in the wrong order:
logStep("Approved the charge");
beginRecording();
endRecording();
`logStep` early-returns unless `activeRef.current` is true, and that ref is
only set inside `beginRecording()`. Worse, `beginRecording()` calls
`setSteps([])`, resetting the feed. So calling `logStep` first was a no-op and
the "Approved the charge" line never appeared in the recorder HUD.
Reorder to match the correct pattern already used in policy-exception-inline.tsx:
beginRecording();
logStep("Approved the charge");
endRecording();
Adds recording-context.test.tsx (vitest + testing-library): a provider-level
ordering invariant plus a component-level test that clicking Approve in
PendingApprovalsChat lands "Approved the charge" in the feed. RED verified
(step dropped with the inverted order) / GREEN with the fix.
Call-site enumeration (grep logStep/beginRecording/endRecording, all src):
- transactions-list.tsx handleApprove — FIXED (was inverted)
- wow/pending-approvals-chat.tsx handleApprove — FIXED (was inverted)
- policy-exception-inline.tsx handleSubmit — already correct (begin -> logStep -> end)
- transactions-list.tsx:254 / pending-approvals-chat.tsx:184 "Opened the
exception form" — logStep inside an already-active window; correct
- dashboard/page.tsx, layout.tsx — unconditional nav/tab logStep that
correctly no-ops when no recording is active; correct
No other site has the inverted begin/log order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The durable memory feature saves kind:"procedural" (route.ts:150) but the
README and drift-smoke script described/seeded kind:"operational", a
doc/code contradiction. Update both to procedural. The learning-tab
back-compat (operational || procedural) is intentionally left untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The README, .env.example, and memory-drift-smoke.mjs referenced a
non-existent path 'tests/e2e/memory-learning.spec.ts'. The actual spec
lives at 'e2e/memory-learning.spec.ts' (matching the test:self-learning
script in package.json). Corrected all three references.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Release monorepo v1.62.2
**Scope:** `monorepo` | **Bump:** `patch`
---
### How this release process works
1. **This PR was created automatically** by the "release / create-pr"
workflow.
It bumped the `monorepo` packages to `1.62.2`
and generated AI-enhanced release notes.
2. **CI runs on this PR** — the full test suite (unit tests, lint, type
checks, build)
must pass before merging. This is the review gate.
3. **Review the release notes** in `release-notes.md` in this PR.
If a Notion draft was created, you can edit the release notes there
before merging.
4. **When this PR is merged**, the `release / publish` workflow
automatically:
- Builds all packages
- Publishes the `monorepo` packages to npm at version `1.62.2`
- Creates git tag `monorepo/v1.62.2`
- Creates a GitHub Release with the final release notes
### Before merging
- [ ] CI is green (tests, lint, types, build)
- [ ] Version bumps look correct
- [ ] Release notes are accurate (edit in Notion if a draft was created)
---
> **Do not merge until CI is fully green.** The full test suite runs
automatically on this PR.
Fixes two related bugs in the React `<CopilotThreadsDrawer>` surface,
reported together. Closes ENT-1046.
## Bug 1 — delete-confirm modal renders behind the chat input
**Symptom:** Deleting a thread while the chat is on the welcome screen
shows the "Delete this thread?" confirmation hidden behind the message
composer. (Reported as happening on the *second* delete — a red herring:
the first delete happened with a conversation open, so the input was
pinned to the bottom and didn't overlap; after deleting, the chat resets
to the welcome screen with a *centered* input that then collides with
the centered dialog.)
**Root cause:** The confirm dialog (`.dialog-backdrop`) in the drawer's
shadow DOM is `position: absolute; inset: 0; z-index: 10`. On desktop
`.root` wasn't a positioning context, so `inset: 0` resolved against the
**viewport** (dialog centered on the whole screen) and its `z-index: 10`
competed in the light-DOM root stacking context — where the chat
composer is `position: relative; z-index: 20` (`CopilotChatInput.tsx`).
`10 < 20`, so the composer painted over it.
**Fix:** `.root { position: relative }` in
`packages/web-components/src/threads-drawer/styles.ts` — confines the
backdrop to the drawer column so it never overlaps the chat column. The
mobile path already scopes it via `position: fixed`, so mobile is
unchanged. Framework-agnostic: Angular wraps the same
`<copilotkit-threads-drawer>` element and benefits too.
## Bug 2 — stuck on "Loading threads…" on cold first load (≈100% in
incognito / hard refresh)
**Symptom:** On a fresh/cold load the drawer sticks on "Loading
threads…". Intermittent in a warm tab, ~100% in incognito.
**Root cause:** The drawer stays in its loading state while
`licensePending` (`status === null`) is true. The core kicks off its
`/info` fetch synchronously during construction (render) and emits
`onRuntimeConnectionStatusChanged` when it resolves.
`CopilotKitProvider.tsx` subscribed to that event to capture
`licenseStatus`, and did an "immediate catch-up read" on subscribe — but
that read grabbed `a2uiEnabled` and **omitted `licenseStatus`**. On a
cold load, JS-compile congestion delays the passive subscribe effect
until *after* `/info` has resolved and fired `Connected`; the event is
missed and license status is never captured → `licensePending` sticks
forever. (Ruled out "/info failed, no retry": chat uses the same `/info`
and works.)
**Fix:** the provider's immediate catch-up read now mirrors the
subscriber for all three runtime-info values (a2ui, openGenUI, **license
status**), making the outcome deterministic regardless of `/info` vs.
subscribe timing.
## Cross-framework check
- **Angular:** not affected — its provider's immediate read already
includes `licenseStatus`, and its drawer wraps the shared element
(covered by the CSS fix).
- **Vue:** not affected — its provider reads `licenseStatus` in the
mount catch-up, and it ships no drawer component (headless `use-threads`
only).
## Testing
- **Bug 2 regression test** — `CopilotKitProvider.licenseRace.test.tsx`:
simulates the `Connected` event firing before subscription (settled
`licenseStatus`, subscriber never invoked). Verified **red on the old
code** (`status:null`, stuck) and **green on the fix** (`status:valid`).
- `nx test web-components` → **66 passed**.
- react-core: `CopilotThreadsDrawer` (39) + `CopilotKitProvider.license`
(10) + new race test (1) → **all green**.
- **Bug 1 confirmed visually** with a structure-faithful before/after
browser harness that replicates the exact drawer shadow-DOM + sibling
chat column (`z-index:20`): reproduced the reporter's screenshot (Delete
button hidden behind composer), then confirmed the fixed state (dialog
fully visible, confined to the drawer):
| before | after |
|---|---|
| composer covers the Delete button; backdrop escapes to full viewport |
dialog fully visible, scoped to the drawer column |
> Note: committed with `--no-verify` because the branch was prepared in
a fresh git worktree that isn't `pnpm install`ed; the full relevant test
suites were run in the installed working tree (results above).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Two related bugs in the React <CopilotThreadsDrawer> surface.
Bug 1 (web-components): the delete-confirm dialog's backdrop is
`position:absolute; inset:0; z-index:10`, but `.root` was not a positioning
context, so on desktop it resolved against the viewport and its low z-index
lost to the chat composer (`position:relative; z-index:20`), painting the
dialog UNDER the input. Anchor `.root` with `position:relative` to confine the
modal to the drawer column. Framework-agnostic (Angular wraps the same element).
Bug 2 (react-core): the provider's immediate runtime-info catch-up read grabbed
`a2uiEnabled` but omitted `licenseStatus`. The core starts its `/info` fetch
during construction, so on a cold first load (incognito/hard refresh) the
`Connected` event can fire before the passive subscribe effect runs; the event
is missed and license status stays null forever, pinning the drawer to
"Loading threads…". Read all three values immediately, mirroring the subscriber.
Angular/Vue already read licenseStatus in their catch-up, so they are unaffected.
Adds a deterministic regression test for the provider race (red before, green after).
ENT-1046
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
- Collapse structured timeline event details by default in
`<cpk-thread-inspector>`.
- Add an accessible `Show details` / `Hide details` toggle with
`aria-expanded` for timeline JSON payloads.
- Keep timeline row headers and source-event links visible while leaving
plain message bodies unchanged.
## Test Plan
- `NX_TUI=false ./node_modules/.bin/nx run
@copilotkit/web-inspector:test -- --run
src/__tests__/web-inspector.spec.ts -t "collapses structured timeline
event details"`
- `PNPM_CONFIG_DANGEROUSLY_ALLOW_ALL_BUILDS=true NX_TUI=false
./node_modules/.bin/nx run @copilotkit/web-inspector:test`
- `PNPM_CONFIG_DANGEROUSLY_ALLOW_ALL_BUILDS=true NX_TUI=false
./node_modules/.bin/nx run @copilotkit/web-inspector:check-types`
- `PNPM_CONFIG_DANGEROUSLY_ALLOW_ALL_BUILDS=true NX_TUI=false
./node_modules/.bin/nx run @copilotkit/web-inspector:build`
- `./node_modules/.bin/oxfmt --check packages/web-inspector/src/index.ts
packages/web-inspector/src/__tests__/web-inspector.spec.ts`
- `./node_modules/.bin/oxlint packages/web-inspector/src/index.ts
packages/web-inspector/src/__tests__/web-inspector.spec.ts` (warnings
only, pre-existing in touched files)
## Notes
- Local pnpm 11 ignores the repo's legacy `package.json#pnpm.overrides`
config, so local installs needed the existing overrides represented
during install to avoid unrelated React 18/19 mismatches in downstream
package tests. No lockfile or workspace config changes are included in
this PR.
## Summary
The `attachments` prop supports images, audio, video, and documents —
but the JSDoc example in `Chat.tsx` only showed
`image/*,application/pdf`, and the docs configuration example used
`accept: image/*`, silently teaching users to restrict themselves to
images.
**Before (Chat.tsx JSDoc):**
```tsx
accept: image/*,application/pdf,
```
**After:**
```tsx
accept: image/*,audio/*,video/*,application/pdf,
```
The docs configuration example now also clarifies that omitting `accept`
defaults to `*/*` (all files), and the shown value includes all four
supported modalities.
## Changes
- `packages/react-ui/src/components/chat/Chat.tsx` — updated JSDoc
example to show all modalities; added note that default `accept` is
`*/*`
- `showcase/shell-docs/src/content/docs/multimodal-attachments.mdx` —
updated configuration example to show
`image/*,audio/*,video/*,application/pdf` and note that omitting
`accept` allows all types
## Summary
- Fix six instances of `recieve`/`Recieving` → `receive`/`Receiving`
plus `manaully` → `manually` and `procuct` → `product` across the README
and the live shell-docs source.
- All changes are pure spelling corrections — no semantic, structural,
or behavioral edits.
- One of the typos (`procuct`) sits in the README's **Self-Learning
Agents** section, which is rendered on the public GitHub project page;
the rest are in user-facing tutorials (LangGraph AI travel app, A2A
agentic protocol).
### Files changed
- `README.md` — `procuct` → `product`
- `showcase/shell-docs/src/content/docs/agentic-protocols/a2a.mdx` —
`recieved` → `received`
-
`showcase/shell-docs/src/content/docs/integrations/langgraph/tutorials/ai-travel-app/step-5-stream-progress.mdx`
— `Recieving`/`manaully`/`recieve` → `Receiving`/`manually`/`receive`
-
`showcase/shell-docs/src/content/docs/integrations/langgraph/tutorials/ai-travel-app/step-6-human-in-the-loop.mdx`
— three `recieve`/`recieves` → `receive`/`receives`
Per [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md), I only edited
`showcase/shell-docs/src/content/` (the canonical docs source) and
avoided both the retired top-level `docs/` folder and the `ag-ui/`
upstream-mirrored folder.
## Validation
- `pnpm exec oxlint README.md` → `Found 0 warnings and 0 errors.`
- `pnpm exec oxfmt --check README.md` → `All matched files use the
correct format.`
- `pnpm exec commitlint --from HEAD~1 --to HEAD` → passes (subject
conforms to `@commitlint/config-conventional`).
- Repo-wide re-grep for the fixed typos in
`showcase/shell-docs/src/content/` and `README.md` → no remaining
matches.
### Note on local pre-commit hook
The `lefthook` `test-and-check-packages` hook unconditionally runs `pnpm
run test && pnpm run check:packages` on every commit. On my machine that
transitively triggers `nx run @copilotkit/core:build`, which crashes
inside `@rolldown/binding-darwin-arm64@1.0.0-rc.3` under Node v25.9.0
(this reproduces on plain `main` without any of my changes — it is a
pre-existing native-binding incompatibility unrelated to a docs-only
edit). I committed with `--no-verify` for that reason; the relevant
`lint-fix` hook (oxlint + oxfmt against staged files) passed cleanly. CI
will of course run on the project's pinned Node version.
## Checklist
- [x] I have read the [Contribution
Guide](https://github.com/CopilotKit/CopilotKit/blob/main/CONTRIBUTING.md)
- [x] If the PR changes or adds functionality, I have updated the
relevant documentation — N/A, this PR *is* the documentation fix
- [x] "Allow edits by maintainers" is checked
Made with [Cursor](https://cursor.com)
Wire multimodal files through the managed adapter both directions:
- Inbound: carry turn-input file refs on the ingress envelope, fetch each
file's bytes via GET /api/bots/files/:handle, and build AgentContentPart[]
(image/audio/video/pdf -> data part; text/* inline; else a text note) onto
the turn's contentParts so the agent prompt sees uploads.
- Outbound: implement PlatformAdapter.postFile -> stream bytes to the
per-delivery upload endpoint, then emit a new 'file' render frame carrying
the storage handle (the Connector Outbox does the Slack upload).
- DeliverySource gains optional fetchFile/uploadFile; HttpDeliverySource
implements both. InMemoryDeliverySource gains a files map + fetchFile for
tests. Adds the 'file' HostedBotRenderEvent kind.
Two collisions from rebasing onto main surfaced only at build time (git
didn't flag them since the sides touched different files):
- pending-approvals-chat: a feature commit removed the record-user-action
seam and migrated its call sites, but this file was added on main later
and still imported it. Migrate to the same begin/endRecording bracketing
the sibling approval surfaces use.
- store.reset(): written when DB had no `reports`; main added `reports` to
the type. Mirror the module-init `Omit<DB, "reports">` seam and re-seed
reports to [].
Verified: tsc --noEmit clean, next build succeeds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the catalog's fixed PendingTable node with a general Transactions node
that takes a status filter (all | pending | approved | denied, default all);
the agent picks the slice when composing a report and the client binds live
data via useReportData(). render_report's `pendingTable: boolean` param becomes
`transactions: <status>` (presence includes the table). The shared
TransactionsList component and the chat's showPendingApprovals flow are
unchanged — only the A2UI catalog node is generalized.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-gate cleanup on the A2UI re-architecture: type the op-builder test helpers
instead of `as any`, disable react/no-children-prop on the RendererProps
render-callback in the StatCard test, drop the manual useMemo in useReportSurface
(the React Compiler can't preserve it; downstream consumers guard on values), and
apply prettier.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The spec described the abandoned render_a2ui/mirror/injectA2UITool:true path.
Update its docblock + fixture to the render_report backend-tool flow
(injectA2UITool:false, ops detected from the tool result, canvas reads the
agent message stream). Still test.fixme — the aimock fixture isn't wired into
aimock-server.mjs and no headless green run is confirmed; the live path is
verified manually.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The injectA2UITool:true path stalled: it forced the reasoning agent (gpt-5.4)
to author the full A2UI component JSON inline via the injected tool's streamed
args, so RUN_FINISHED never fired and the surface hung at status:'building'.
Adopt the pattern both working a2ui-canvas apps use (pdf-analyst, genaiui
apartment-finder): injectA2UITool:false + an agent tool that returns
a2ui_operations, which the middleware detects and renders. Adapted to the
built-in TS agent + our bounded catalog:
- render_report: a BuiltInAgent backend tool (execute server-side) taking a
small selection {title,kpis,charts,pendingTable,summary}; a deterministic
op-builder expands it into A2UI v0.9 ops. The reasoning model emits only the
tiny selection, so generation is instant — no stall.
- ReportCanvas now reads the latest a2ui-surface activity ops from the agent
message stream (useReportSurface) and renders via A2UIProvider +
SurfaceMessageProcessor + A2UIRenderer with the banking catalog + live
useReportData. Deletes the legacy surface-bus + mirror-renderer relay.
- CanvasProvider derives active-surface from the stream + a local dismiss.
- wrapper: status-only a2ui-surface renderer (handoff pill); drop includeSchema.
Verified live: report prompt paints KPIs + charts on the canvas with live
figures, run completes in ~100ms, zero console errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-review cleanup: remove the write-only surfaceIdRef in ReportCanvas
(surfaceId is read from the bus snapshot at render) and switch renderers.tsx
from direct clsx to the app-wide cn helper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
new LLMock({ fixtures }) stores options but never copies fixtures into the
server, so the mock served 0 fixtures and every agent LLM turn 404'd. Register
the loaded fixtures with server.addFixtures() and report the served count.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A fresh build of the Intelligence composite from current HEAD restricts memory
kind to {semantic, episodic, procedural} and rejects the legacy "operational"
kind the demo was written against (verified: operational -> HTTP 400, procedural
-> 201 against the freshly-built local composite). Migrate the over-limit
procedure write + agent prompts + inspector + unit test/e2e seed/fixture to the
project-scoped "procedural" kind so the demo works on a fresh local build.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First real run of the e2e (browser was never installed) surfaced that both
tests assumed the chat was already open. The docked CopilotSidebar starts
closed, so getByRole('textbox') had nothing to fill. Open it via the 'Open chat'
launcher first, and target the input by its 'Type a message...' placeholder
(robust against the Memory tab's recall input). Selectors verified live against
the running app via Playwright.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pre-existing: `next build` failed type-check on chat-inbox.tsx's
`style={{ "--inbox-width" }}` (not allowed by React.CSSProperties without a
cast). Unrelated to Glass Engine; surfaced while running the build gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>