- ThreadStoreRegistry.register: delete old store before notifyUnregistered
so callbacks that call getThreadStore(agentId) see undefined, not the
new store
- ThreadDetailsComponent: reset _expandedMessages on threadId change
alongside _expandedToolCalls (prevents stale expanded state across
thread switches)
- handle-threads.test: assert identifyUser called in getThreadMessages
intelligence path; add identifyUser-throws 500 test
- use-threads.test: add fetchMoreThreads end-to-end test (calls the
function, asserts cursor param on second fetch, asserts 3 threads)
- in-memory-runner.test: call clearThreads() in first describe's
beforeEach for GLOBAL_STORE consistency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary
Bumps the internal `@copilotkit/license-verifier` dependency from
`0.0.1-a1` to `0.2.0` in `@copilotkit/runtime` and `@copilotkit/shared`.
The new `license-verifier` release adds a required `telemetry_id` field
to the signed `LicensePayload` — see the intelligence repo ENT-251 PR
for motivation (per-license analytics correlation ID, enables reverse
lookup from inbound Scarf events to the HubSpot Contact that issued the
license).
## Scope
- `packages/runtime/package.json`: bump dep.
- `packages/shared/package.json`: bump dep.
- `.changeset/bump-license-verifier-ent-251.md`: changeset (patch bump
on runtime + shared; propagates to all \`@copilotkit/*\` via fixed:
config).
(closes ENT-251)
New license verifier release adds a required telemetry_id field to the
signed LicensePayload. Runtime consumes verified payloads unchanged;
shared re-exports the widened type. See the intelligence repo ENT-251
PR for the motivation (per-license analytics correlation ID).
Closes#3217
When `getLanguageModel()` returns null and provider/model are undefined
(as with LangChainAdapter), the code constructed `"undefined/undefined"`
as a model string, causing a cryptic "Unknown provider" error.
Now checks each source of model info explicitly and throws a clear error
message directing users to provide an explicit agents config when using
adapters that don't expose model metadata.
Split from #3838.
Addresses PR feedback on #3814: replaces the "red-green tested locally"
rationale with actual unit coverage.
The inline `event.messageId || randomId()` fallback becomes an exported
`resolveMessageId` helper. The resolver now calls it; the helper is
covered by a small test suite asserting: provided ids pass through
verbatim; null / undefined / empty-string event ids fall back to the
"ck-<uuid>" shape; successive fallbacks produce distinct ids.
Covers the three branches added by the fix:
- getLanguageModel() returns a LanguageModel -> wired into BuiltInAgent
- provider + model strings present -> composed as "provider/model"
- neither present (e.g. LangChainAdapter) -> throws CopilotKitMisuseError
with adapter name in the message, instead of silently producing
"undefined/undefined" and failing downstream.
Also guards the partial-info case where only one of provider/model is set
to ensure the regression doesn't re-emerge via a half-populated adapter.
## Summary
Three connected features land together so the CopilotKit VS Code
extension becomes a coherent debugger/preview surface:
1. **Hook Explorer** — every V1 + V2 render hook can be discovered and
previewed live with auto-generated controls, an inline `▶️ Preview
Component` CodeLens, and a sidebar that lists every captured site.
2. **AG-UI Event Inspector** — live SSE debug stream of all AG-UI
events, filterable and color-coded, in a sidebar view + editor panel.
3. **A2UI Catalog sidebar → webview** — the last native TreeView gets
replaced with a Tailwind-styled webview that matches the other two, now
with a proper **Go to source** action on components and fixtures.
## Hook Explorer
### Discovery + preview
- oxc-based scanner walks the workspace and finds every call-site of any
hook in the registry (17 across V1 + V2, render + data).
- Preview panel bundles the user's source via Rolldown (IIFE format,
React externalized, CSS collected per `@copilotkit/a2ui-renderer`
pattern), executes it in the webview with a capture-only **stub** for
`@copilotkit/react-core` (+ `/v2`), and mounts the user's component just
long enough to record each hook's config.
- Auto-generated form on the left/top drives the `render` prop's
args/parameters/state/event live. V1 parameter arrays and V2 Zod /
Standard Schema all map through a unified `FormSchema` derived at
runtime from the captured config.
- `useCopilotAction`, `useCopilotAuthenticatedAction_c`,
`useCoAgentStateRender`, `useLangGraphInterrupt`, `useRenderTool`,
`useRenderToolCall`, `useDefaultRenderTool`, `useLazyToolRenderer`,
`useRenderCustomMessages`, `useRenderActivityMessage`,
`useHumanInTheLoop`, `useInterrupt`, `useFrontendTool`, `useComponent`,
`useDefaultTool` all previewable.
- Inline `▶️ Preview Component` CodeLens above every render-hook call
site, backed by the same `copilotkit.hooks.preview` command as the
sidebar.
- Imported render components work: rolldown walks transitive imports
from the hook's `render` prop through any number of sibling files.
- Cross-file hook switches are robust: controls are reset on load,
Harness only mounts once the real HostRoot is ready, a top-level error
boundary auto-recovers when you pick a different hook.
### Why the stub approach
Bundling the real `@copilotkit/react-core` through rolldown's IIFE
output hit a `__commonJSMin` TDZ chain (`require_clipboard`,
`require_graphql`, `require_context_helpers`, …) because the
chat/runtime-client/markdown graph has circular imports. Externalizing
react-core + routing to a Proxy-backed stub that captures hook configs
avoids the whole CJS wrapping problem, shrinks the preview bundle from
~24 MB to ~1.3 KB, and keeps the preview runtime path completely
runnable without a live CopilotKit backend. Tradeoff documented in
`copilotkit-stubs.ts`.
### Weather-themed fixtures
14+ fixtures under `packages/vscode-extension/test-workspace/hooks`,
each a distinct visual scenario (forecast card, severity-palette alerts
with imported CSS, forecast strip, live radar grid, conic-gradient
precipitation gauge, air-quality badge with imported render, pollen
report with a 2-hop import graph, HITL evacuation confirm,
sunrise/sunset gradient, etc.). Used both as regression fixtures and as
the demo surface for video.
### Styling
- Tailwind-via-CDN + VS Code CSS variables for theme-aware chrome.
- User-provided CSS imports collected by rolldown and injected as a
`<style>` tag per load.
- Controls + form fields converted to Tailwind; textarea matches input
styling.
- Framed "Rendered output" card so the render prop is visually
unmistakable.
## AG-UI Event Inspector
### Runtime (`@copilotkit/runtime` + `@copilotkit/shared`)
- `DebugEventBus` — in-memory pub/sub on `BaseCopilotRuntime`, only
instantiated when `NODE_ENV != production`.
- Event tap in `createSseEventResponse` broadcasts every AG-UI event
with metadata (agentId, threadId, runId, timestamp).
- `GET /debug-events` SSE endpoint — returns 404 in production, streams
`DebugEventEnvelope` JSON to connected clients, initial `: connected`
comment flushes headers immediately.
### VSCode Extension
- `DebugStream` — Node SSE client with auto-reconnect, exponential
backoff, URL validation, error surfacing.
- `InspectorPanel` (editor panel, command `CopilotKit: Open AG-UI
Inspector`) and `InspectorViewProvider` (sidebar view) both use a shared
`DebugStream` instance — events persist when switching tabs.
- Inspector React app: `ConnectionBar`, `FilterBar`, `EventList`,
`EventDetail`.
- Color scheme: purple (lifecycle), red (errors), blue (text), orange
(tools), green (reasoning), teal (state), yellow (activity), gray
(unknown).
## A2UI Catalog → webview
- Replaces `ComponentPreviewProvider` (native TreeDataProvider) with
`CatalogListViewProvider` (WebviewViewProvider), matching the Hooks and
Inspector sidebars.
- New React webview with refresh header, component rows (name + relative
path + `auto` badge when no fixture), expandable fixtures list.
- Click a component row → preview (or toggle if it has fixtures); click
a fixture row → preview that fixture.
- Hover action buttons: `▷` preview + `</>` go-to-source on every row.
- "Go to source" opens the component file for component rows; for
fixture rows it opens the fixture file and jumps the cursor to the named
fixture key.
## Test coverage
- Runtime: DebugEventBus unit tests (8), handleDebugEvents endpoint (5),
fetch-router routes (4), integration across Express/Hono/Node/Fetch (9).
- Hooks: scanner + 16 fixture bundle-smoke test, regression guard
against `node_<builtin>` self-references, CSS collector test, stub-based
capture E2E, cross-kind controls remount, FormRenderer defensive
rendering.
- Inspector + webview: DebugStream reconnect (10), inspector components
(17), colors (9).
- Total: **178 tests** passing for the vscode-extension package; runtime
suite unchanged.
## Test plan
- [ ] `pnpm nx run copilotkit-vscode-extension:build` and `pnpm nx run
copilotkit-vscode-extension:test` both green
- [ ] F5 launches the Extension Dev Host with `test-workspace` open
- [ ] Hooks sidebar lists every fixture hook; click a row → preview
opens; `</>` button opens the source
- [ ] `▶️ Preview Component` CodeLens shows above every render hook in a
`.tsx` file; clicking it opens the preview
- [ ] Form controls drive the render live; cross-kind hook switches
(action ↔ custom-message) don't crash; a forced render-prop throw
recovers when a different hook is picked
- [ ] Imported-render fixtures (`ImportedAirQuality`,
`ImportedPollenReport`) bundle and preview correctly
- [ ] A2UI Catalog sidebar is the new webview, refresh works, `</>` on a
fixture opens the fixture file and reveals the named key
- [ ] AG-UI Inspector connects to `GET /debug-events`, filters + detail
work, events survive sidebar/panel switch, invalid URL shows red error
- ThreadStoreRegistry.register() now fires onThreadStoreUnregistered
before overwriting an existing store so subscribers (web inspector)
don't stay subscribed to stale stores on replacement; test extended
to verify both events fire in order
- Wire handleClearThreads to POST /threads/clear route; add RouteInfo
variant, router pattern, and fetch-handler case so the inspector can
actually call it
- Add handleGetThreadMessages tool-call mapping test using properly
typed Message objects (role as const, type as const) — exercises the
real mapping code path without mocking getThreadMessages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Excludes showcase/shell-docs and showcase/shell-dojo demo-content.json from
the size check in lefthook.yml — these data files were added by main but
weren't in the exemption list, causing the pre-commit hook to reject them.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- middleware SKILL: beforeRequestMiddleware runs AFTER hooks.onRequest (not before); cite fetch-handler.ts:136-147 for exact order
- agent-runners/in-memory ref: delete globalThis[Symbol] leaves module-captured reference intact; use in-place Map.clear() on data.stores + data.historicRunsBackup; flag missing official reset helper as follow-up
- intelligence-mode SKILL: identifyUser does NOT forward thrown Response (resolveIntelligenceUser converts to generic 500); gate auth rejection in hooks.onRequest which does forward Responses
- built-in-agent/factory-modes ref: custom factory example now checks abortSignal.aborted at entry and between yields; note that agent.abortRun() only flips the flag — generator must consult it
- setup-endpoint Express delegate: stream SSE via Readable.fromWeb().pipe(res) instead of buffering via arrayBuffer(); applies to both the recommended pattern and the CRITICAL "Correct" example. Add Node 18.17+ note.
- built-in-agent frontmatter: maxSteps defaults to undefined (AI SDK stops after one generation), not 1.
- built-in-agent examples: replace bare anthropic("claude-sonnet-4") / anthropicText("claude-sonnet-4") with the dated id "claude-sonnet-4-5-20250929" (AnthropicMessagesModelId in @ai-sdk/anthropic@3.0.49 does not accept the bare form).
- 0-to-working-chat Next.js: align catch-all with migration-playbook — use [[...slug]] so the bare /api/copilotkit basePath also routes.
- migration-playbook v1->v2 parameters: v1 entries default to required: true; only map to .optional() when required: false was explicit. Prevents silently flipping the contract.
- switching-agents Recipe 2 (Tabs): hold activeAgent in a ref so the onAgentsChanged subscribe effect only re-binds on copilotkit change, not on every tab click.
- a2ui-rendering custom-catalog example: use the real createCatalog(definitions, renderers) signature with Zod schemas; remove the bogus extractSchema<T>() generic (types erase at runtime).
- spa-without-runtime clipboard handler: correct the comment — returning a structured error does NOT trigger onError with tool_handler_failed; only thrown handlers do.
- agent-runners: agent_thread_locked fires only on Intelligence 409, not SSE;
SSE surfaces "Thread already running" as a plain 500 body
- agent-runners: SqliteAgentRunner install-hint is unreachable — the import of
@copilotkit/sqlite-runner fails at module load if better-sqlite3 is missing
- agent-runners: soften "CopilotIntelligenceRuntimeOptions explicitly excludes
runner" — the field is simply not declared; excess-property checks flag it
- agent-runners/sqlite: mirror real schema (CREATE INDEX IF NOT EXISTS)
- middleware: consistent afterRequestMiddleware signature across Wrong/Correct
examples; document the full { runtime, response, path, messages?, threadId?,
runId? } shape
- server-side-tools: Factory Mode example now uses AI SDK + the real converter
(convertToolDefinitionsToVercelAITools); TanStack caveat called out since
defineTool output is not a TanStack tool and there is no built-in converter
- setup-endpoint: Cloudflare Workers example hoists runtime/handler to module
scope behind a lazy getter; openaiText signature corrected to createOpenaiChat
when passing an explicit API key (openaiText config omits apiKey)
- transcription: JSON payload mode works in both multi-route and single-endpoint
— dispatch is by Content-Type, not endpoint mode
- wiring-external-agents/mastra: getLocalAgents requires resourceId; remote
Mastra section now points at HttpAgent instead of misshaping getRemoteAgents
- package.json: add per-condition types to every exports entry so bundler and
moduleResolution: "bundler"/"node16" both pick up the matching .d.mts/.d.cts
intent edit-package-json replaced "files": ["skills"] instead of appending, excluding dist/ from runtime and react-core published packages. publint caught it (pkg.exports.[].import/require -> file not published). Restoring dist + skills.
a2ui-renderer was unaffected (had existing "files": ["dist"] which got appended correctly).
- Keep deletion of sdk-python/copilotkit/langgraph_agent.py (deprecated LangGraphAgent
removed in this PR; main's unrelated bug fixes are superseded by our removal)
- Resolve poetry.lock conflict by taking main's ag_ui_langgraph 0.0.33
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review-loop fixes on the runtime surface:
- sse-response.ts: wrap DebugEventBus.broadcast in try/catch inside the
observer's next — a buggy subscriber used to propagate an exception
that RxJS routed to `error`, killing the SSE stream for an unrelated
reason. Logged via the existing logError helper instead.
- sse-response.ts: non-abort write failures no longer fall through
silently. Previously streamClosed stayed false and the next event
re-attempted a broken writer. Now logs + sets streamClosed.
- sse-connect-agent-id.test.ts: new unit test that feeds a synthetic
observable into handleSseConnect and asserts the emitted envelope
carries the route-resolved agentId ('weather-agent') rather than
the literal 'connect'. Regression guard for the fix in 7f3a8c27c
that the /run-based integration tests didn't cover.
- debug-events.suite.ts: replaced a version of this assertion that
couldn't reliably drive /connect in the integration runtime with a
pointer to the unit test above.
- fetch-router.ts: clarify the cpk-debug-events comment — the prefix
is what guards against agent-name collision, not the ordering of
router branches.
- debug-stream.ts (extension client): include the underlying JSON parse
error message in the emitted error so a truncated SSE frame can be
distinguished from a runtime/protocol version mismatch.
Per PR review suggestion (14) — a cpk- prefix makes collision with a
user-named agent essentially impossible. The earlier change only
documented the route as reserved; renaming it removes the hazard.
Rename scope:
- URL path segment (/debug-events → /cpk-debug-events) in the router,
handler switch arms, method-not-allowed check, and integration suite.
- RouteInfo union discriminator ('debug-events' → 'cpk-debug-events')
in hooks.ts and its two consumers in fetch-handler.ts.
- VS Code DebugStream client URL.
- Docs page (event-inspector.mdx) — both the Callout and the Steps copy.
Left unchanged (internal names that describe the feature but aren't the
public route):
- packages/runtime/src/v2/runtime/handlers/handle-debug-events.ts
- packages/runtime/src/v2/runtime/core/debug-event-bus.ts
- packages/shared/src/debug-event-envelope.ts (and the DebugEventEnvelope
type)
- 'debug-event' (singular) message type in the VS Code webview bridge —
that's the extension host ↔ webview protocol, not the HTTP route.
Critical
- scripts/hooks/check-binaries.sh: restore showcase data-file exclusions
(demo-content / search-index / starter-content >1 MB) that the inline
refactor dropped; add 'set -eu' so silent shell failures don't hide
policy violations.
- packages/vscode-extension/src/extension/preview-panel.ts: drop 'blob:'
from CSP script-src — it lets arbitrary-string JS execute via Blob URL
and defeats most of CSP's XSS protection.
- packages/vscode-extension/package.json: set private: true. The extension
ships as a .vsix via vsce, not npm, and workspace:* devDependencies
would break an accidental 'npm publish'.
Important
- runtime: forward the real agentId from handleConnectAgent into
handleSseConnect / createSseEventResponse so DebugEventBus envelopes
on /connect carry the actual agent name instead of the literal
'connect'. Updates handle-connect.ts, sse/connect.ts.
- hooks/panel.ts CSP: narrow connect-src from 'https:' to just the
Tailwind CDN. The preview path never drives a real CopilotKit runtime
— all hook calls route through the stub — so there's no legitimate
https: fetch to allow from inside bundled user code.
- extension/utils.ts getNonce(): switch to crypto.randomBytes. Math.random()
is not acceptable for a value that gates inline-script execution.
- .github/workflows/vscode-extension.yml:
* Build step uses 'nx run copilotkit-vscode-extension:build' instead
of 'pnpm run build' (targeted build with Nx caching, not full
monorepo rebuild).
* Added explicit 'Type check' step (tsc --noEmit).
* Added Lint step gated with continue-on-error until the Nx target
exists, so a missing target doesn't break the pipeline.
* Publish job now queries the Marketplace for the current published
version and skips 'vsce publish' when the local package.json
version matches — stops every docs/CI-only push to main from
failing on duplicate-version errors.
- hooks/hook-scanner.ts: bound the synchronous walk at 20 000 files so a
pathologically large workspace can't freeze the extension host; flag
kept in module-level constant with a rationale comment.
- sse-response.ts: document that debugEventBus.broadcast intentionally
runs before the stream-closed gate so debug subscribers see trailing
events even after the SSE client disconnects.
- inspector-panel.ts: subscribe to DebugStream lazily on show() rather
than in the constructor; unsubscribe on panel dispose. Avoids firing
the event callback on every envelope when no panel is open.
Suggestions
- fetch-router.ts: document 'debug-events' as a reserved route so it
can't be shadowed by an agent literally named 'debug-events'.
- activate.ts findValuePosition: add optional startOffset parameter,
document the first-occurrence limitation + the follow-up path for
per-fixture precision.
- activate-hook-explorer.ts isInsideWorkspace: fix JSDoc to reflect the
code (root itself is excluded).