Annotate any page.
Humans and agents, together.
A TypeScript annotation engine that injects into any live page and turns it into a shared canvas — humans pin feedback on real elements, agents push annotations and move cursors back. One schema (AFS 1.1), one Forensic output, fully bidirectional.
Human → Agent
Pin a comment on any element, text selection, or group. Streams to your agent over a webhook in AFS-1.1.
Agent → Human
Agents call window.__annotations to drop annotations, reply in threads, and glide a cursor live.
Forensic context
Every annotation carries selector path, computed styles, React tree, a11y, and nearby DOM for precise location.
Drop-in inject
Auto-injects into a Kernel/CDP browser — no extension, no install on the target page.
Install #
Published to the private registry as @coframe-gtm/annotations.
# pnpm / npm / yarn
pnpm add @coframe-gtm/annotations
Versioned entry points keep the API stable across majors:
| Import | What you get |
|---|---|
@coframe-gtm/annotations/v1 | The browser library + types (mount overlay, api, schema). |
@coframe-gtm/annotations/v1/inject | The self-installing IIFE source for CDP / script injection. |
@coframe-gtm/annotations/v1/server | Server-side helpers: webhook event types + AFS output formatters. |
Quick start #
Mount the overlay, point it at a webhook, and you have a bidirectional channel.
import { mountOverlay, api, initSession } from "@coframe-gtm/annotations/v1";
// 1. Mount the overlay into a host element (closed Shadow DOM inside).
mountOverlay(document.getElementById("app"));
// 2. Open a session — annotations + presence stream to this webhook.
initSession({ webhookUrl: "https://your.host/annotations/webhook" });
// 3. Drive it from anywhere via the global bridge.
window.__annotations.setMode("feedback"); // let humans pin feedback
window.__annotations.subscribeMode((m) => console.log("mode:", m));
Inject into a live browser #
No Chrome extension, no install on the target page. The library ships a self-installing IIFE you push over the Chrome DevTools Protocol — exactly how it auto-injects into a Kernel browser.
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const iife = buildInjectableBundle(); // from /v1/inject
// Inject on every new document + the current one.
await cdp.Page.addScriptToEvaluateOnNewDocument({ source: iife });
await cdp.Runtime.evaluate({ expression: iife });
window.__annotations,
not a branded name. Host apps wire their own webhook + branding on top.