Commit Graph

6618 Commits

Author SHA1 Message Date
Jordan Ritter 6faf3a7bb7 fix(showcase/shell-docs): middleware uses per-visitor distinct_id via first-party cookie
The previous hard-coded distinct_id ("docs-pageview-tracker") collapsed
every visitor into a single synthetic PostHog person, which destroyed
unique-visitor analytics.

Each visitor now gets a stable UUID minted on first visit, persisted
in a first-party `ph_distinct_id` cookie (Lax, Secure, ~2 year TTL)
and read back on subsequent requests. This avoids IP+UA hashing and
the PII concerns that come with it.

Also wraps the PostHog capture POST in `event.waitUntil()` — the
middleware signature now takes `NextFetchEvent` — so the Edge runtime
keeps the request alive until the POST resolves. A bare fire-and-forget
fetch can be torn down as soon as `NextResponse.next()` returns,
dropping events.
2026-04-20 16:36:40 -07:00
Jordan Ritter 7c09e25ca3 ci(showcase): allowlist shell-docs/src/data/* in check-binaries 2026-04-20 14:28:54 -07:00
Jordan Ritter 9c4503ec3b chore(showcase/shell-dojo): add .gitignore and lockfile for parity with sibling shell packages 2026-04-20 14:22:58 -07:00
Jordan Ritter 8d56c2230b chore: apply formatter to integration branch
- Extend .oxfmtrc.json ignore to cover shell-docs/src/content
  (mirrors existing shell/src/content ignore, which was not updated
  when shell-ops-v2 moved MDX content into the new shell-docs package)
- Format showcase/scripts/generate-registry.ts and probe-docs.ts
2026-04-20 14:13:30 -07:00
Jordan Ritter a6836556d7 chore(showcase/shell-docs): add package-lock.json
Matches shell/ and shell-dashboard/ which commit their npm lockfiles to
pin standalone (non-pnpm-workspace) install closures. Ensures reproducible
Docker builds in CI.
2026-04-20 14:05:11 -07:00
Jordan Ritter b6568518ee fix(showcase/shell-docs): drop @copilotkitnext/react CSS import
shell-docs renders pure MDX docs with iframed live demos (via <InlineDemo>
and <IframeSwitcher>) — it never instantiates the live copilot runtime
inline, so it doesn't need @copilotkitnext/react or its styles. Dropping
the import also removes the package from shell-docs's dep closure, keeping
the docs shell lean.
2026-04-20 14:04:51 -07:00
Jordan Ritter c320457180 chore(showcase/shell-docs): add .gitignore matching shell-dashboard pattern 2026-04-20 14:02:45 -07:00
Jordan Ritter c298fe2ae2 fix(showcase/shell-docs): move 4085-added unselected/prebuilt-components content into shell-docs
These files were added in #4085 but landed in showcase/shell/src/content/docs/
after the MDX-docs extraction had already moved the rest of content/docs into
shell-docs. Follow The Rule (MDX docs content belongs in shell-docs) and
relocate them so they render correctly on docs.showcase.copilotkit.ai.
2026-04-20 14:00:57 -07:00
Jordan Ritter a0daa21166 ci(showcase): add shell-docs to ALL_SERVICES; provision Railway service showcase-shell-docs
Adds the shell-docs package to the deploy matrix, paths-filter, and
dispatch options. Railway service `showcase-shell-docs` (ID
7badfb8d-4228-414c-9145-b4026803714f) has been provisioned with the
custom domain `docs.showcase.copilotkit.ai` and the Railway-generated
CNAME target `t6ge0qwv.up.railway.app`.

Env vars seeded on the Railway service: OPENAI_API_KEY,
ANTHROPIC_API_KEY, GOOGLE_API_KEY, POSTHOG_PROJECT_KEY, PORT=10000,
NEXT_PUBLIC_BASE_URL=https://docs.showcase.copilotkit.ai,
NEXT_PUBLIC_SHELL_URL=https://showcase.copilotkit.ai.

Matrix entry uses lfs:true + context:"." + explicit Dockerfile path
(mirrors shell) because the image pulls in shared/ scripts/ packages/
at build time. build_args_sha/branch are included so the build surface
embeds commit metadata, matching shell's pattern.
2026-04-20 14:00:28 -07:00
Jordan Ritter debfa6600d refactor(showcase): update generator scripts to emit into both shell and shell-docs where applicable
MDX docs moved from shell to shell-docs, but several generated artifacts
are still consumed by both shells:
- registry.json: shell uses it (home grid, integrations, matrix,
  middleware, layout); shell-docs uses it (docs renderer framework lookup)
- demo-content.json: shell uses it (integrations/[slug]/[demo]); shell-docs
  uses it (<Snippet> in docs renderer)
- search-index.json: shell-docs consumes it for the docs search modal;
  shell also keeps a copy so its header search still works — links 301
  across to docs.showcase.copilotkit.ai.

Updated scripts:
- generate-registry.ts: dual-emits registry.json to both shells
  (constraints.json stays shell-only — integration-explorer is shell)
- bundle-demo-content.ts: dual-emits demo-content.json
- generate-search-index.ts: scans from shell-docs/src/content (where MDX
  now lives), writes to both shells' data dirs
- probe-docs.ts: scans shell-docs/src/content/docs (content source moved),
  still writes docs-status.json under shell/ for the dashboard
- sync-docs-from-main.ts: target path updated to shell-docs/src/content

Tests in __tests__/ reference shell/src/data paths; dual-emit keeps
those stable so existing afterEach-restore hooks continue to work.
2026-04-20 14:00:28 -07:00
Jordan Ritter c6e4b809e9 feat(showcase/shell): add 301 redirects from /docs /ag-ui /reference /[framework] to docs.showcase.copilotkit.ai
Shell no longer hosts MDX docs routes — they live on shell-docs
(docs.showcase.copilotkit.ai). Add permanent redirects from the legacy
shell paths so old URLs and SEO authority carry over to the new host.

Framework slugs are enumerated from registry.json at build time (not a
:slug* wildcard) so /integrations and /matrix — both still owned by
shell — are NOT caught by the redirect. Fixed routes (/docs, /ag-ui,
/reference) cover the three other docs catch-alls.
2026-04-20 14:00:28 -07:00
Jordan Ritter fc80501048 feat(showcase): move MDX docs routes + components + libs + content into shell-docs
Extracts everything that exists to render MDX documentation (docs/[[...slug]],
[framework]/[[...slug]], ag-ui/[[...slug]], reference/[...slug]) out of shell
into the new shell-docs package that will serve docs.showcase.copilotkit.ai.

Moves (git mv preserves history):
  - App routes: /docs, /[framework], /ag-ui, /reference
  - Docs-only components: docs-page-view, docs-callout, docs-steps, docs-tabs,
    mdx-components, framework-tabs, framework-selector, sidebar-*, snippet,
    property-reference, router-pivot, stored-framework-highlight, react/*
  - Docs-only libs: lib/docs-render, lib/mdx-registry
  - All content: content/docs, content/ag-ui, content/reference, content/snippets
  - .docs-sync-sha marker (follows the content)

Duplicates into shell-docs (both shells need them):
  - brand-nav, search-modal, search-trigger, copy-button, framework-provider
  - lib/registry.ts, data/registry.json, data/demo-content.json,
    data/search-index.json
  - app/layout.tsx + globals.css + public/{images,logos}

shell-docs gets its own minimal middleware (PostHog-only — no SEO redirect
table, docs host never served legacy URLs). shell keeps seo-redirects.ts
for the legacy-URL migration table; framework-scope protection in its
middleware is now effectively dead but harmless (next.config.ts redirects
fire before middleware ever sees /<framework>/ paths).

InlineDemo updated for cross-host context: 'Open full demo' link points
at the shell host (showcase.copilotkit.ai) since the integration profile
route only exists there.
2026-04-20 14:00:28 -07:00
Jordan Ritter dc73b9f334 chore(showcase): scaffold shell-docs package 2026-04-20 13:59:48 -07:00
Jordan Ritter 878ac3af2d chore(showcase): rename shell-dojolike → shell-dojo for naming consistency with dojo.showcase.copilotkit.ai
- Renamed showcase/shell-dojolike/ → showcase/shell-dojo/ (git mv for history).
- Updated package name to @copilotkit/showcase-shell-dojo.
- Updated .github/workflows/showcase_deploy.yml: dispatch option, paths-filter key, ALL_SERVICES entry (context, image, comment).
- Updated .github/workflows/static_check-binaries.yml size allowlist path.
- Renamed Railway service showcase-shell-dojolike → showcase-shell-dojo (id 7ad1ece7-2228-49cd-8a78-bddf30322907 preserved; domain dojo.showcase.copilotkit.ai unchanged).
- Updated image source to ghcr.io/copilotkit/showcase-shell-dojo:latest.
2026-04-20 13:54:42 -07:00
Jordan Ritter daf09358c4 chore(showcase): rename shell-internal → shell-dashboard and provision Railway service @ dashboard.showcase.copilotkit.ai
- Renamed showcase/shell-internal/ → showcase/shell-dashboard/ (git mv for history).
- Updated package name to @copilotkit/showcase-shell-dashboard (+ lockfile).
- Updated external references: showcase/scripts/{probe-docs,generate-status}.ts comments and showcase/README.md.
- Added dispatch + filter + ALL_SERVICES entry for shell-dashboard in .github/workflows/showcase_deploy.yml.
- Provisioned Railway service showcase-shell-dashboard (id 4d5dfd74-be61-40b2-8564-b53b7dd4c15b):
  - image source ghcr.io/copilotkit/showcase-shell-dashboard:latest
  - env vars OPENAI_API_KEY, ANTHROPIC_API_KEY, PORT copied from showcase-shell
  - custom domain dashboard.showcase.copilotkit.ai attached (CNAME → 6u9icvje.up.railway.app)
  - image will be built and pushed via showcase_deploy.yml on merge
2026-04-20 13:54:17 -07:00
Jordan Ritter 82e9769f5c fix(showcase/starters/langroid): restore /api/health 200 by dropping sed log-prefix wrappers and gating Next.js on agent readiness
Production showcase-starter-langroid returns ``{"status":"degraded","agent":"down"}`` HTTP 503
at ``/api/health`` — the exact path the showcase-deploy workflow's ``ALL_SERVICES.health_path``
verify step asserts, so the next CI redeploy of this starter would fail verification.

Root cause: two compounding bugs in ``showcase/starters/langroid/entrypoint.sh``.

1. Process-substitution log prefixers suppressed stdout. Both backgrounded
   services were wrapped with ``> >(sed 's/^/[agent] /') 2>&1 &`` /
   ``> >(sed 's/^/[nextjs] /') 2>&1 &``. In Railway's V2 runtime this shape
   reliably produced ZERO ``[agent]``/``[nextjs]`` log lines (confirmed against
   several weeks of Railway logs for this service) and correlated with
   agent-unreachable 503s at ``/api/health``. The package entrypoint
   (``showcase/packages/langroid/entrypoint.sh``) uses the plain-``&`` pattern
   with no wrapper, stays green on the same Railway runtime, and shows full
   uvicorn ``INFO:`` startup logs including ``127.0.0.1:...  "GET /health
   HTTP/1.1" 200 OK`` from Next.js's fetch. Match the working package pattern.

2. Cold-start race amplified by ``sleepApplication=true``. Railway sleeps
   idle services. On wake, Next.js is ready in <1s but Python + langroid
   imports take 10-20s. Without a readiness gate, Next.js answered the first
   post-wake ``/api/health`` probe with ``agent:"down"`` before uvicorn had
   bound port 8123. The deploy workflow's verify step then saw the 503 and
   reported the deploy as unhealthy. Add a 30s readiness probe that curls
   ``http://127.0.0.1:8123/health`` in a loop before starting Next.js. The
   IPv4 literal (``127.0.0.1`` not ``localhost``) is load-bearing — Node
   22+'s fetch resolves ``localhost`` to IPv6 ``::1`` first, and uvicorn
   binds IPv4 only; the readiness probe must not false-negative on resolver
   semantics alone.

Also export ``PYTHONUNBUFFERED=1`` so Python import-time tracebacks (e.g.
langroid module-load failures) reach the container log immediately instead
of sitting in userspace buffers until process exit closes them off.

Verification:
- Built langroid starter image locally from this branch. ``/api/health``
  returns 200 with ``{"status":"ok","agent":"ok"}``. Full uvicorn INFO
  logs visible including ``127.0.0.1:xxxxx - "GET /health HTTP/1.1"
  200 OK`` confirming Next.js -> agent path works.
- Readiness probe fires before Next.js launch: ``[entrypoint] Agent
  /health ready after 3s``.
- New regression guards in ``starter-consistency.test.ts`` cover all four
  invariants (no sed wrapping around uvicorn, no sed wrapping around next
  start, ``PYTHONUNBUFFERED=1`` export, readiness probe on 127.0.0.1:8123
  before ``next start``). Verified red→green (reverted entrypoint -> 4
  fails; restored -> 4 pass). Full showcase/scripts suite: 1079/1079 pass.
2026-04-20 13:53:08 -07:00
Jordan Ritter afc9d0fbea Merge #4085 into integration branch (feature port, no-docker variant) 2026-04-20 13:52:57 -07:00
Martha Schumann 857c7bd106 docs(mastra): use requestContext in Agent instructions example
Restores the API-surface change from the original QA PR #3866 (merged
via mirror #4040) that was inadvertently reverted in 804c5d0f. Keeps
Jordan's other fixes (imports, model, null guards, grammar) intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:40:29 -07:00
github-actions[bot] 146fa4e6a5 style: auto-fix formatting 2026-04-20 17:15:35 +00:00
Alem Tuzlak ecf79393e4 ci(vscode-extension): least-privilege perms, concurrency guard, drop dead lint step
- Add top-level 'permissions: contents: read' — the workflow performs
  no repo writes, so declaring the minimum explicitly closes a
  default-token hardening gap.
- Serialize publishes with concurrency: group: vscode-extension-publish,
  cancel-in-progress: false. Two rapid pushes to main used to race
  straight into vsce publish; the second failed noisily on duplicate
  version or, worse, published out of order.
- Remove the 'Lint' step. The Nx target 'copilotkit-vscode-extension:lint'
  doesn't exist and there's no 'lint' npm script either, so the step
  always failed — 'continue-on-error: true' just hid it while never
  actually linting anything. Re-add once a real lint target lands.

check-binaries.sh:
- Replace '[ ] && exit 1 / exit 0' with an explicit 'if [ ]; then
  exit 1; fi; exit 0' block. Under 'set -e' the old form was safe
  only because the trailing 'exit 0' existed; the explicit form is
  robust regardless of what follows.
2026-04-20 19:13:50 +02: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 8f3c47e72a docs(vscode-extension): restructure as three-feature hub + refresh Hook Explorer page
Persona fix: the landing page at /vs-code-extension only documented the
A2UI catalog preview, so builders using CopilotKit hooks and runtime
debuggers never discovered their respective features. Restructured
into a three-section hub that surfaces Hook Explorer, A2UI Catalog,
and AG-UI Inspector side-by-side, with install steps at the top and
cross-links to the deep-dive pages.

/troubleshooting/hook-explorer refreshed end-to-end:
- Replaced stale TreeDataProvider description with the current webview
  sidebar + row-click-previews + '</>' go-to-source UX.
- Documented the inline '▶️ Preview Component' CodeLens.
- Expanded hook coverage table to include useComponent, useDefaultTool,
  useHumanInTheLoop, useInterrupt, useRenderCustomMessages,
  useRenderActivityMessage, useDefaultRenderTool, useLazyToolRenderer,
  useCopilotAuthenticatedAction_c, useRenderToolCall — all 15 render
  hooks covered with their preview shape.
- Added 'Behavior notes' section: auto-recovery on crash + reset on
  selection change, controls persistence, Tailwind + local CSS flow-
  through.
- Added imported-render support note.

/troubleshooting/event-inspector — added a back-link to Hook Explorer
at the bottom callout so the cross-link is bidirectional.
2026-04-20 18:45:36 +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
Alem Tuzlak 055ea7ffc4 fix(vscode-extension): install browser require shim before executing IIFE
Bundled CJS libs (lucide-react, some radix internals) keep verbatim
require("react") calls inside __commonJSMin wrappers. Rolldown's
globals config rewrites ESM-style external refs but leaves those
require() calls alone, and the browser has no global require — so
the IIFE threw 'require is not defined' at first execution.

Install a window.require shim before appending the script that maps
the React externals to the already-imported modules and warns + returns
{} for unknown specifiers, so a stray transitive require doesn't take
the whole preview down.
2026-04-20 11:58:42 +02:00
Alem Tuzlak 80a43776c1 fix(vscode-extension): reorder isBuiltin check before skipPrefixes + stub markdown chain in iife-bundler
Two-part fix that together make 'node_path is not defined' go away.

Problem 1: hook-bundler passed skipSpecifierPrefixes: ["node:", "vscode"].
The iife-bundler resolver's skipPrefixes check ran BEFORE the isBuiltin
stub, so "node:path" short-circuited to null. Rolldown then externalized
node:path as an IIFE parameter — the bundle opened with
  (function(exports, react, ..., node_path, node_process, node_url) { ... })
and the webview call site only passed exports+react globals, leaving
node_path undefined. ReferenceError at first access.

Fix A: move the isBuiltin stub BEFORE the skipPrefixes check so both
"path" and "node:path" resolve to the virtual empty/shim module. Clean
up hook-bundler's skipSpecifierPrefixes to just ["vscode"] since "node:"
is no longer needed as a skip hint.

Problem 2: with Node builtins stubbed, rolldown's package-condition
resolver picks the BROWSER condition for subpath exports like
vfile's '#minurl'. The browser variant only exports { isUrl }, but
vfile/lib/index.js imports { urlToPath, isUrl }. MISSING_EXPORT errors
block the build — previously hidden because externalizing Node builtins
kept a node-ish resolution mode.

Fix B: stub the unreachable markdown chain (vfile, stringify-entities,
parse-entities, character-entities*, decode-named-character-reference,
hast-util-to-html) with a Record<string, string[]> that enumerates each
dep's named exports so rolldown's named-export analysis succeeds. Same
pattern as tsdown.config.ts's HOOK_PREVIEW_STUBBED_DEPS for the static
webview bundle.

Adds a regression test that bundles TodoActions.tsx and asserts no
`node_<builtin>` identifier is referenced without a declaration.
2026-04-20 11:49:25 +02:00
Alem Tuzlak 8b2b71447f fix(vscode-extension): stub Node builtins in IIFE bundler (not external) — fixes 'node_path is not defined'
Prior behaviour marked transitive Node builtins ("path", "fs", etc.) as
external during per-hook source bundling. In IIFE format without a globals
map entry, rolldown emits:

  var node_path = node_path;

…which throws 'node_path is not defined' the moment the IIFE initialises
inside the hook-preview webview. The user sees it as a Mount error.

Route builtins to virtual stub modules instead (same pattern as the
tsdown.config.ts stub for the static webview bundle):
  - crypto → WebCrypto-backed shim (randomFillSync/randomBytes/randomUUID)
  - anything else → empty module (export default {})

Browser-reachable code paths either never call the missing API or hit the
crypto shim and work. Code that does try to call e.g. path.join now fails
at the call site with a clearer error rather than an opaque ReferenceError
at module init.
2026-04-20 11:31:26 +02:00
Alem Tuzlak 9749fdefc3 fix(vscode-extension): WebCrypto-backed crypto shim so uuid works in the hook-preview webview
Prior stub resolved 'crypto' to an empty module; that broke the uuid npm
package's v4 path (used by react-core's ThreadsProvider on first render):

  Uncaught TypeError: (0, crypto_1.randomFillSync) is not a function
    at rng → v4 → randomUUID → ThreadsProvider

Replace the empty stub with a tiny shim that forwards the three APIs we
actually see called at module-init / first-render time (randomFillSync,
randomBytes, randomUUID) to globalThis.crypto. The webview always has
WebCrypto available. Other crypto APIs (hashes, ciphers, key derivation)
still default to the empty stub since none of the transitive deps we
bundle hit them; extend the shim if that changes.
2026-04-20 11:25:34 +02:00
Alem Tuzlak b6fe997da2 fix(vscode-extension): resolve @copilotkit/runtime-client-gql to TS source to dodge graphql TDZ
Second __commonJSMin TDZ hit:
  Uncaught ReferenceError: Cannot access 'require_graphql' before initialization

Same shape as the clipboard fix in d551a2e77. runtime-client-gql's CJS
dist wraps the `graphql` npm lib (which is itself CJS) — with circular
imports through react-core's runtime client, the outer `require_graphql =
__commonJSMin(...)` declaration is accessed before its initializer runs.

TS source uses direct ESM imports that rolldown orders cleanly, so the
forward reference TDZ window doesn't open.
2026-04-20 11:22:14 +02:00
Alem Tuzlak 4c55f871a6 refactor(vscode-extension): drop copy-identity hover button
The button copied a made-up composite key (hook::name) that wasn't useful
for any realistic workflow — bug reports, codebase search, and Slack shares
all work better with the file path + line number already shown on the row.
Dropping it trims the hover area to just the ▷ preview button on render
hooks. Removes the copyIdentity case from the bridge type, view-provider
handler, and callback map. The copilotkit.hooks.copyIdentity palette
command stays registered (the helper still exists) — zero UI surface, so
it doesn't contribute to the clutter the user flagged.
2026-04-20 11:22:13 +02:00
Alem Tuzlak af42a841ee fix(vscode-extension): row click opens source; preview moves behind ▷ button
Default interaction on a hook row was surprising — clicking a render hook
spawned the preview panel unprompted, while clicking a data hook opened
the source. Align both categories on the same default (open source on
click) and keep the preview action behind the dedicated ▷ button that
appears on hover for render hooks. Also drops the redundant hover
'open source' button since the row click already does that — removes
visual clutter the user flagged in the prior feedback.
2026-04-20 11:12:24 +02:00
Alem Tuzlak 3a2784353d refactor(vscode-extension): drop HookTreeDataProvider; webview now owns the hook list
The new HookListViewProvider replaces the TreeDataProvider entirely,
and the old view-provider.ts had no remaining imports after activate-
hook-explorer was migrated. Its tests covered buildTreeData /
findLeaf / statusKeyForSite, all of which now live in tree-model.test.ts
alongside the new groupSitesByHook coverage.
2026-04-20 11:10:01 +02:00
Alem Tuzlak 49c53d9942 feat(vscode-extension): webview-based Hook Explorer sidebar
Replaces the native TreeView for copilotkit.hooks with a React webview
that mirrors the AG-UI Inspector sidebar. Registered hooks are shown
prominently as collapsible groups; HOOK_REGISTRY entries with zero call
sites are tucked behind a "Show available hooks" toggle so the main
view stays quiet. Uses VS Code CSS variables and Tailwind-via-CDN.

- HookListViewProvider implements vscode.WebviewViewProvider with a
  typed bridge contract in hook-list-bridge-types.ts, replays sites on
  the webview's `ready` signal, and forwards preview/openSource/
  copyIdentity/refresh back to injected callbacks.
- New pure groupSitesByHook helper (in tree-model.ts) splits registry
  entries into registered / available, covered by tree-model.test.ts.
- activate-hook-explorer keeps the five copilotkit.hooks.* commands for
  the command palette; each now accepts an optional HookCallSite and
  falls back to a QuickPick picker when invoked without args.
- tsdown.config.ts emits dist/webview/hook-list.js (no stubbing needed:
  the bundle doesn't pull in @copilotkit/react-core).
2026-04-20 11:09:48 +02:00