Commit Graph

281 Commits

Author SHA1 Message Date
Alem Tuzlak 6f77116aee feat(runtime): pass through REASONING_* chunks in TanStack converter 2026-04-27 13:47:02 +02:00
Alem Tuzlak d07c1d109e test(runtime): assert TanStack converter does not emit STATE_* for non-state tools 2026-04-27 13:42:24 +02:00
Alem Tuzlak a0be5455ab feat(runtime): emit STATE_DELTA on AGUISendStateDelta in TanStack converter 2026-04-27 13:40:38 +02:00
Alem Tuzlak 5f233a1987 feat(runtime): emit STATE_SNAPSHOT on AGUISendStateSnapshot in TanStack converter 2026-04-27 13:37:36 +02:00
Alem Tuzlak 71005252b8 test(runtime): add TanStack tool-result + reasoning chunk helpers 2026-04-27 13:17:09 +02:00
Mike Ryan ae978b95b1 test(runtime): align intelligence telemetry mock platform 2026-04-24 11:22:49 -07:00
github-actions[bot] 5bd77b35f0 style: auto-fix formatting 2026-04-24 11:22:49 -07:00
Mike Ryan ad7a383095 fix(runtime): tighten realtime startup handling 2026-04-24 11:22:49 -07:00
Mike Ryan 17bd39cd26 fix(runtime): finalize realtime connection responses 2026-04-24 11:22:49 -07:00
Mike Ryan 05aa15f908 fix(runtime): align intelligence realtime contracts 2026-04-24 11:22:49 -07:00
Benjamin Taylor b8e7b575c3 refactor(runtime): drop framework tag from v2 telemetry
The framework label was only meaningful for Segment's per-event
properties. Scarf receives only the event name, so the label has no
destination. Removes the `framework` option on createCopilotRuntimeHandler
and its pass-through from the hono/express adapters, plus the now-unused
setGlobalProperties/globalProperties/flattenObject on the telemetry
client.

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

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

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

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

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

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 13:04:13 -07:00
Martha Schumann 990c097268 fix(inspector): address post-review bugs and test gaps
- ThreadStoreRegistry.register: delete old store before notifyUnregistered
  so callbacks that call getThreadStore(agentId) see undefined, not the
  new store
- ThreadDetailsComponent: reset _expandedMessages on threadId change
  alongside _expandedToolCalls (prevents stale expanded state across
  thread switches)
- handle-threads.test: assert identifyUser called in getThreadMessages
  intelligence path; add identifyUser-throws 500 test
- use-threads.test: add fetchMoreThreads end-to-end test (calls the
  function, asserts cursor param on second fetch, asserts 3 threads)
- in-memory-runner.test: call clearThreads() in first describe's
  beforeEach for GLOBAL_STORE consistency

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 12:53:07 -07:00
Martha Schumann 96e8112039 Merge remote-tracking branch 'origin/main' into claude/langgraph-tests-cleanup-1jVZ3 2026-04-22 09:48:55 -07:00
Mike Ryan 79e1bebece fix(runtime): require identifyUser name in intelligence mode 2026-04-22 09:55:59 -05:00
Mike Ryan 4369252a1e chore(format): run prettier on runtime test 2026-04-22 07:24:51 -07:00
Mike Ryan 85ea1969e5 feat(runtime): remove intelligence organization id config 2026-04-22 07:24:51 -07:00
Alem Tuzlak 90941ed08e Merge branch 'main' into fix/issue-2118 2026-04-22 14:43:39 +02:00
Alem Tuzlak a337b5f63d Merge branch 'main' into fix/issue-2118 2026-04-22 14:29:11 +02:00
Alem Tuzlak ec905205f9 Merge branch 'main' into fix/issue-3637 2026-04-22 14:27:14 +02:00
Alem Tuzlak 2b88450b44 fix: prevent 'Unknown provider' regression for LangChainAdapter (#3840)
Closes #3217

When `getLanguageModel()` returns null and provider/model are undefined
(as with LangChainAdapter), the code constructed `"undefined/undefined"`
as a model string, causing a cryptic "Unknown provider" error.

Now checks each source of model info explicitly and throws a clear error
message directing users to provide an explicit agents config when using
adapters that don't expose model metadata.

Split from #3838.
2026-04-22 14:23:49 +02:00
Alem Tuzlak 24b5e80bcc Merge remote-tracking branch 'origin/main' into fix/issue-2118 2026-04-22 12:59:14 +02:00
Alem Tuzlak f9d01dfd3b refactor(runtime): extract resolveMessageId helper and cover it with tests
Addresses PR feedback on #3814: replaces the "red-green tested locally"
rationale with actual unit coverage.

The inline `event.messageId || randomId()` fallback becomes an exported
`resolveMessageId` helper. The resolver now calls it; the helper is
covered by a small test suite asserting: provided ids pass through
verbatim; null / undefined / empty-string event ids fall back to the
"ck-<uuid>" shape; successive fallbacks produce distinct ids.
2026-04-22 12:59:06 +02:00
Alem Tuzlak db38f50918 test(runtime): cover handleServiceAdapter model-source resolution (#3217)
Covers the three branches added by the fix:
- getLanguageModel() returns a LanguageModel -> wired into BuiltInAgent
- provider + model strings present -> composed as "provider/model"
- neither present (e.g. LangChainAdapter) -> throws CopilotKitMisuseError
  with adapter name in the message, instead of silently producing
  "undefined/undefined" and failing downstream.

Also guards the partial-info case where only one of provider/model is set
to ensure the regression doesn't re-emerge via a half-populated adapter.
2026-04-22 12:39:31 +02:00
github-actions[bot] 15c6e027b9 style: auto-fix formatting 2026-04-22 09:58:37 +00:00
Alem Tuzlak d7c7b52620 test(runtime): cover Retry-After parsing and 429 fetch path (#3637)
Adds parseRetryAfter unit tests (header absent, integer seconds, zero,
negative, future/past HTTP-date, unparseable) and fetchWithRetry 429
branch tests (honored within limit, rejected when exceeding
maxRetryAfterSeconds, fallback to exponential backoff when missing,
ignored on non-429 retryable statuses).
2026-04-22 11:56:53 +02:00
Jordan Ritter 4b4923561b feat: VS Code extension — Hook Explorer, AG-UI Inspector, webview-first sidebars (#3935)
## Summary

Three connected features land together so the CopilotKit VS Code
extension becomes a coherent debugger/preview surface:

1. **Hook Explorer** — every V1 + V2 render hook can be discovered and
previewed live with auto-generated controls, an inline `▶️ Preview
Component` CodeLens, and a sidebar that lists every captured site.
2. **AG-UI Event Inspector** — live SSE debug stream of all AG-UI
events, filterable and color-coded, in a sidebar view + editor panel.
3. **A2UI Catalog sidebar → webview** — the last native TreeView gets
replaced with a Tailwind-styled webview that matches the other two, now
with a proper **Go to source** action on components and fixtures.

## Hook Explorer

### Discovery + preview
- oxc-based scanner walks the workspace and finds every call-site of any
hook in the registry (17 across V1 + V2, render + data).
- Preview panel bundles the user's source via Rolldown (IIFE format,
React externalized, CSS collected per `@copilotkit/a2ui-renderer`
pattern), executes it in the webview with a capture-only **stub** for
`@copilotkit/react-core` (+ `/v2`), and mounts the user's component just
long enough to record each hook's config.
- Auto-generated form on the left/top drives the `render` prop's
args/parameters/state/event live. V1 parameter arrays and V2 Zod /
Standard Schema all map through a unified `FormSchema` derived at
runtime from the captured config.
- `useCopilotAction`, `useCopilotAuthenticatedAction_c`,
`useCoAgentStateRender`, `useLangGraphInterrupt`, `useRenderTool`,
`useRenderToolCall`, `useDefaultRenderTool`, `useLazyToolRenderer`,
`useRenderCustomMessages`, `useRenderActivityMessage`,
`useHumanInTheLoop`, `useInterrupt`, `useFrontendTool`, `useComponent`,
`useDefaultTool` all previewable.
- Inline `▶️ Preview Component` CodeLens above every render-hook call
site, backed by the same `copilotkit.hooks.preview` command as the
sidebar.
- Imported render components work: rolldown walks transitive imports
from the hook's `render` prop through any number of sibling files.
- Cross-file hook switches are robust: controls are reset on load,
Harness only mounts once the real HostRoot is ready, a top-level error
boundary auto-recovers when you pick a different hook.

### Why the stub approach
Bundling the real `@copilotkit/react-core` through rolldown's IIFE
output hit a `__commonJSMin` TDZ chain (`require_clipboard`,
`require_graphql`, `require_context_helpers`, …) because the
chat/runtime-client/markdown graph has circular imports. Externalizing
react-core + routing to a Proxy-backed stub that captures hook configs
avoids the whole CJS wrapping problem, shrinks the preview bundle from
~24 MB to ~1.3 KB, and keeps the preview runtime path completely
runnable without a live CopilotKit backend. Tradeoff documented in
`copilotkit-stubs.ts`.

### Weather-themed fixtures
14+ fixtures under `packages/vscode-extension/test-workspace/hooks`,
each a distinct visual scenario (forecast card, severity-palette alerts
with imported CSS, forecast strip, live radar grid, conic-gradient
precipitation gauge, air-quality badge with imported render, pollen
report with a 2-hop import graph, HITL evacuation confirm,
sunrise/sunset gradient, etc.). Used both as regression fixtures and as
the demo surface for video.

### Styling
- Tailwind-via-CDN + VS Code CSS variables for theme-aware chrome.
- User-provided CSS imports collected by rolldown and injected as a
`<style>` tag per load.
- Controls + form fields converted to Tailwind; textarea matches input
styling.
- Framed "Rendered output" card so the render prop is visually
unmistakable.

## AG-UI Event Inspector

### Runtime (`@copilotkit/runtime` + `@copilotkit/shared`)
- `DebugEventBus` — in-memory pub/sub on `BaseCopilotRuntime`, only
instantiated when `NODE_ENV != production`.
- Event tap in `createSseEventResponse` broadcasts every AG-UI event
with metadata (agentId, threadId, runId, timestamp).
- `GET /debug-events` SSE endpoint — returns 404 in production, streams
`DebugEventEnvelope` JSON to connected clients, initial `: connected`
comment flushes headers immediately.

### VSCode Extension
- `DebugStream` — Node SSE client with auto-reconnect, exponential
backoff, URL validation, error surfacing.
- `InspectorPanel` (editor panel, command `CopilotKit: Open AG-UI
Inspector`) and `InspectorViewProvider` (sidebar view) both use a shared
`DebugStream` instance — events persist when switching tabs.
- Inspector React app: `ConnectionBar`, `FilterBar`, `EventList`,
`EventDetail`.
- Color scheme: purple (lifecycle), red (errors), blue (text), orange
(tools), green (reasoning), teal (state), yellow (activity), gray
(unknown).

## A2UI Catalog → webview

- Replaces `ComponentPreviewProvider` (native TreeDataProvider) with
`CatalogListViewProvider` (WebviewViewProvider), matching the Hooks and
Inspector sidebars.
- New React webview with refresh header, component rows (name + relative
path + `auto` badge when no fixture), expandable fixtures list.
- Click a component row → preview (or toggle if it has fixtures); click
a fixture row → preview that fixture.
- Hover action buttons: `▷` preview + `</>` go-to-source on every row.
- "Go to source" opens the component file for component rows; for
fixture rows it opens the fixture file and jumps the cursor to the named
fixture key.

## Test coverage
- Runtime: DebugEventBus unit tests (8), handleDebugEvents endpoint (5),
fetch-router routes (4), integration across Express/Hono/Node/Fetch (9).
- Hooks: scanner + 16 fixture bundle-smoke test, regression guard
against `node_<builtin>` self-references, CSS collector test, stub-based
capture E2E, cross-kind controls remount, FormRenderer defensive
rendering.
- Inspector + webview: DebugStream reconnect (10), inspector components
(17), colors (9).
- Total: **178 tests** passing for the vscode-extension package; runtime
suite unchanged.

## Test plan

- [ ] `pnpm nx run copilotkit-vscode-extension:build` and `pnpm nx run
copilotkit-vscode-extension:test` both green
- [ ] F5 launches the Extension Dev Host with `test-workspace` open
- [ ] Hooks sidebar lists every fixture hook; click a row → preview
opens; `</>` button opens the source
- [ ] `▶️ Preview Component` CodeLens shows above every render hook in a
`.tsx` file; clicking it opens the preview
- [ ] Form controls drive the render live; cross-kind hook switches
(action ↔ custom-message) don't crash; a forced render-prop throw
recovers when a different hook is picked
- [ ] Imported-render fixtures (`ImportedAirQuality`,
`ImportedPollenReport`) bundle and preview correctly
- [ ] A2UI Catalog sidebar is the new webview, refresh works, `</>` on a
fixture opens the fixture file and reveals the named key
- [ ] AG-UI Inspector connects to `GET /debug-events`, filters + detail
work, events survive sidebar/panel switch, invalid URL shows red error
2026-04-21 17:09:45 -07:00
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
Martha Schumann 48331a8cfe fix(inspector): address code review findings
- ThreadStoreRegistry.register() now fires onThreadStoreUnregistered
  before overwriting an existing store so subscribers (web inspector)
  don't stay subscribed to stale stores on replacement; test extended
  to verify both events fire in order
- Wire handleClearThreads to POST /threads/clear route; add RouteInfo
  variant, router pattern, and fetch-handler case so the inspector can
  actually call it
- Add handleGetThreadMessages tool-call mapping test using properly
  typed Message objects (role as const, type as const) — exercises the
  real mapping code path without mocking getThreadMessages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 15:19:58 -07:00
Martha Schumann 0c287c65f3 chore: merge origin/main
Excludes showcase/shell-docs and showcase/shell-dojo demo-content.json from
the size check in lefthook.yml — these data files were added by main but
weren't in the exemption list, causing the pre-commit hook to reject them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 15:17:06 -07:00
Martha Schumann 0e77affe14 Merge main: remove langgraph_agent.py (deprecated), resolve lock conflict
- Keep deletion of sdk-python/copilotkit/langgraph_agent.py (deprecated LangGraphAgent
  removed in this PR; main's unrelated bug fixes are superseded by our removal)
- Resolve poetry.lock conflict by taking main's ag_ui_langgraph 0.0.33

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 12:10:19 -07: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
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] 0ee8f18462 style: auto-fix formatting 2026-04-16 14:24:32 +00:00
Alem Tuzlak 1eb4a154ad Merge origin/main into feature branch: resolve 7 conflicts 2026-04-16 16:16:48 +02:00
Alem Tuzlak a8fdf8de0b fix: CR round 1 — leaked listeners, silent errors, missing menu entries, test for SSE comment 2026-04-16 15:02:55 +02:00
Alem Tuzlak 9292e9a5fa fix: address CR round 1 findings — leaked listeners, silent errors, missing menu entries 2026-04-16 15:01:11 +02:00
Alem Tuzlak 7fc82c6061 fix(runtime): send SSE comment on connect to flush response headers immediately 2026-04-16 14:23:54 +02:00
Alem Tuzlak 0813cc390a test(runtime): add debug-events integration tests across all adapters
Tests the /debug-events SSE endpoint across Express, Hono, Node, and
Fetch Direct adapters. Verifies SSE response format, event flow through
the DebugEventBus, envelope structure (timestamp, agentId, threadId,
runId, event), full event sequence, HTTP method validation (405 for
POST), and NODE_ENV=production guard (404).

Key implementation detail: reader.cancel() must NOT be awaited on
tee'd ReadableStreams (created by response.clone() in the fetch
handler) because Node.js blocks until the other tee branch is also
consumed/cancelled.
2026-04-16 13:57:53 +02:00
Alem Tuzlak 7bde34a856 test(runtime): add handleDebugEvents endpoint tests 2026-04-16 13:03:32 +02:00
Alem Tuzlak 1848a161ee test(runtime): add DebugEventBus and debug-events route tests 2026-04-16 13:00:09 +02:00