Commit Graph

349 Commits

Author SHA1 Message Date
Martha Schumann 3d6166611e refactor(runtime,inspector): tighten types and align JSDoc with reality
Type quality:
- intelligence/threads.ts handleGetThreadMessages: switch on the Message
  discriminant (role) and read narrowed fields directly. Removes
  `as Record<string, unknown>` laundering and chained `as` casts on
  toolCalls/function/arguments. AssistantMessage's toolCalls always have
  `function: { name, arguments }`, so the prior fallbacks (`tc.name`,
  `tc.args`) were dead branches.
- in-memory.ts getThreadState: import StateSnapshotEvent and use it
  instead of `(event as { snapshot?: unknown }).snapshot`.

Comment / API alignment:
- intelligence/threads.ts handleClearThreads JSDoc no longer claims the
  inspector calls this; the actual caller is the demo button.
- in-memory.ts clearThreads JSDoc updated to match.
- in-memory.ts getThreadEvents JSDoc no longer references a SQLite
  runner that does not exist; just describes the compaction logic.
- web-inspector lint fix: rename unused `changed` parameter to
  `_changed` per oxc no-unused-vars.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:45:07 -07:00
Martha Schumann 7dcaa15063 test(core,runtime): strengthen error-log assertions and stub identity
- thread-store-registry: makeStore now attaches a __testId via
  intersection so callers can distinguish stubs at a glance during
  debugging instead of relying on identity-by-allocation alone.
- thread-store-registry subscriber-isolation test: assert the
  diagnostic content ("Subscriber onThreadStoreRegistered error") and
  Error argument, not just that some error was logged.
- handle-threads identifyUser-throws test: assert
  "Error identifying intelligence user" with an Error argument, since
  the throw originates inside resolveIntelligenceUser which logs and
  returns 500 before subscribeToThreads is reached.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:13:43 -07:00
Martha Schumann 7d525d62aa test(runtime): align MessagePopulatingTestAgent with TestAgent contract
- Add the missing protected connect() override returning EMPTY so the
  mock matches TestAgent and ThrowingAgent. Without it, a clone() that
  ever exercised connect() would fall through to AbstractAgent.connect()
  and may try to open a real transport in tests.
- clone() now forwards this.agentId directly instead of coercing
  undefined to "". The constructor accepts string | undefined to make
  this type-safe — coercion would silently turn "no agent id" into
  "empty agent id", a different state per AgentConfig.agentId?: string.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 12:13:22 -07:00
Martha Schumann d5e5da6dce test(runtime): tighten thread-handler and in-memory-runner assertions
handleClearThreads is intentionally synchronous, but neither test
asserted the return type. Add `expect(response).not.toBeInstanceOf(Promise)`
on both branches so a regression that starts awaiting I/O updates the
synchronous call sites.

The handleGetThreadEvents/handleGetThreadState 501 tests asserted the
status code but not that intelligence stayed untouched. Stub spies for
both `listThreads` and a hypothetical `getThreadEvents`/`getThreadState`,
then assert neither was called — so a regression that drops the early
return and falls through to platform calls fails this test even after
the response code changes.

The handleSubscribeToThreads 500 test created an `errorSpy` to silence
console output but never asserted on it. Add `expect(errorSpy).toHaveBeenCalled()`
so a regression that quietly drops the diagnostic log is caught.

The InMemoryAgentRunner getThreadEvents test claimed the synthetic
terminal event was present but only asserted on TEXT_MESSAGE_*. Add an
explicit assertion on `RUN_ERROR` with `code: "INCOMPLETE_STREAM"` so
finalizeRunEvents' contract is locked in — a regression that stops
appending the synthetic event would leave the inspector showing an
in-progress thread forever.

Declare `onNewMessage` on `MessagePopulatingTestAgent.runAgent`'s options
type to match the runner's call site and `TestAgent` above. Without it,
a regression that starts depending on `onNewMessage` here would compile
cleanly even though the mock would silently drop the call.
2026-04-30 11:54:14 -07:00
Martha Schumann 7421bf12b1 test(runtime): tighten thread-handler and in-memory-runner tests
handle-threads.test.ts:
- handleGetThreadMessages intelligence-path test now asserts the response
  body verbatim, so a regression that swaps in a stub body is caught.
- 422-no-intelligence test issues a real DELETE request for the delete
  path instead of cloning a POST request.
- handleClearThreads block carries a comment explaining why the handler
  is intentionally synchronous (no I/O on either branch).
- The identifyUser-throws test now silences console.error for the
  duration of the assertion, matching the pattern already used by the
  subscribe-throws test.

in-memory-runner.test.ts:
- ThrowingAgent test asserts RUN_ERROR is the last emitted event and
  that no RUN_FINISHED is emitted, locking in terminal-event semantics.
- getThreadEvents test asserts the full TEXT_MESSAGE triple is present
  in the persisted event log, and the comment now reflects the real
  finalizeRunEvents behaviour (it appends a synthetic terminal event
  when the agent does not emit one, so terminal events ARE persisted).
- getThreadState multi-run test gains a cross-thread isolation
  assertion: a snapshot on a different thread must not bleed into the
  original thread's state.
- Bumped the inter-thread sort delay from 5ms to 20ms to absorb timer
  jitter on slow CI runners.
- Removed four redundant `agent.agentId = "test-agent"` reassignments
  (the constructor already sets it via super({ agentId })).
- Aligned MessagePopulatingTestAgent.runAgent with TestAgent: `onEvent`
  is now required so the runner contract is exercised consistently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:14:09 -07:00
Jordan Ritter b355acd56c fix(runtime): stop converting TanStack stream after RUN_FINISHED
TanStack's chat() engine runs a multi-turn agent loop: after the model
returns tool calls, it tries to execute them via processToolCalls().
Frontend-only tools (like render_pie_chart) are unknown to TanStack, so
executeToolCalls() treats them as errors and buildToolResultChunks()
re-emits TOOL_CALL_END without a preceding TOOL_CALL_START. The ag-ui
verify middleware rejects this duplicate.

Fix: track a runFinished flag in convertTanStackStream and discard all
events after the first RUN_FINISHED, which marks the boundary between
the streaming pass and TanStack's internal tool execution loop.

Also adds built-in-agent to docker-compose.local.yml and local-ports.json.
2026-04-29 22:41:58 -07:00
Jordan Ritter 497b205d1e fix: auto-format 16 files with pre-existing oxfmt violations
These files accumulated formatting drift across recent PRs. Fixes the
format CI check on main.
2026-04-29 19:12:22 -07:00
Jordan Ritter f2bc4a8dcb fix: default streamSubgraphs to true in LangGraph agent wrapper (#4446)
## Summary

- Explicitly defaults `streamSubgraphs: true` in `LangGraphAgent.run()`
forwardedProps so subagent streaming works with `@ag-ui/langgraph`
0.0.31+, which changed the default from `true` to `undefined`
- Uses nullish coalescing (`??`) so explicit user overrides (including
`false`) are preserved
- Fixes 6 showcase integrations failing E2E probes on the `subagents`
feature

## Why

`@ag-ui/langgraph` 0.0.31 removed the `?? true` fallback on
`streamSubgraphs` in `handleStreamEvents()`. The CopilotKit runtime
never explicitly set this prop, so subgraph event forwarding became
silently disabled. This caused all subagent-dependent demos to stop
working.

A previous v1 fix (commit `21e12afca`) handled this in the now-deleted
`agui-action.ts`, but the logic was lost during the v2 migration.

## Additional context

Investigation identified a second issue: `@ag-ui/langgraph` 0.0.34
includes state snapshot fixes needed for shared-state-read/write
features, but 0.0.34 is not yet published on npm (0.0.31 is latest). The
ag-ui team needs to cut a release. Full analysis: [Notion
write-up](https://app.notion.com/p/3513aa381852812a9ecef5fbf1e71739)

## Test plan

- [x] Runtime tests pass (1412/1412)
- [x] 7-agent CR converged Round 1 (0 findings)
- [ ] Verify showcase E2E probes for subagent features turn green after
merge
2026-04-29 16:57:58 -07:00
Jordan Ritter 1baeb9940a fix: use RegExp routes in Express adapter for Express 4/5 compatibility
Express 4 does not support the {*splat} wildcard syntax introduced in
Express 5. Replace string-based wildcard patterns with RegExp routes
that work across both major versions.
2026-04-29 16:50:19 -07:00
Jordan Ritter c0c97b4bf1 fix: default streamSubgraphs to true in LangGraph agent wrapper
The @ag-ui/langgraph 0.0.31+ changed the default for streamSubgraphs
from true to undefined, breaking subagent streaming. This enriches the
run() input's forwardedProps with streamSubgraphs: true as a default,
while preserving any explicit user override via the nullish coalescing
operator.
2026-04-29 15:59:08 -07:00
Martha Schumann d55d5495c7 Merge remote-tracking branch 'origin/main' into feat/CPK-7193-inspector-threads-clean
# Conflicts:
#	pnpm-lock.yaml
2026-04-29 13:31:32 -07:00
Jordan Ritter 9c9af7665f fix: update express wildcard routes for Express 4.20+ compat
Express 4.20+ uses path-to-regexp v8 which requires named wildcard
params. Bare * is no longer valid — use {*splat} syntax instead.

Updated production code (express.ts) and test (express-fetch-bridge).
Restores express >=4.20.0 override now that code is compatible.
2026-04-28 10:33:05 -07:00
Jordan Ritter a23bdccb6c fix: use /* instead of * in express route for Express 4.20+ compat
Express 4.20+ uses path-to-regexp v8 which rejects bare * as a route
pattern. Use /* which is the forward-compatible syntax.
2026-04-28 10:33:05 -07:00
github-actions[bot] 8334dcdde6 style: auto-fix formatting 2026-04-27 16:32:58 +00:00
Alem Tuzlak 18f6c1e2e5 fix(runtime): close open reasoning message in REASONING_END and on REASONING_START reopen 2026-04-27 18:30:01 +02:00
Alem Tuzlak a48e9a17b3 fix(runtime): align TanStack state-tool detection on rawContent ?? raw.result fallback 2026-04-27 18:16:15 +02:00
Alem Tuzlak 7bcd2f81ee fix(runtime): track REASONING message-open and run-open separately to avoid duplicate REASONING_MESSAGE_END 2026-04-27 18:01:54 +02:00
Alem Tuzlak d128257dd2 feat(runtime): auto-close open REASONING lifecycle in TanStack converter 2026-04-27 13:50:50 +02:00
Alem Tuzlak 6f77116aee feat(runtime): pass through REASONING_* chunks in TanStack converter 2026-04-27 13:47:02 +02:00
Alem Tuzlak d07c1d109e test(runtime): assert TanStack converter does not emit STATE_* for non-state tools 2026-04-27 13:42:24 +02:00
Alem Tuzlak a0be5455ab feat(runtime): emit STATE_DELTA on AGUISendStateDelta in TanStack converter 2026-04-27 13:40:38 +02:00
Alem Tuzlak 5f233a1987 feat(runtime): emit STATE_SNAPSHOT on AGUISendStateSnapshot in TanStack converter 2026-04-27 13:37:36 +02:00
Alem Tuzlak 71005252b8 test(runtime): add TanStack tool-result + reasoning chunk helpers 2026-04-27 13:17:09 +02:00
Mike Ryan ae978b95b1 test(runtime): align intelligence telemetry mock platform 2026-04-24 11:22:49 -07:00
github-actions[bot] 5bd77b35f0 style: auto-fix formatting 2026-04-24 11:22:49 -07:00
Mike Ryan ad7a383095 fix(runtime): tighten realtime startup handling 2026-04-24 11:22:49 -07:00
Mike Ryan 17bd39cd26 fix(runtime): finalize realtime connection responses 2026-04-24 11:22:49 -07:00
Mike Ryan 05aa15f908 fix(runtime): align intelligence realtime contracts 2026-04-24 11:22:49 -07:00
Benjamin Taylor b8e7b575c3 refactor(runtime): drop framework tag from v2 telemetry
The framework label was only meaningful for Segment's per-event
properties. Scarf receives only the event name, so the label has no
destination. Removes the `framework` option on createCopilotRuntimeHandler
and its pass-through from the hono/express adapters, plus the now-unused
setGlobalProperties/globalProperties/flattenObject on the telemetry
client.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:01:27 -05:00
github-actions[bot] dfeedce2b5 style: auto-fix formatting 2026-04-24 11:01:27 -05:00
Benjamin Taylor 3ba588cad4 refactor(runtime): remove Segment channel from v2 telemetry client
The v2 TelemetryClient still pings Scarf for pixel-style event counts,
but no longer ships per-event properties to Segment. Drops the
`@segment/analytics-node` import, anonymousId, write key, and track call;
the dep stays in package.json until a follow-up lockfile update.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:01:27 -05:00
Benjamin Taylor bf4a071388 fix(runtime): Restore telemetry calls lost in fetch migration 2026-04-24 11:01:27 -05:00
github-actions[bot] b3339c7c29 style: auto-fix formatting 2026-04-23 01:59:07 +00:00
Martha Schumann 68e247a78d refactor(inspector): port thread UI from Angular to Lit + add per-thread events/state endpoints
Replaces the Angular-backed cpk-thread-list / cpk-thread-details custom
elements with native Lit implementations inside @copilotkit/web-inspector,
so React, Vanilla, and any other non-Angular consumer of the inspector
gets full functionality without pulling the Angular runtime. The
@copilotkit/web-inspector-angular package is deleted entirely, and the
Angular demo no longer calls defineInspectorElements.

Backend: adds GET /threads/:id/events and GET /threads/:id/state to the
runtime (in-memory runner path). The Intelligence path returns 501 with
a clear "not yet supported on this runtime" empty state — coordination
with the Intelligence team is tracked separately (CPK-7453).

Also guards attachToCore's getThreadStores call so consumers on an older
@copilotkit/core don't throw when assigning inspector.core, and drops
two unrelated showcase-whitelist lines that landed in
scripts/hooks/check-binaries.sh during a prior merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 18:54:10 -07:00
Martha Schumann 2a9b2d3e60 chore: merge origin/main into feat/CPK-7193-inspector-threads-clean
- hooks.ts: keep both threads/clear and cpk-debug-events in RouteInfo
- use-threads.tsx: keep registerThreadStore effect + adopt main's
  runtimeStatus gating for context dispatch
- use-threads.test.tsx: keep both our register/unregister test and
  main's new runtimeConnectionStatus=Connected gating test
- scripts/hooks/check-binaries.sh: add shell-docs and shell-dojo
  demo-content.json exclusions (main introduced these >1MB files without
  updating the exclusion list)
- lefthook.yml, pnpm-lock.yaml: accept main's version

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 13:04:13 -07:00
Martha Schumann 990c097268 fix(inspector): address post-review bugs and test gaps
- 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>
2026-04-22 12:53:07 -07:00
Martha Schumann 96e8112039 Merge remote-tracking branch 'origin/main' into claude/langgraph-tests-cleanup-1jVZ3 2026-04-22 09:48:55 -07:00
Mike Ryan 79e1bebece fix(runtime): require identifyUser name in intelligence mode 2026-04-22 09:55:59 -05:00
Mike Ryan 4369252a1e chore(format): run prettier on runtime test 2026-04-22 07:24:51 -07:00
Mike Ryan 85ea1969e5 feat(runtime): remove intelligence organization id config 2026-04-22 07:24:51 -07:00
Alem Tuzlak 90941ed08e Merge branch 'main' into fix/issue-2118 2026-04-22 14:43:39 +02:00
Alem Tuzlak a337b5f63d Merge branch 'main' into fix/issue-2118 2026-04-22 14:29:11 +02:00
Alem Tuzlak ec905205f9 Merge branch 'main' into fix/issue-3637 2026-04-22 14:27:14 +02:00
Alem Tuzlak 2b88450b44 fix: prevent 'Unknown provider' regression for LangChainAdapter (#3840)
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.
2026-04-22 14:23:49 +02:00
Alem Tuzlak 24b5e80bcc Merge remote-tracking branch 'origin/main' into fix/issue-2118 2026-04-22 12:59:14 +02:00
Alem Tuzlak f9d01dfd3b refactor(runtime): extract resolveMessageId helper and cover it with tests
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.
2026-04-22 12:59:06 +02:00
Alem Tuzlak db38f50918 test(runtime): cover handleServiceAdapter model-source resolution (#3217)
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.
2026-04-22 12:39:31 +02:00
github-actions[bot] 15c6e027b9 style: auto-fix formatting 2026-04-22 09:58:37 +00:00
Alem Tuzlak d7c7b52620 test(runtime): cover Retry-After parsing and 429 fetch path (#3637)
Adds parseRetryAfter unit tests (header absent, integer seconds, zero,
negative, future/past HTTP-date, unparseable) and fetchWithRetry 429
branch tests (honored within limit, rejected when exceeding
maxRetryAfterSeconds, fallback to exponential backoff when missing,
ignored on non-429 retryable statuses).
2026-04-22 11:56:53 +02:00
Jordan Ritter 4b4923561b feat: VS Code extension — Hook Explorer, AG-UI Inspector, webview-first sidebars (#3935)
## 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
2026-04-21 17:09:45 -07:00