@contextune/sdk
v0.2.0
Published
Network-intercept behavioural SDK: captures analytics events at the transport layer and surfaces a structured snapshot for AI agent context.
Maintainers
Readme
@contextune/sdk
Network-intercept behavioural SDK: captures analytics events at the transport layer and surfaces a structured snapshot for AI agent context.
Contextune helps customer-facing AI agents respond better by giving them behavioural context they normally can't see. It captures a live view of what the user is doing in the browser, the pages they visit, where they click and scroll, where they hesitate, and injects it into the agent's context window. The agent can then infer intent from behaviour instead of from the latest message alone. It's built for cases where what the user just did on the page changes what the agent should say: travel concierges, in-product support agents, and ecommerce shopping assistants.
@contextune/sdk is the client-side piece. It maintains that behavioural profile in the browser and exposes it as a structured snapshot you can drop into a prompt. It runs in plain JavaScript or TypeScript with no framework or model assumptions, makes no network requests of its own, and keeps the snapshot in memory until you read it.
The snapshot captures signals an agent can't infer from the message alone: the running event log with elapsed time between events, scroll depth, rage clicks, the marketing parameters that brought the user in, and device context.
contextune.com · docs.contextune.com
Install
npm install @contextune/sdkNo peer dependencies.
Use
import { Contextune } from "@contextune/sdk";
Contextune.init({ source: "ga4" });
// Later, at the point your agent is invoked:
const snapshot = Contextune.getSnapshot();
// Inject `snapshot` into the agent's system prompt or first message.getSnapshot() returns a structured object:
{
event_log: [
{ name: 'page_view', elapsed_s: 0 },
{ name: 'add_to_cart', elapsed_s: 34, properties: { item: 'field-jacket-m' } },
{ name: 'checkout_started', elapsed_s: 67 },
],
marketing_params: {
utm_source: 'google', utm_medium: 'cpc', utm_campaign: 'summer',
utm_term: null, utm_content: null,
referrer: 'https://www.google.com/',
landing_page: 'https://shop.example.com/?utm_source=google',
},
device_info: {
user_agent: '...',
viewport_w: 1440, viewport_h: 900,
language: 'en-GB',
platform: 'MacIntel',
device_type: 'desktop',
},
}Two modes
Network-intercept mode
Pass source to tap an existing analytics stream. The SDK patches fetch, sendBeacon, and XMLHttpRequest, filters traffic from the configured provider, and extracts structured events without any changes to your existing analytics setup.
Contextune.init({ source: "ga4" }); // tap GA4 traffic
Contextune.init({ source: "segment" }); // tap Segment traffic
Contextune.init({ source: "mixpanel" }); // tap Mixpanel trafficDirect-call mode
Omit source entirely. No network interception; only track() and page() calls are captured. Use this when you want Contextune itself as the lightweight analytics layer.
Contextune.init();
Contextune.track("filter-applied", { facet: "size", value: "M" });
Contextune.track("checkout-started", { cart_value: 129 });Options
Contextune.init({
// 'ga4' | 'segment' | 'mixpanel' — tap an existing analytics stream.
// Omit for direct-call mode (only track()/page() are captured).
source: "ga4",
// Cap on the rolling event log. Default 50.
eventLogSize: 50,
// Event log lifetime.
// 'navigation' (default) — cleared on page reload.
// 'ct-session' — survives reloads; scoped to 30 min of inactivity.
persistence: "navigation",
// Context blocks. Both default to true.
context: {
extras: {
marketing: true, // UTM params, referrer, landing page
device: true, // viewport, language, platform, device type
},
},
// DOM-derived behavioural signals (optional).
tracking: {
scroll: true, // fire 'scroll' at 25/50/75/100% depth milestones
rageClicks: true, // fire 'rage_click' on ≥3 clicks within a 1-second window
},
});Subscribing to events
React to events as they arrive rather than polling getSnapshot().
const unsubscribe = Contextune.subscribe(filter, handler);handler receives two arguments:
| Argument | Type | Description |
| -------- | -------------------- | ------------------------------------------------------------------ |
| entry | EventLogEntry | The event that just landed |
| state | ContextuneSnapshot | Full snapshot captured immediately before this event was added |
filter controls which events trigger the handler:
// Fire on every event
const unsubscribe = Contextune.subscribe("event", (entry, state) => {
console.log(entry.name, entry.elapsed_s);
});
// Fire only when the predicate returns true
const unsubscribe = Contextune.subscribe(
(entry) => entry.name === "checkout_started",
(entry, state) => sendAgentContext(state),
);
// Cleanup — call the returned function to detach
unsubscribe();Subscriptions are framework-agnostic. They work in vanilla JS, React, Vue, or any other environment. Multiple subscribers can be active simultaneously. All are cleared automatically on Contextune.destroy().
If subscribe is called before init(), the returned unsubscribe is a no-op.
Lifecycle
Contextune.init(options?): idempotent singleton. Returns the existing instance if already initialised. Calldestroy()first to re-initialise with different options.Contextune.getSnapshot(options?): returns the current snapshot, ornullbeforeinit().Contextune.track(name, properties?): push a named event into the log directly.Contextune.page(url?): record a page view. Call on each SPA route change.Contextune.subscribe(filter, handler): react to events as they arrive. Returns an unsubscribe function.Contextune.clear(): empty the event log (in memory and, underct-session, its persisted copy) while keeping the SDK running — patches, subscribers, and the session clock stay intact.Contextune.destroy(): full teardown — removes transport patches, clears the buffer and all subscribers, resets the singleton, and underct-sessionremoves the stored log and expires the cookie so the profile can't be recovered.
Page views (single-page apps)
Call Contextune.page() on every route change so the event log reflects navigation:
import { Contextune } from "@contextune/sdk";
// e.g. in a Next.js usePathname() effect or router subscription:
Contextune.page();
// or pass an explicit URL to record a pre-redacted path:
Contextune.page("/checkout/confirmation");Snapshot formats
getSnapshot() accepts an optional format:
Contextune.getSnapshot(); // ContextuneSnapshot object (default)
Contextune.getSnapshot({ format: "js" }); // same — explicit
Contextune.getSnapshot({ format: "json" }); // JSON string
Contextune.getSnapshot({ format: "toon" }); // TOON string (token-optimised for LLMs)TOON (Token-Oriented Object Notation) is a compact, flat serialisation designed to minimise token usage in LLM prompts. Flat blocks render as YAML-style key: value pairs; the event log renders as a CSV table.
Privacy
The SDK never makes a network request of its own. The event log redacts properties named password, email, credit_card, cvv, ssn, and any key matching /(password|secret|token|api[_-]?key|jwt|session)/i. URL-shaped values have credential-bearing query parameters stripped and UUID path segments replaced with [id]. Redaction is applied before any event enters the ring buffer.
Bundle size
~13 KB minified / ~4–5 KB gzipped. No peer dependencies.
Supported browsers
All evergreen browsers ✅
Via CDN (no bundler):
<script src="https://unpkg.com/@contextune/sdk"></script>
<script>
Contextune.init();
const snapshot = Contextune.getSnapshot();
</script>Via package manager (bundled apps):
npm install @contextune/sdkContributing
This repository is a read-only mirror. The SDK is developed in a private monorepo and published here on each release, so changes land as Release vX.Y.Z commits rather than direct merges. Issues and pull requests are still welcome. See CONTRIBUTING.md for how they get incorporated.
License
MIT. See LICENSE. Copyright 2026 Reidworks.
