mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
ac75fc36f1
## What this PR does
Adds the **Threads tab** to the CopilotKit web inspector. The tab lists
every thread the current agent has run and, when you click one, shows
three per-thread sub-tabs:
- **Conversation** — historical messages
- **Agent State** — state snapshot at the end of the thread
- **AG-UI Events** — full AG-UI event stream for the thread (tool calls,
state deltas, text chunks, etc.)
Data flows through new backend endpoints plus a Lit-based UI inside
`@copilotkit/web-inspector`. No extra package required for consumers.
## Changes by layer
### `@copilotkit/core`
- **`ThreadStoreRegistry`** — new class; keyed by `agentId`, lets
`useThreads()` and the inspector share a reference to the same store
without coupling the two packages directly
- **`onAgentRunStarted` subscriber event** — fires before
`agent.runAgent()` snapshots the subscriber list, so the inspector can
subscribe in time to receive run events
- `CopilotKitCore.getThreadStore()` / `registerThreadStore()` /
`unregisterThreadStore()` / `getThreadStores()` — public surface for
hook + inspector to interact with the registry
### `@copilotkit/runtime`
- **Thread HTTP handlers** — `handleListThreads`, `handleUpdateThread`,
`handleArchiveThread`, `handleDeleteThread`, `handleSubscribeToThreads`,
`handleGetThreadMessages`, plus the two new ones below
- **New: `GET /threads/:id/events`** and **`GET /threads/:id/state`** —
return the thread's AG-UI event stream and last `STATE_SNAPSHOT`
payload. Wired through both the in-memory runner and the Intelligence
platform's `_inspect/threads/:id/{events,state}` endpoints (consumed by
`CopilotKitIntelligence.getThreadEvents()` / `getThreadState()`)
- All mutations authenticate via `identifyUser(request)`; `userId` in
the request body is ignored
- **`InMemoryAgentRunner`** — stores thread history (messages +
compacted events per run); new `getThreadEvents()` and
`getThreadState()` methods; `getThreadState()` walks the compacted
events and returns the payload of the last `STATE_SNAPSHOT`
### `@copilotkit/react-core`
- **`useThreads` hook** — fetches threads, subscribes to a Phoenix
WebSocket channel for real-time metadata events, and
registers/unregisters its thread store with `CopilotKitCore` on
mount/unmount
### `@copilotkit/web-inspector`
- **Full Threads tab UI** — implemented in Lit as two custom elements
(`cpk-thread-list`, `ɵCpkThreadDetails`) living in-file alongside the
main `WebInspectorElement`
- Thread details fetches per-thread history via the new endpoints and
renders:
- Conversation: user/assistant bubbles, tool-call blocks with
expand/collapse, tool-call groups, reasoning/state-update chips,
generative-UI placeholders. Tool-call status is derived from parsed-args
presence — frontend-rendered generative-UI tools (charts, custom UI)
read `DONE` once args have streamed in, since they never produce a
`role: tool` result message
- Agent State: syntax-highlighted JSON of the last state snapshot
- AG-UI Events: colored event rows (by type family) with timestamped,
highlighted payloads. Off-screen rows use `content-visibility: auto` so
reveal cost is independent of total event count
- Right-side detail panel with thread metadata + activity counts,
toggled from the tab bar
- Tab DOM is mounted once per activation and hidden via `display:none`
when inactive, so switching between Conversation / Agent State / AG-UI
Events is a CSS swap rather than a render. Each panel's TemplateResult
is memoized by tuple of input references (`_conversation` + expand-state
Sets for conversation; `_fetchedState` for agent state; events array for
AG-UI events), so when the underlying data hasn't changed Lit's diff
short-circuits. JSON syntax highlighting is WeakMap-memoized by payload
reference
- `attachToCore()` guards the `core.getThreadStores()` call so consumers
still on an older `@copilotkit/core` don't throw when assigning
`inspector.core`
## Architectural notes
**Events/state via Intelligence:** the Intelligence platform persists
every AG-UI event in `cpki.run_events` keyed by run → thread and exposes
them via `_inspect/threads/:id/{events,state}`. The runtime's
`CopilotKitIntelligence.getThreadEvents()` / `getThreadState()` consume
those, so the same per-thread HTTP endpoints used by the in-memory path
serve Intelligence-backed consumers identically.
## Tests added
| File | What's new |
|---|---|
| `packages/core/src/__tests__/thread-store-registry.test.ts` | New —
register/get, replacement, no-op unregister, subscriber events |
| `packages/runtime/src/v2/runtime/__tests__/handle-threads.test.ts` |
`handleClearThreads`, `handleGetThreadMessages`, plus new
`handleGetThreadEvents` and `handleGetThreadState` describe blocks |
|
`packages/runtime/src/v2/runtime/runner/__tests__/in-memory-runner.test.ts`
| `getThreadMessages`, new `getThreadEvents` (stored events, unknown
thread, multi-run flattening), new `getThreadState` (null without
snapshot, returns last STATE_SNAPSHOT, most-recent across runs) |
| `packages/react-core/src/v2/hooks/__tests__/use-threads.test.tsx` |
Registry lifecycle (register on mount, unregister on unmount) |
| `packages/web-inspector/src/__tests__/web-inspector.spec.ts` | New
`ɵCpkThreadDetails caching` describe — threadId-change drops all panel
caches; conversation cache invalidates on `_conversation` reassignment
and on expand-state change; state and events caches invalidate on their
fetched-data reassignment |
🤖 Generated with [Claude Code](https://claude.com/claude-code)