Commit Graph

977 Commits

Author SHA1 Message Date
Alem Tuzlak b095cf0443 fix: add remarkPlugins/rehypePlugins passthrough support (#2296) (#3822)
## Summary

Fixes #2296

The Markdown component had hardcoded remark/rehype plugins with no way
for consumers to add their own. This adds optional `remarkPlugins` and
`rehypePlugins` props that are spread into the existing plugin arrays,
enabling custom markdown processing.

## Test plan

- [ ] Verify custom remarkPlugins are applied alongside built-in plugins
- [ ] Verify custom rehypePlugins are applied alongside built-in plugins
- [ ] Verify Markdown renders correctly without custom plugins (no
regression)
2026-04-22 14:26:17 +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 77dd2abc29 Merge remote-tracking branch 'origin/main' into fix/remark-rehype-plugins-2296
# Conflicts:
#	packages/react-ui/src/components/chat/Markdown.tsx
2026-04-22 12:53:35 +02:00
Alem Tuzlak 38e95491e7 refactor(react-ui/Markdown): pass through all react-markdown Options via spread
Addresses PR feedback on #3822.

Instead of plumbing remarkPlugins and rehypePlugins through the component
one prop at a time, MarkdownProps now extends Omit<Options, "children">
and spreads the remainder into the underlying ReactMarkdown. Consumers
can now pass any react-markdown Options field (urlTransform, allowedElements,
skipHtml, etc.) without further plumbing.

The three fields that need library-specific defaults (components,
remarkPlugins, rehypePlugins) are still destructured, merged, and memoized
internally so the built-in behavior is preserved and merged values keep
stable references across renders.

Dropping the custom memo comparator — now that arbitrary props flow
through, a fixed 4-key check would silently skip re-renders when
consumer-provided props change. React's default shallow-compare, combined
with the internal useMemo on merged values, gives correct behavior without
regressing the memoization for the common case.
2026-04-22 12:50:09 +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
Alem Tuzlak e71b27ed97 Merge remote-tracking branch 'origin/main' into fix/issue-2651
# Conflicts:
#	packages/shared/src/utils/json-schema.ts
2026-04-22 12:02:32 +02:00
Alem Tuzlak b28d15339c feat(react-core): add pin-to-send scroll mode to CopilotChat v2 (#4142)
## What does this PR do?

Adds a new `"pin-to-send"` value to `CopilotChatView`'s `autoScroll`
prop, matching ChatGPT's scroll behavior: when the user sends a message,
that message scrolls to the top of the viewport and stays there while
the assistant's response streams in below. The user reads at their own
pace — the viewport does not chase the bottom.

### New public API

```ts
autoScroll?: "pin-to-bottom" | "pin-to-send" | "none" | boolean
```

- `"pin-to-bottom"` — current behavior. Chases the bottom as content
streams (still the default).
- `"pin-to-send"` — **new.** Scrolls the latest user message to ~16px
from the top, maintains a dynamic bottom spacer so the message can
actually reach the top, and does not chase the bottom.
- `"none"` — no auto-scroll.
- Boolean back-compat: `true` → `"pin-to-bottom"`, `false` → `"none"`.
Existing consumers unchanged.

`AutoScrollMode` type is exported from the package barrel.

### Example usage

```tsx
<CopilotChat autoScroll="pin-to-send" />
```

### How it works

1. `CopilotChat` computes the latest user message ID from `messages` and
publishes `{ id, sendNonce }` via a new `LastUserMessageContext`. The
nonce increments on each new send so message edits also retrigger.
2. A new `usePinToSend` hook reads the context and, on each new send:
measures viewport + user message heights, sets a spacer `<div>`'s
height, then does a single smooth `scrollTo` to the user message's
offset.
3. A shrink-only `ResizeObserver` on content collapses the spacer as the
assistant's response fills space below, so there's no wasted empty
space.
4. `ScrollView` branches on the normalized mode: existing
`<StickToBottom>` path for `"pin-to-bottom"`, existing plain-div path
for `"none"`, new `PinToSendScrollContainer` for `"pin-to-send"`.

### Edge cases handled

- Thread restore with existing messages doesn't trigger a spurious
scroll.
- `scrollTo` target computed via `getBoundingClientRect` arithmetic —
robust across any CSS positioning.
- Virtualization: the spacer lives outside the virtualized list.
- Back-compat: `autoScroll={true|false}` produces byte-for-byte
identical DOM to pre-change.

### Tests

- 7 unit tests for `normalizeAutoScroll`.
- 4 TDD behavior tests for `usePinToSend` (spacer sizing, scroll target,
shrink-only ResizeObserver, cleanup).
- 5 integration tests for `CopilotChatView` covering all three modes +
boolean back-compat.
- Full `@copilotkit/react-core` suite: **85 files / 1120 tests pass,
zero regressions.**
- publint + attw green across all 13 packages.
- Existing `CopilotChatView.slots.e2e.test.tsx` (43 tests) unchanged.

## Related PRs and Issues

- (none — new feature)

## Checklist

- [x] I have read the Contribution Guide
- [ ] Docs site update to follow in a separate PR

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-22 10:54:35 +02:00
Jordan Ritter fbdaa75d65 docs(vscode-extension): correct RELEASING.md to reflect metadata-sync automation
The aimock-parity paragraph and the Rollback section still described
contributors hand-editing both package.json and CHANGELOG.md. That was
stale — the vscode-extension-changelog-sync.yml workflow auto-syncs
package.json.version (and optionally package.json.description) from
the CHANGELOG entry (and README first paragraph) when the PR is opened
and commits 'chore: release vX.Y.Z' on the PR branch.

Rewrite both passages so the documented flow matches reality: contributor
edits CHANGELOG.md only (optionally README.md), sync workflow handles the
package.json bump automatically, publish workflow self-gates via vsce show
and ships on merge to main.
2026-04-21 19:27:11 -07:00
Jordan Ritter b64980d0aa style(vscode-extension): preformat workflow yaml and RELEASING.md to match CI formatter 2026-04-21 18:41:48 -07:00
github-actions[bot] f97698bdda chore(vscode-extension): sync description from README 2026-04-22 01:22:20 +00:00
Jordan Ritter 0f76dc11f2 ci(vscode-extension): sync package.json description from README 2026-04-21 18:21:42 -07:00
Jordan Ritter bb983470f6 docs(vscode-extension): describe one-file-edit release flow
Rewrites the 'Cutting a release' section to match the new
changelog-sync automation: maintainers prepend a CHANGELOG entry,
commit with any message, open a PR, and the sync workflow auto-bumps
package.json with 'chore: release vX.Y.Z'. Adds a manual escape hatch
for when the sync workflow is unavailable.

Other sections (Prerequisites, Open VSX setup, CI publish flow,
Rollback, Notes) are unchanged.
2026-04-21 18:19:20 -07:00
Jordan Ritter bfb17691bb chore(vscode-extension): drop release helper script; match aimock manual flow 2026-04-21 18:19:19 -07:00
Jordan Ritter b018869676 ci(vscode-extension): swap Marketplace auth to OIDC (Entra federated SP)
- Use azure/login@v2 + vsce --azure-credential instead of VSCE_PAT
- Add id-token: write permission on publish job
- Add verify-pat --azure-credential pre-flight to catch auth issues before publish
- Retain OVSX_PAT for Open VSX (OIDC not yet supported there)
- Update RELEASING.md: new auth flow + rollback guidance
2026-04-21 18:19:19 -07:00
github-actions[bot] 51c967b9f5 style: auto-fix formatting 2026-04-21 18:19:19 -07:00
Jordan Ritter 4d354415b2 docs(vscode-extension): document main-push release flow
Rewrite the 'Cutting a release' + 'CI publish flow' sections to describe
the aimock-pattern flow (hand-bump + PR-merge + self-gated CI publish)
and the new multi-bullet --summary/--type CLI. Retain the Open VSX
one-time-setup block, the transient-failure handling notes, verification
steps, rollback guidance, and ADO PAT retirement note.
2026-04-21 18:19:19 -07:00
Jordan Ritter 8bfef15d8c chore(vscode-extension): aimock-style release helper + CHANGELOG seed
Rewrite vscode-extension-release.sh to cut an aimock-style release commit
(bump package.json + prepend CHANGELOG.md section) without tagging or
pushing — CI now owns tag/release creation on main-push. Takes one or
more --summary bullets grouped by --type (Added/Changed/Fixed/Removed/
Deprecated/Security, Keep-a-Changelog style). Supports --dry-run.

Seed packages/vscode-extension/CHANGELOG.md with a retroactive v0.1.0
entry covering the three launch surfaces (A2UI Preview, Hook Explorer,
AG-UI Inspector) so the first real release has prior history to anchor
against and the GH Release step has notes to pull from.
2026-04-21 18:19:18 -07:00
Jordan Ritter 2670b041db chore(vscode-extension): retry publish on transient registry failures
Wrap both registry publish steps in a bash retry helper that retries up
to 5 times with staggered backoff (10s/20s/40s/60s/90s) on transient
conditions (5xx, timeouts, connection resets, DNS). Auth and validation
errors still fail fast with no retry.

Critically, 'version already exists' is treated as idempotent success:
if attempt N-1 landed on the registry but its response was lost to a
502 after commit, attempt N sees the already-published version and
short-circuits rather than failing the job.

Motivated by Open VSX /publish returning intermittent 502 Bad Gateway
errors from Eclipse Foundation infra. Each attempt is wrapped in
::group:: markers so per-attempt logs are collapsible in the Actions
UI. Reconciliation step updated to reflect retry semantics and to
tell the operator to rerun the job (not bump the version) on exhausted
retries. RELEASING.md gets a 'Transient registry failures' section
documenting the behavior for both CI and manual publish paths.
2026-04-21 18:19:18 -07:00
Jordan Ritter 9878de8246 docs(vscode-extension): add RELEASING.md runbook
Covers prerequisites (VSCE_PAT + OVSX_PAT as production env secrets),
the one-time Open VSX namespace claim, how to cut a release via the
local helper script, the CI publish flow, verification URLs, rollback
guidance (registries are immutable, ship forward), and a note on the
2026-12-01 Azure DevOps PAT sunset.
2026-04-21 18:19:18 -07:00
Tyler Slaton a66f068e57 feat(react-core): add pin-to-send scroll mode to CopilotChat v2
Widens CopilotChatView's autoScroll prop to accept "pin-to-bottom" |
"pin-to-send" | "none" | boolean. "pin-to-send" scrolls the latest
user message to ~16px from the top on send and maintains a dynamic
bottom spacer so the viewport doesn't chase the streaming response.

Boolean back-compat: true -> "pin-to-bottom", false -> "none".
2026-04-21 17:41:55 -07: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
github-actions[bot] 963a06a3ea style: auto-fix formatting 2026-04-21 23:55:37 +00:00
Jordan Ritter 895f9b782b chore(vscode-extension): prep for Marketplace publish
- Remove private flag; add repository, bugs, homepage, pricing, keywords
- Update description to include Hook Explorer and AG-UI Inspector
- Add top-level icon (PNG) for Marketplace listing
- Add .vscodeignore to keep VSIX lean
- Add README.md and LICENSE for Marketplace listing
- Add vscode:prepublish script
2026-04-21 16:53:56 -07:00
github-actions[bot] 58dc1fee62 style: auto-fix formatting 2026-04-21 16:25:11 -07:00
Mike Ryan 219f08ccb9 chore(runtime): clean up connect API and test typing 2026-04-21 16:25:11 -07:00
github-actions[bot] 6a04464bb0 style: auto-fix formatting 2026-04-21 16:25:11 -07:00
Mike Ryan 25f6f15418 refactor(runtime): Support durable compaction of threads 2026-04-21 16:25:11 -07:00
Alem Tuzlak eb6e4d63cc chore(vscode-extension): drop duplicate refresh buttons + remove inspector button from A2UI tab
- package.json: remove the view/title binding that put
  'Open AG-UI Inspector' on the A2UI Catalog view — the inspector is
  its own sidebar tab now, so adding that action to the catalog title
  bar was redundant noise. Inspector view keeps its own binding.
- Both sidebar webviews (hook-list + catalog-list) had an in-webview
  refresh button that duplicated the native VS Code title-bar refresh
  icon already registered under view/title. Drop the React buttons
  and rely on the native icons; single source of truth and cleaner
  header.
2026-04-21 11:48:17 +02:00
Alem Tuzlak fe8f36e027 chore(vscode-extension): rename Hook Explorer sidebar title to 'Generative UI'
User-facing labels only:
- package.json views.copilotkit.hooks.name → 'Generative UI'
- webview sidebar header → 'Generative UI'
- webview <title> → 'Generative UI'
- Output channel → 'CopilotKit Generative UI'
- Warning toast text updated to match

Internal identifiers (view id copilotkit.hooks, file names, class
names, commands copilotkit.hooks.*) kept stable to avoid churn in
settings, keybindings, and external scripts.
2026-04-21 11:44:42 +02:00
github-actions[bot] 146fa4e6a5 style: auto-fix formatting 2026-04-20 17:15:35 +00:00
Alem Tuzlak f5cd389371 fix(vscode-extension): surface silent failures + buffer-aware lens + bounded queues
Review-loop fixes on the VS Code extension surface:

Scanner / lens
- hook-scanner.ts: extract scanContent(filePath, content) so callers
  with the live editor buffer can parse that instead of re-reading the
  last-saved on-disk version. scanFile keeps the same on-disk semantics
  via delegation.
- hook-scanner.ts: scanWorkspace now returns {sites, capped, filesScanned}
  so MAX_FILES_SCANNED truncation is observable instead of silent.
- activate-hook-explorer.ts: when the cap trips, log the detail to the
  Hook Explorer output channel and surface a user-facing warning toast
  pointing at it. Tighten the save-path catch to log unexpected throws
  instead of swallowing them.
- hook-lens-provider.ts: consume document.getText() via scanContent so
  lenses follow unsaved edits. Output channel is now required and the
  scanContent-throw path logs to it.

Panels (bounded queues + durable error trails)
- preview-panel.ts + hooks/panel.ts: pendingMessages now capped
  (1000 and 32 respectively). A wedged webview (CSP violation, crashed
  bundle, never-ready) used to grow these buffers unbounded on every
  save. Latched pendingCapLogged logs one breadcrumb per wedged
  episode, resets on 'ready' (and on dispose for hooks/panel) so a
  recovered session can re-arm the warning.
- preview-panel.ts: removed the misleading type:'error' message on
  successful .fixture.ts[x] bundles — a valid fixture is not an error.
- activate.ts: catalog onOpenSource now wraps openTextDocument in a
  try/catch that appends the full stack to a dedicated
  'CopilotKit A2UI Catalog' output channel in addition to the toast,
  so open-source failures leave a durable trail instead of a 5-second
  flash.
- activate.ts: revert the dead startOffset param added to
  findValuePosition — the plumbing through validateFixtureMessages
  wasn't done in the same PR, leaving unused API surface. Kept the
  limitation comment as documentation.

Catalog wire protocol
- catalog-list-view-provider.ts: normalize fixtureName (?? undefined)
  at the wire boundary so a JSON null can't slip through into callback
  code that declares 'string | undefined'. Exhaustive 'never' guard
  also logs an unknown-variant warning for version-skew scenarios
  (older host receives a newer webview's message).
2026-04-20 19:13:25 +02:00
Alem Tuzlak f1bf3a02e7 fix(runtime): isolate debug broadcast, stop writing to broken streams, cover /connect agentId
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.
2026-04-20 19:12:50 +02:00
Alem Tuzlak 449f73675b chore(vscode-extension): reorder sidebar views — hooks first, then a2ui, then inspector
Puts the two most-used views up top and demotes the inspector to last.
No functional change; just the "views.copilotkit" ordering in the
extension manifest.
2026-04-20 18:39:56 +02:00
Alem Tuzlak dce4bd8f42 fix(vscode-extension): restore blob: in A2UI preview-panel CSP
Dropping 'blob:' from script-src in the prior review-response commit
broke the A2UI catalog preview: webview/App.tsx intentionally
URL.createObjectURLs the bundled IIFE and loads it via
'<script src=blob:…>', so the browser blocked every catalog load with
'Loading the script blob:vscode-webview://… violates Content Security
Policy directive'.

Re-add 'blob:' to preview-panel's script-src with a documented
rationale: the blob content originates from the trusted extension host
over postMessage, not from external input, so the general concern that
'blob: enables arbitrary-string JS' doesn't apply to this threat
model. The hook-preview panel still excludes 'blob:' because it uses
an inline nonce-gated <script> instead.
2026-04-20 18:36:10 +02:00
github-actions[bot] d842349270 style: auto-fix formatting 2026-04-20 16:06:27 +00:00
Alem Tuzlak c2c90e733b refactor(runtime): rename debug-events route to cpk-debug-events
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.
2026-04-20 18:04:50 +02:00
Alem Tuzlak 7f3a8c27c7 fix: address PR review feedback (@ranst91)
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).
2026-04-20 17:56:09 +02:00
github-actions[bot] ea2c70c11d style: auto-fix formatting 2026-04-20 15:05:21 +00:00
Alem Tuzlak 9496984449 Merge remote-tracking branch 'origin/main' into worktree-mutable-discovering-valiant
# Conflicts:
#	docs/content/docs/integrations/langgraph/doctest.json
#	lefthook.yml
#	pnpm-lock.yaml
2026-04-20 17:03:47 +02:00
Alem Tuzlak d4089bbc61 style(vscode-extension): drop render() overlay chip, clarify controls copy
- Remove the '⟵ render() →' chip on the preview card border; the blue
  frame + 'Rendered output' label above already carry that meaning
  clearly enough.
- Caption now says 'The controls on the left/top drive its props live.'
  so it still makes sense at narrow widths where the split collapses
  and the controls stack above the render column.
2026-04-20 15:59:41 +02:00
Alem Tuzlak 300f866f69 feat(vscode-extension): simplify CodeLens label + imported-render fixtures
Label
- CodeLens now reads just '▶️ Preview Component' regardless of hook
  kind / identity — the sidebar already carries the hook name and the
  tooltip still has the full detail when you hover.

Fixtures
- ImportedAirQuality.tsx (V1, useCopilotAction) — render is an imported
  reference to AirQualityBadge, defined in
  test-workspace/hooks/shared/AirQualityBadge.tsx. Confirms the preview
  bundler walks the import when the render prop is a component symbol
  rather than an inline arrow.
- ImportedPollenReport.tsx (V2, useRenderTool) — render is an imported
  PollenReport, which itself imports from ./shared/pollen-copy.ts.
  Exercises a two-hop import graph rooted at the hook's render prop.

Scanner's prefilter skips shared/*.tsx automatically because neither
shared file calls a hook or references @copilotkit/*, so the sidebar
stays tidy — only the two new hook fixtures show up.

Integration test expectations updated: showAirQuality + pollenReport
added to the scan list, and both new files included in the bundle-smoke
loop.
2026-04-20 15:45:19 +02:00
Alem Tuzlak 1b458f1c28 feat(vscode-extension): add 'Preview' CodeLens for CopilotKit hooks
Renders a '▶️ Preview <hook>(<name>)' CodeLens above every render-hook
call-site in .ts / .tsx files. Clicking the lens fires the existing
copilotkit.hooks.preview command with the resolved site — same flow as
the sidebar, just available inline where the hook is called.

- Only render-category hooks (useCopilotAction, useRenderTool,
  useLangGraphInterrupt, useHumanInTheLoop, ...) get a lens; data hooks
  have nothing to preview and are skipped.
- Provider parses on demand via the same oxc scanner as the sidebar, so
  lens positions stay in sync without a separate cache.
- Debounced per-file save handler already rescans sites; we now also
  call lensProvider.refresh() from there so lenses update on save.
2026-04-20 15:33:55 +02:00
Alem Tuzlak 216bec49f2 feat(vscode-extension): convert A2UI catalog sidebar to webview + go-to-source
The catalog sidebar was the last view still rendered via a native
TreeDataProvider while inspector + hooks had already moved to webviews.
Replace it with a matching webview so all three sidebars share the same
Tailwind-driven look and interaction model, and add a 'go to source' action
on every row (component and fixture).

Changes
- New CatalogListViewProvider mirrors HookListViewProvider: buffer
  messages until the webview posts 'ready', then flush init +
  components. Refresh / preview / openSource actions forwarded to
  injected callbacks.
- New React webview at src/webview/catalog-list:
  - Sticky 'A2UI Catalog' header with a refresh button
  - One row per discovered component (name + relative path + 'auto'
    badge when no fixture file exists)
  - Expandable fixtures list (click the chevron / row to toggle)
  - Hover-reveal actions: ▷ preview (component-only) + </> go-to-source
  - Fixture rows preview via click, with a </> button that opens the
    fixture file and reveals the named fixture key
- 'Go to source' for components opens the catalog file; for fixtures
  opens the fixture file and selects the "<name>": key so the user
  lands right on the edit point.
- New bridge types + new tsdown entry for the catalog-list bundle.
- activate.ts: drop the tree provider, own the scan results locally,
  hand them to the webview, keep component registry seeding in sync on
  file-change rescans.
- package.json: componentPreview view is now type: "webview", renamed
  "A2UI Catalog". Removed the TreeView-only view/item/context binding.
- Deleted sidebar/view-provider.ts — no longer referenced.

All three sidebars (Inspector, Hooks, Catalog) now use the same webview
pattern, same go-to-source UX, and the same Tailwind styling.
2026-04-20 15:30:22 +02:00
Alem Tuzlak fd383a8694 feat(vscode-extension): weather-themed hook preview — full V1+V2 render coverage, polished UI
UI chrome
- Replace the unstyled 'hook-preview' classes with Tailwind utilities layered
  over the theme-aware VS Code CSS variables; Tailwind CDN is loaded from
  panel.ts so user render props can use utilities too.
- Render section is now visually unmistakable: 'Rendered output' label,
  sky-blue framed card with a 'render() →' chip, and a caption explaining
  the frame. Caption + sidebar row follow the same visual language.
- Controls + form fields converted to Tailwind (ActionControls,
  CoAgentStateControls, InterruptControls, RenderToolControls,
  CustomMessageControls, RawJsonField, StringField, NumberField,
  BooleanField, FormRenderer).
- JSON textarea styled to match regular inputs: same bg/border/padding,
  minHeight floor, tab-size: 2, ligatures off, spellcheck off, 'JSON' pill.

Sidebar UX swap
- Clicking a render-row now previews the hook (the primary action); the
  hover-button opens source (`</>` glyph). Data-row click still opens
  source because they have no preview.

Load flow robustness
- Ready-handshake in panel.ts: buffer sends in `pendingMessages` until the
  webview posts `{type:'ready'}`, then flush. Fixes 'Waiting for scan…'
  that was caused by postMessage racing the webview's module load.
- Check fs.existsSync before bundleHookSite so a deleted-fixture path
  surfaces a clean in-webview error instead of a rolldown UNRESOLVED_ENTRY
  toast.
- Assign `window.__copilotkit_hookSite = undefined` between bundle loads
  (rolldown's `var` declaration is non-configurable so `delete` throws).
- Gate Harness mount on a real hostRoot: render a 'Loading bundle…'
  placeholder until the bundle finishes, otherwise the placeholder HostRoot
  publishes an empty registry past the gate.
- Strip function values (onRespond / onResolve) before postMessage —
  structured-clone can't serialize callbacks.
- Reset controls/respondValue/resolveValue in the load handler so a cross-
  kind switch can't leave the previous hook's shape under a new kind's
  control component.
- Top-level PreviewErrorBoundary with a resetKey-driven
  componentDidUpdate recovery: any crash from the render prop, controls,
  or descendants shows a readable message and auto-clears when the user
  picks a different hook. Boundary does not remount descendants, so
  AppInner's message listener stays attached and incoming 'load's aren't
  lost on reset.
- FormRenderer filters undefined fields defensively and falls back to
  an empty values object on first render tick.

Capture-only CopilotKit stubs
- Externalize @copilotkit/react-core and /v2, resolve at runtime to a
  Proxy-backed stubs object that captures every hook call into
  window.__copilotkit_captured and renders providers as Fragment
  pass-throughs. Avoids dragging the chat/runtime-client/markdown graph
  through rolldown IIFE (which was the __commonJSMin TDZ root cause).
- RegistryReader fans hook captures into the right slot (renderToolCalls,
  tools, coAgentStateRenders) and keeps a `byHook` bucket so findConfig
  can look up anonymous/nameless hook variants.

Hook registry coverage
- useComponent and useDefaultTool retagged from category:'data' to
  category:'render' with correct renderProps. Both have render props in
  real react-core; they're just thin wrappers over useFrontendTool and
  useCopilotAction respectively.
- useCopilotAuthenticatedAction_c, useHumanInTheLoop, useDefaultRenderTool,
  useComponent, useDefaultTool added to RENDER_TOOL_CALL_HOOKS in the
  registry-reader so their captures route correctly.

Weather-themed fixtures (14 hook instances covering every V1 + V2 render hook)
- useCopilotAction: addLocation + removeLocation (WeatherActions), severeAlert
  with imported CSS (SevereWeatherAlert + severe-alert.css)
- useCopilotAuthenticatedAction_c: publishAlert (AdminIssueAlert)
- useCoAgentStateRender: forecast_agent (ForecastAgent)
- useLangGraphInterrupt: LocationPermissionInterrupt
- useRenderTool: getWeather (WeatherTool)
- useRenderToolCall: viewRadar (WeatherRadar)
- useDefaultRenderTool: defaultWeatherFallback (DefaultWeatherRender)
- useLazyToolRenderer: historicalTemperatures (LazyHistoricalChart)
- useRenderCustomMessages: WeatherCustomMessage
- useRenderActivityMessage: WeatherActivityMessage
- useHumanInTheLoop: confirmEvacuation (ConfirmEvacuation)
- useInterrupt: UnitsPreferenceInterrupt
- useFrontendTool: precipitationGauge (PrecipGaugeFrontendTool)
- useComponent: sunTimes (WeatherComponent)
- useDefaultTool: DefaultWeatherCatchAll

Each fixture uses a distinct visual (gradient cards, severity-palette
alerts, forecast strip, radar grid, conic-gradient gauge, permission
dialogs) so the preview exercises a broad range of layouts.

Tests
- Integration test enumerates all 17 expected hook sites and bundles every
  fixture file; capture test uses the new synthetic interrupt name.
2026-04-20 15:24:12 +02:00
Alem Tuzlak 540013e00b fix(vscode-extension): gate preview render on controls being seeded
ControlsDispatch (and its adapters — ActionControls, etc.) read fields
off `values` like `values.status`. `controls` is seeded via useEffect
after `config` lands, so the first render post-capture had
`controls === null` → 'Cannot read properties of null (reading status)'.

Add a short 'Preparing controls…' placeholder between the config arrival
and the next render tick when the seeding effect has run.
2026-04-20 12:57:55 +02:00
Alem Tuzlak 1ada5829e6 fix(vscode-extension): don't mount Harness until bundle sets the real HostRoot
Reload race: after a 'load' message App called setHostRoot(null) + setPayload(new).
The first render showed Harness with a placeholder HostRoot (() => null).
RegistryReader's useEffect fired against an empty capture array, published
an empty registry, and the !hostRoot || !registry gate let the next render
past it before the real component ever ran — so the user saw 'was not
captured' even when the bundle was valid.

Split the gate: render a 'Loading bundle…' placeholder while hostRoot is
null, mount Harness only once it's set, then wait on registry. The real
component now always gets at least one render pass, populating
window.__copilotkit_captured before RegistryReader reads it.

Also remove the temporary diagnostic console.logs and drop the stray
excalidraw.log I accidentally staged earlier.
2026-04-20 12:55:25 +02:00
Alem Tuzlak 7a7f19f61c debug(vscode-extension): log stub capture + registry read + findConfig match
Temporary diagnostics to narrow down why the capture registry appears
empty in the real webview even though jsdom E2E tests (stub + Harness +
React render) pass. Will be removed once the flow is confirmed.
2026-04-20 12:50:27 +02:00
Alem Tuzlak 740db6585c fix(vscode-extension): assign undefined instead of deleting hookSite global
Rolldown's IIFE emits `var __copilotkit_hookSite = …` at the top level,
which binds a non-configurable property on `window`. The reload-cleanup
path tried to `delete` it, which throws in strict mode. Assigning
`undefined` has the same semantics for our read-below check (which
already treats `undefined` as "not set") without the throw.
2026-04-20 12:40:38 +02:00
Alem Tuzlak 1fece3ddbf fix(vscode-extension): replace real CopilotKit harness with capture-only stub
The previous harness imported `@copilotkit/react-core` and mounted a real
`<CopilotKit>` provider inside the webview. That dragged the full
chat/runtime-client/markdown dep graph through rolldown's IIFE output,
which wraps CJS modules in `__commonJSMin`. Under the circular-import
graph this triangle produces, the wrappers hit a TDZ at runtime:
  Cannot access 'require_clipboard' before initialization
  Cannot access 'require_graphql' before initialization
  Cannot access 'require_context_helpers' before initialization
(and every other chunk whose factory assignment lost its race with its
first caller). Every targeted fix only moved the failure to the next
chunk.

Pivot: the preview never needed a live CopilotKit runtime — we only ever
wanted the `render` prop the user passed to their hook, driven by a
form over its declared parameters. So:

- Externalize `@copilotkit/react-core` and `@copilotkit/react-core/v2`
  in the hook bundler; map both globals to
  `__copilotkit_deps.copilotkitStubs`.
- Add `copilotkit-stubs.ts`: a Proxy-backed object that captures every
  hook call into `window.__copilotkit_captured`, renders CopilotKit/
  CopilotKitProvider as Fragment pass-throughs, and returns null
  components / noop functions for anything else — so the bundle never
  fails on unknown exports.
- Harness drops the `<CopilotKit>` wrapper and the fetch interceptor;
  RegistryReader reads the capture array in a useEffect and shapes it
  into the existing `CapturedRegistry` layout.
- Tests rewritten against the stub path (no real react-core import).

Result: TodoActions.tsx bundle is 1.3KB with zero `__commonJSMin`
wrappers, down from 24MB with 1000+ `require_" identifiers. The
tradeoff is explicit in the stub file: hook-level state that needs a
real runtime (e.g. useCopilotChat returning live messages) isn't
available here — add a specific stub return value if a preview needs it
rather than leaning on the Proxy fallback.
2026-04-20 12:37:53 +02:00
Alem Tuzlak 30d7d81b1a fix(vscode-extension): alias workspace CopilotKit pkgs to TS source in runtime bundler
The runtime IIFE bundler (used when previewing a user's hook site) had no
workspace-source aliasing — so @copilotkit/react-core → @copilotkit/shared
and → @copilotkit/runtime-client-gql both landed on the CJS dist, which
rolldown wraps in __commonJSMin. Under the circular-import graph that flows
through the chat/runtime-client/shared triangle that wrapping trips a TDZ:
  Cannot access 'require_clipboard' before initialization
  Cannot access 'require_graphql' before initialization

Mirrors the workspaceSourceAliases fix already applied in tsdown.config.ts
for the extension's own webview bundles: pre-compute a spec → src/index.ts
map by resolving each workspace package's package.json via extensionRequire,
then short-circuit resolveId before Node fallback. Only applied when the
src file exists on disk (monorepo checkout); for marketplace installs where
users pull these packages from npm the CJS dist is the only thing on disk,
so the alias silently falls through — that production path is a separate
concern tracked outside this change.
2026-04-20 12:02:49 +02:00