# The Watch panel

> See what the agent sees and did — live turns, the action timeline, latency — an in-app devtool you drop in during a build.

`CoworkkitWatch` is a drop-in developer panel you mount during a build to **see what the agent perceives and did**. It reads the same internal session state the on-screen button does — no new wire, no instrumentation on your side — and renders it as a floating, resizable panel over your app. Because it reads the Provider's context, mount it anywhere inside `CoworkkitProvider`; where it sits in the tree doesn't matter.

It's the fastest way to answer the two questions every integration raises: does the agent actually see the surfaces and elements I declared, and did my actions fire — and gate — the way I intended?

## Drop it in

It ships as a deep import so it stays tree-shakeable — the panel and its code never reach a bundle that doesn't import it. Add one line to the Provider you already have:

**`app/providers.tsx`**

```tsx
"use client";

import { CoworkkitProvider } from "@coworkkit/react";
import { CoworkkitWatch } from "@coworkkit/react/watch";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <CoworkkitProvider
      getToken={async () => {
        const res = await fetch("/api/session", { method: "POST" });
        if (!res.ok) {
          // Check before parsing, and guard the parse: an error page (a proxy 404,
          // a framework 500) is often HTML, and a throw here would lose both fields.
          const body = await res.json().catch(() => ({}));
          // Pass the reason AND the status through — that is what lets the FAB say
          // "Setup needed" for a bad key instead of a generic "Can't connect":
          throw Object.assign(new Error(body.error ?? "session mint failed"), {
            reason: body.reason,
            status: res.status,
          });
        }
        return res.json();
      }}
    >
      {children}
      {/* Mounts only in development; ships nothing to a production build. */}
      <CoworkkitWatch />
    </CoworkkitProvider>
  );
}
```

Watch renders only in development, and it doesn't open on its own — it's a panel you toggle. Open it from the watch icon in the on-screen button's control row, or press `ctrl+shift+k`. If you've mounted it and don't see the watch icon, you're almost certainly on a production build, where Watch hides itself by default — that's expected, not a missing feature; **It's a dev surface** (below) shows how to force it on when you need it.

## What it shows

A pinned **State strip** across the top — session phase, mic, Hand-mode arm state, the active surface, session duration and idle time — plus four tabs:

- **Timeline** — the live event stream, turn by turn: each action call, how it was gated (allowed, Hand-blocked, or held for a [confirmation](/docs/control)), and its outcome. This is where you watch an action actually fire, or catch the `needs_hand_mode` that explains why it didn't.
- **Catalogue** — the live declared-exposure snapshot: the current surface, every mounted element (including see-only ones), and the full action catalogue with each entry's `kind` and `control`. This is what [proves the agent sees](/docs/actions-surfaces) what you declared — and it marks the declared-vs-live distinction, so a snapshot taken before the agent joins isn't mistaken for "the agent already knows".
- **Transcript** — the conversation, both sides, as the agent heard and spoke it.
- **Metrics** — per-turn latency, a turn table, and a session summary, for spotting a slow leg without reaching for a profiler.

## It's a dev surface

Watch is gated to `NODE_ENV === "development"` by default — in a production build it mounts nothing, so leaving `<CoworkkitWatch />` in your tree is safe. Two escapes exist for when you need it past dev: `enableInProduction` for a QA build, and `visibleTo` — a predicate over your own auth that must return `true` before anything renders (pair it with the `redact` policy to mask transcripts and tool I/O).

The header's copy and download buttons emit the whole session — state, timeline, transcript, metrics — as a single JSON blob. That's how you hand a teammate the exact observable state you're looking at: send them the export instead of narrating what you saw.
