EITP v1.3
Emotional Intelligence Transfer Protocol
This is not an IETF RFC. It is a protocol specification published by the original implementer, documenting a production system running across automotive, RV, and powersports verticals. The specification is derived entirely from working code. All thresholds, packet formats, and taxonomy values reflect the production implementation. This document invites interoperable implementations and community contribution.
Changelog — v1.2 → v1.3
v1.3 introduces one new packet dimension — Provenance — and refines the bot-filtering model to acknowledge a class of session that passes the human-proof gate (§8) but produces motion the inference engine judges physically impossible. Both changes are additive; v1.2 packets remain forward-compatible.
-
New dimension: Provenance (§5.12). The packet now carries
an inference-time judgment about whether the underlying telemetry batch is
consistent with a physically-attested human body. Values:
plausible(motion was physical; the emotion read is from a real human) anddeclined(motion was physiologically impossible — e.g. cursor jerk orders of magnitude beyond human capability — and the inference engine refused to commit to an emotion label). Consumers should treatdeclinedpackets as substrate-attested non-human and exclude them from aggregate signals such as the Emotional Volatility Index (Dimension 8). - Bot filtering (§8) framing refined. The v1.2 inverted human-proof model is unchanged at the producer layer. v1.3 adds that the inference engine may itself contribute a second, semantic layer of rejection — flagging batches whose motion is physically impossible even after the proof gate has been cleared. This catches sophisticated synthesis (humanlike scroll variance paired with non-human mouse motion) without retraining the proof gate against an infinite adversarial tail.
-
Reference implementation note. The reference engine
(SentientIQ haiku-first-pipeline.cjs) emits
physical_plausibilityas a structured field on every Sonnet classification. The persistence layer writes the value toemotional_events.physical_plausibility(migration 128); the EVI aggregator filters toplausible.
Changelog — v1.1 → v1.2
v1.2 reconciles the specification with the reference implementation as of May 2026. Every change below reflects a decision the running platform already made; the spec is catching up, not introducing new requirements.
- Inference pipeline (§7) rewritten as single-tier Sonnet-primary. The v1.1 two-tier "Haiku triage → Sonnet deep analysis" design was retired in production. Tier-1 was acting as a gate without materially improving outcome quality at the platform's volume; Sonnet now runs primary on every batch the rule-gate admits. The fast-tier path is documented historically; it is no longer part of the conformance contract.
-
Emotion taxonomy (§6) expanded to two orthogonal axes.
v1.1's flat 21-label list (text said "22") collapsed emotions and behaviors into
one dimension, producing drifting composite labels like
analysis_paralysisandprice_sensitivity. v1.2 separates emotion (28 states — how the buyer feels) from behavior (10 states — what the buyer is doing) and treats context (pricing, payment, trade-in, …) as free-text.analysis_paralysisnow decomposes to(emotion: paralysis, behavior: analysis); the packet carries both fields plus context. - Bot filtering (§8) rewritten around an inverted human-proof model. v1.1's velocity / pattern thresholds (click rate > 50, scroll > 100, CV < 0.1) generated false positives on fast humans, high-DPI cursors, and VPN/mobile-carrier ranges. v1.2 inverts the question: sessions are assumed bot until one of three physiological signals proves a human (scroll-velocity variance, mouse tremor in the 8–12 Hz band, or speed variance), with two hard-reject guards (sub-200 ms first interactive event; mouse-event bursts above machine-speed density).
-
Intervention cooldown default raised 60 s → 300 s
(§9.3). Production data showed 60 s permitted intervention stacking
within a single buyer's deliberation window. Per-tenant override is supported via
seer_config.intervention_gap_ms. - Tracker count corrected to 8 (§3). v1.1 enumerated 10 tracker subsections but the conformance text said 8. The actual modules are mouse, click, scroll, viewport, tab, form, selection, plus the proximity engine. Gesture detection (circular motion, dwell, zigzag, exit vector) operates on the mouse tracker's history buffer; page content extraction is a library function. Both retain their own subsections under §3 but neither is a standalone tracker module for conformance purposes.
-
HTTP-header advertisement deferred. v1.1's JSON-LD referenced
X-EITP-Versionresponse headers and a<meta name="eitp-compliant">tag. Neither is implemented in the reference platform. v1.2 holds conformance advertisement to an/eitp.jsondescriptor only; headers and meta-tag advertisement are reserved for a future revision when third-party consumers exist. - Packet dimension count made consistent. v1.1 referenced "8 dimensions" in the abstract and architecture diagram while §5 defined nine. v1.2 uses nine throughout.
1. Introduction
1.1 The Cold-Start Problem
Every LLM-powered conversation begins the same way: the model receives a user's first message and must infer everything — intent, emotional state, urgency, context — from that single text input. This is the cold-start problem.
But the user wasn't cold before they typed. They spent minutes browsing. They scrolled past pricing. They hovered over a call-to-action and pulled away. They switched tabs to compare a competitor. They came back. Their mouse slowed to a crawl on the financing section. All of this behavioral signal — rich, continuous, and expressive — is discarded the moment the chat window opens.
EITP captures this signal, transforms it into structured emotional context, and delivers it to the LLM so the first response is informed rather than generic.
Production evidence from the reference implementation:
- Decision paralysis — 16 conversions observed, 38.9 minute average session duration, 25.1 emotional events per session
- Overwhelm — 10 conversions, 19.2 minute average, 45.6 events per session
- Cognitive overload — 9 conversions, 126.5 minute average, 17.3 events per session
- Frustration — 6 conversions, 42 minute average, 50 events per session
62% of buyers who convert do so under uncertainty or cognitive distress. These buyers work 2.7x harder to reach a decision than engaged, confident buyers. Without behavioral telemetry, an LLM has no way to distinguish a paralyzed buyer (who needs guidance) from a confident one (who needs space).
1.2 Design Goals
- Implementable. A developer can build a conformant producer or consumer from this document alone.
- Layered. You can implement the packet format (Section 5) without the inference pipeline (Section 7). A consumer needs only Sections 5, 6, and 9.
- Privacy-first. No PII crosses the wire. Form values are never transmitted. Text selections are sanitized. IP addresses are injected server-side and excluded from consumer-facing packets.
- LLM-agnostic. The EITP packet is a JSON data structure. It is not specific to any model provider, framework, or inference strategy.
1.3 Terminology
- Producer
- A client-side system that captures behavioral telemetry and transmits it. Typically a JavaScript library running in the browser.
- Consumer
- A system that receives EITP packets and uses the emotional context. Typically an LLM or an application layer that injects EITP context into an LLM prompt.
- Bridge
- A server-side intermediary that receives telemetry from producers over WebSocket, injects server-side context (e.g., IP address), and forwards to a message bus.
- Inference Engine
- A system that processes raw telemetry and produces EITP v1.3 packets. May use AI, rules, or any combination.
- Session
- A bounded period of user interaction, identified by a session ID. Expires after a configurable timeout (default: 5 minutes of inactivity).
- Emotion
- A categorical label from the 28-value emotion axis (Section 6) describing how the user feels.
- Behavior
- A categorical label from the 10-value behavior axis (Section 6) describing what the user is doing. Orthogonal to emotion.
- Intervention
- A proactive action taken by a consumer in response to an EITP packet — typically a conversational message initiated before the user sends their first message.
1.4 Architecture Overview
EITP defines six layers. Implementations may target any subset, provided they produce or consume conformant data structures at layer boundaries.
A minimal producer implements Layers 1–2 only (capture and transmit raw telemetry). A full-stack implementation covers Layers 1–5. A consumer needs only to understand the Layer 5 packet format (Section 5) and the taxonomy (Section 6).
2. Conformance Levels
EITP defines five conformance roles. An implementation may fulfill one or more roles simultaneously.
2.1 Producer Level 1 (Telemetry)
A Level 1 Producer:
- Must implement at least 3 of the 8 tracker modules defined in Section 3 (mouse, click, scroll, viewport, tab, form, selection, proximity)
- Must batch events before transmission (Section 4.3)
- Must sanitize PII from all text payloads before transmission (Section 10.1)
- Must include a session identifier with every batch
- Must include a tenant identifier with every batch
- Should implement reconnection with exponential backoff (Section 4.4)
2.2 Producer Level 2 (Full)
A Level 2 Producer meets all Level 1 requirements and additionally:
- Must implement all 8 tracker modules
- Must implement gesture detection over the mouse tracker's history buffer (Section 3.2)
- Must implement page content extraction (Section 3.10)
- Must include page content in telemetry batches
2.3 Bridge
A conformant Bridge:
- Must accept WebSocket connections from producers
- Must inject the client's IP address into telemetry payloads server-side (Section 4.5)
- Must enforce rate limiting (Section 4.6)
- Must enforce a subject/topic whitelist on publish operations
- Must enforce a maximum message size
- Should support the NATS text protocol framing (Section 4.1)
2.4 Inference Engine
A conformant Inference Engine:
- Must produce EITP v1.3 packets conforming to Section 5
- Must classify emotions using the two-axis taxonomy (Section 6): one value from the 28-state emotion axis and one from the 10-state behavior axis
- Must implement bot filtering meeting the minimum requirements of Section 8
- Should track session emotional history for trajectory calculation
- May use any inference strategy (AI, rules, statistical models, or hybrid)
2.5 Consumer
A conformant Consumer:
- Must accept EITP v1.3 packets as defined in Section 5
- Must follow the consumption contract (Section 9)
- Must respect cooldown fields
- Must not expose telemetry details to the end user
3. Signal Capture Layer
The signal capture layer consists of eight independent tracker modules — mouse, click,
scroll, viewport, tab, form, selection, and the proximity engine — that attach to DOM
events, compute derived metrics, and emit typed events through a shared
track(type, data) interface. Each module is independently implementable. A
Level 1 Producer requires any three. A Level 2 Producer requires all eight plus gesture
detection (§3.2) and page content extraction (§3.10), neither of which is a standalone
tracker: gesture detection operates on the mouse tracker's history buffer, and page content
extraction is a library function invoked when assembling each batch.
3.1 Mouse Tracker
The mouse tracker captures cursor position and computes the first three kinematic derivatives in real time. This is the foundational signal source — mouse kinematics encode hesitation, urgency, confusion, and confidence in ways that text alone cannot express.
Physics Chain
At each sampling tick, the tracker computes the full kinematic state from consecutive position samples:
// Position (raw input)
x, y // pixels
// Velocity (1st derivative)
vx = dx / dt // px/s
vy = dy / dt // px/s
// Acceleration (2nd derivative)
ax = dvx / dt // px/s²
ay = dvy / dt // px/s²
// Jerk (3rd derivative)
jx = dax / dt // px/s³
jy = day / dt // px/s³
// Scalar magnitudes
speed = √(vx² + vy²) // px/s
jerk = √(jx² + jy²) // px/s³
Event: mouse
Emitted at each sampling tick.
| Field | Type | Unit | Description |
|---|---|---|---|
x | number | px | Cursor X position (clientX) |
y | number | px | Cursor Y position (clientY) |
vx | number | px/s | X velocity |
vy | number | px/s | Y velocity |
ax | number | px/s² | X acceleration |
ay | number | px/s² | Y acceleration |
jx | number | px/s³ | X jerk |
jy | number | px/s³ | Y jerk |
speed | number | px/s | Velocity magnitude |
jerk | number | px/s³ | Jerk magnitude |
Configuration
| Parameter | Default | Unit |
|---|---|---|
| Sampling rate | 20 | Hz |
| History buffer | 100 | samples |
| Inactivity threshold | 5000 | ms |
3.2 Gesture Detection
Gesture detection operates on a sliding window of the last 10 mouse samples from the history buffer. Three gesture patterns are detected:
Circular Motion
The centroid of the 10 points is computed. Each point's distance from the centroid is measured.
Circularity is defined as max(0, 1 - (variance / avgRadius²)). A score of 1.0
indicates a perfect circle; a score below the threshold indicates non-circular motion.
Event: circular_motion
| Field | Type | Description |
|---|---|---|
circularity | number (0–1) | Circularity score |
radius | number (px) | Average radius from centroid |
duration | number (ms) | Time span of window |
Thresholds: circularity > 0.85, radius > 20px
Zigzag (Direction Changes)
Direction is quantized to 8 compass octants (π/4 buckets). Changes are counted with a minimum movement of 30px and 300ms debounce between changes.
Event: direction_changes
| Field | Type | Description |
|---|---|---|
count | number | Direction changes in window (>6) |
duration | number (ms) | Time span of window |
avgSpeed | number (px/s) | Average speed across window |
Threshold: > 6 direction changes in 10 samples
Dwell (Hesitation)
Emitted when cursor speed drops below the micro-movement threshold for longer than the dwell threshold. Only fires when the cursor is inside the viewport.
Event: mouse_pause
| Field | Type | Description |
|---|---|---|
duration | number (ms) | Time spent dwelling |
x, y | number (px) | Cursor position |
viewport.relX, viewport.relY | number (0–1) | Relative viewport position |
Thresholds: speed < 20 px/s, duration > 500ms
Exit Vector
Event: mouse_exit — emitted when the cursor leaves the document.
| Field | Type | Description |
|---|---|---|
angle | number (degrees) | Exit angle from viewport center |
velocity | number (px/s) | Exit speed |
edge | string | "top" | "right" | "bottom" | "left" |
x, y | number (px) | Exit position |
3.3 Click Tracker
Event: click
| Field | Type | Description |
|---|---|---|
x, y | number (px) | Click position |
target.tag | string | Element tag name |
target.class | string | Element class name |
target.id | string | Element ID |
target.text | string | Text content (first 50 chars) |
mouseVelocity | number (px/s) | Cursor speed at moment of click |
mouseJerk | number (px/s³) | Cursor jerk at moment of click |
Event: rage_click
Emitted when 3 or more clicks occur within a 500ms window. Indicates frustration with an unresponsive element.
| Field | Type | Description |
|---|---|---|
count | number | Number of rapid clicks (≥3) |
area | number (px²) | Bounding box area of click cluster |
Thresholds: ≥ 3 clicks within 500ms
3.4 Scroll Tracker
Event: scroll
Debounced at 100ms.
| Field | Type | Description |
|---|---|---|
depth | number (0–100) | Current scroll depth percentage |
maxDepth | number (0–100) | Maximum depth reached this session |
velocity | number (px/s) | Scroll velocity (positive = down) |
direction | string | "down" | "up" |
3.5 Proximity Engine
The proximity engine tracks cursor distance to interactive elements using a spatial grid (cell size: 160px). It detects when the cursor enters, hovers over, lingers near, or leaves the proximity zone of categorized page elements.
Element Kinds
Eleven element kinds, listed in priority order with their associated intent levels:
| Kind | Intent Level | Examples |
|---|---|---|
price | — | Price displays, cost elements |
purchase_button | high | Buy, Add to Cart, Order |
checkout_button | high | Checkout, Complete Purchase |
subscribe_button | high | Subscribe, Upgrade |
demo_cta | medium-high | Book Demo, Schedule, Try |
signup_cta | medium-high | Sign Up, Register, Get Started |
tier_card | medium | Pricing tier/plan containers |
pricing_nav | medium | Pricing navigation links |
feature_nav | low-medium | Feature navigation links |
learn_more | low-medium | Learn More, Read More |
cta | low | Generic calls to action |
Proximity Events
For each element kind, four events are generated:
{kind}_proximity_enter— cursor enters the 140px radius{kind}_hover— cursor is directly over the element{kind}_proximity— cursor is within radius but not directly over{kind}_proximity_leave— cursor exits the 152px leave radius (140px + 12px spatial hysteresis) or 600ms time hysteresis elapses
Proximity Enter Payload
| Field | Type | Description |
|---|---|---|
distance | number (px) | Distance from element edge |
over | boolean | True if directly over element |
element_type | string | The element kind |
text | string | Element text (first 80 chars) |
intent | string | Intent level for this kind |
signals.hasDiscount | boolean | Text/class contains discount language |
signals.hasTrial | boolean | Text/class contains trial language |
signals.hasUrgency | boolean | Text/class contains urgency language |
Price elements additionally include: currency, value (parsed numeric), isOriginal, isSale.
Event: pricing_comparison
Emitted when 2 or more tier_card elements are simultaneously visible in the
viewport. Cooldown: 5000ms between emissions.
Configuration
| Parameter | Default | Unit |
|---|---|---|
| Grid cell size | 160 | px |
| Enter radius | 140 | px |
| Spatial hysteresis | 12 | px (leave = 152px) |
| Time hysteresis | 600 | ms |
3.6 Viewport Tracker
Event: viewport_resize
Debounced at 250ms.
| Field | Type | Description |
|---|---|---|
width, height | number (px) | Viewport dimensions |
orientation | string | "landscape" | "portrait" |
Event: viewport_boundary
Fires once when user scrolls to within 50px of the page top or bottom.
| Field | Type | Description |
|---|---|---|
boundary | string | "top" | "bottom" |
scrollDepth | number | 0 (top) or 100 (bottom) |
Event: viewport_approach (Exit Intent)
Emitted when the cursor is within 100px of the top viewport edge AND moving upward with
velocity exceeding 400 px/s. Suppressed when the cursor is over a <nav>,
<header>, or [role="navigation"] element.
| Field | Type | Description |
|---|---|---|
edge | string | "top" |
distance | number (px) | Distance from top edge |
velocity | number (px/s) | Cursor speed magnitude |
vector.vx, vector.vy | number (px/s) | Velocity components |
Thresholds: distance < 100px, upward velocity > 400 px/s
3.7 Tab Tracker
Event: tab_switch
| Field | Type | Description |
|---|---|---|
action | string | "away" | "return" |
awayDuration | number (ms) | Time spent away (on return) |
activeTime | number (ms) | Time spent active (on away) |
totalSwitches | number | Cumulative switch count (on return) |
Event: long_tab_return
Emitted when the user returns after an absence exceeding 30 seconds. This is a strong comparison-shopping signal — the user likely visited a competitor or alternative.
| Field | Type | Description |
|---|---|---|
awayDuration | number (ms) | Time away (>30,000ms) |
sessionTime | number (ms) | Total session time |
3.8 Form Tracker
Event: form_focus
| Field | Type | Description |
|---|---|---|
type | string | Field type (text, email, select, etc.) |
name | string | Field name attribute |
id | string | Field ID attribute |
Event: form_blur
| Field | Type | Description |
|---|---|---|
type | string | Field type |
name | string | Field name attribute |
duration | number (ms) | Time spent focused |
hasValue | boolean | Whether field has a value on blur |
Field abandonment is defined as hasValue === false AND
duration > 2000ms. No separate event is emitted; abandonment is inferred
from form_blur events meeting both conditions.
Event: form_submit
| Field | Type | Description |
|---|---|---|
id | string | Form ID |
action | string | Form action URL |
method | string | GET | POST |
3.9 Selection Tracker
Event: text_selection
Emitted on non-empty text selection. Text is PII-sanitized before transmission.
| Field | Type | Description |
|---|---|---|
text | string | Sanitized text (first 100 chars) |
length | number | Full selection length |
hasPrice | boolean | Selection matches price pattern |
Event: text_selection_with_price
Emitted additionally when the selected text contains a price pattern ($NNN.NN).
| Field | Type | Description |
|---|---|---|
text | string | Raw text (first 100 chars) |
price | string | Matched price string |
length | number | Full selection length |
PII Sanitization Patterns
Producers must replace the following patterns with [REDACTED] before transmission:
| Pattern | Regex |
|---|---|
[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,} | |
| Phone | \d{3}[-.]?\d{3}[-.]?\d{4} |
| SSN | \d{3}-\d{2}-\d{4} |
| Credit card | (\d{4}[\s-]?){3}\d{4} |
| Zip code | \d{5}(-\d{4})? |
3.10 Page Content Extraction
Page content extraction provides the object of attention — not just how the user is behaving, but what they are looking at. This is what makes EITP different from pure analytics. The LLM receives both emotional state and the content that produced it.
Extracted fields:
| Field | Description | Limits |
|---|---|---|
pageTitle | Document title | — |
h1 | First H1 element text | — |
headings | H2 element texts | Max 8 |
tables | Table headers and row data | Max 3 tables, 15 rows each |
structuredData | JSON-LD data (Product, Vehicle, Offer, ItemList, FAQPage) | — |
visibleContent | Paragraph text near viewport center | Max 5 paragraphs |
meta | Description and keywords from meta tags | — |
Content extraction should be cached for 5 seconds per section to minimize DOM reads.
4. Transport Layer
4.1 WebSocket Primary Transport
The primary transport is WebSocket (wss://). The producer connects to a Bridge
endpoint and communicates using NATS text protocol framing over WebSocket frames.
Handshake
// Server sends on connection:
INFO {"server_id":"eitp-bridge","version":"2.0.0","proto":1,"max_payload":1048576}\r\n
// Client responds:
CONNECT {"tenant_id":"...","session_id":"..."}\r\n
// Server acknowledges:
+OK\r\n
Protocol Commands
| Command | Direction | Format |
|---|---|---|
PUB | Client → Bridge | PUB <subject> <size>\r\n<payload>\r\n |
SUB | Client → Bridge | SUB <subject> <sid>\r\n |
MSG | Bridge → Client | MSG <subject> <sid> <size>\r\n<payload>\r\n |
PING | Both | PING\r\n |
PONG | Both | PONG\r\n |
Keepalive: Producers should send PING\r\n every 30 seconds.
Bridges must respond with PONG\r\n.
4.2 Subject Namespace
Messages are routed via subject strings. The following subjects are defined:
| Subject | Direction | Payload |
|---|---|---|
telemetry.raw | Producer → Bridge | Telemetry batch (Section 4.3) |
emotions.{tenantId}.detected.{sessionId} | Engine → Persistence | Emotion event |
emotions.{tenantId}.{sessionId} | Engine → Consumer | Emotional profile for chat context |
seer.{tenantId}.intervention.request.{sessionId} | Engine → Consumer | EITP v1.3 packet (Section 5) |
bot.{tenantId}.detection.{sessionId} | Engine → Analytics | Bot classification event |
Bridges must enforce a whitelist on client PUB subjects.
At minimum, only telemetry.raw should be allowed from
untrusted clients.
4.3 Batching
Producers must batch events before transmission. Events are accumulated in a buffer and flushed on a timer or when the buffer reaches capacity.
Batch Envelope
{
"type": "telemetry_batch",
"events": [
{
"type": "mouse",
"data": { /* event-specific payload */ },
"timestamp": 1740700800000,
"currentSection": "pricing"
},
// ... more events
],
"sessionId": "sq_1740700800000_abc123def",
"tenantId": "org_xxx",
"vertical": "rv_dealer",
"pageContent": { /* Section 3.10 extraction */ },
"timestamp": 1740700800000
}
Flush Parameters
| Parameter | Default | Description |
|---|---|---|
| Flush interval | 3000ms | Timer-based flush |
| Batch size | 50 events | Maximum events per batch |
| Priority threshold | 40 events | Early flush for priority events |
| Max buffer | 500 events | Hard buffer cap (oldest events dropped) |
Priority events that trigger early flush: click,
rage_click, form_submit, text_selection_with_price,
viewport_approach, mouse_pause, and all proximity events.
Producers should also flush on beforeunload and
visibilitychange (hidden) to capture final events before the user leaves.
4.4 Reconnection Strategy
When the WebSocket connection drops, producers should reconnect with exponential backoff:
| Parameter | Value |
|---|---|
| Base delay | 1,000ms |
| Backoff factor | 2 |
| Maximum delay | 30,000ms |
| Jitter | Random 0–1,000ms |
| Maximum attempts | 5 |
Formula: delay = min(1000 * 2^attempt, 30000) + random(0, 1000)
After exhausting all reconnection attempts, the producer should fall
back to HTTP POST with the same batch envelope format. On tab visibility change (hidden → visible),
the producer should send an immediate PING to verify
connection health.
4.5 Server-Side IP Injection
Bridges must inject the client's IP address into telemetry payloads before forwarding to the message bus. Clients must not self-report their IP address. This is a security requirement: client-reported IPs are trivially spoofable, and IP is used downstream for geolocation and bot detection.
4.6 Rate Limiting
Bridges must enforce rate limits to prevent abuse. Reference implementation defaults:
| Limit | Value |
|---|---|
| Maximum message size | 50,000 bytes |
| Maximum events per second (producer) | 500 |
| Maximum events per type per second | 100 |
5. The EITP v1.3 Packet Normative
The EITP v1.3 packet is the core data structure of the protocol. It is a JSON object with ten dimensions that together describe the user's emotional state, behavioral context, pre-arrival origin, the content they are engaging with, and the inference engine's attestation that the underlying motion was physically possible (the provenance verdict, §5.12). A conformant Inference Engine must produce packets matching this schema. A conformant Consumer must accept packets matching this schema.
5.1 Packet Envelope
{
"sessionId": "sq_1740700800000_abc123def",
"tenantId": "org_xxx",
"timestamp": 1740700800000,
"emotion": { /* 5.2 */ },
"spatial": { /* 5.3 */ },
"temporal": { /* 5.4 */ },
"behavioral": { /* 5.5 */ },
"patterns": { /* 5.6 */ },
"intent": { /* 5.7 */ },
"pageContent": { /* 5.8 */ },
"globalIntelligence": { /* 5.9 */ },
"channel": { /* 5.10 */ },
"provenance": { /* 5.12 — v1.3+ */ }
}
| Field | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | Unique session identifier |
tenantId | string | Yes | Tenant/organization identifier |
timestamp | number | Yes | Epoch milliseconds at packet creation |
5.2 Dimension 1: Emotion
{
"primary": "paralysis",
"behavior": "analysis",
"context": "pricing",
"confidence": 0.82,
"intensity": 0.65,
"trajectory": "intensifying"
}
| Field | Type | Required | Description |
|---|---|---|---|
primary | string | Yes | One of the 28 canonical emotions (Section 6). How the buyer feels. |
behavior | string | null | Yes | One of the 10 canonical behaviors (Section 6) or null. What the buyer is doing. |
context | string | null | No | Free-text subject (e.g. "pricing", "trade_in", "financing"). Lowercase, ≤64 chars. May be null. |
confidence | number (0.0–1.0) | Yes | Inference engine's certainty in the (emotion, behavior) classification |
intensity | number (0.0–1.0) | Yes | Strength of the emotional signal |
trajectory | string | Yes | "intensifying" | "stable" | "cooling" |
v1.2 separates emotion and behavior into orthogonal fields. v1.1's composite labels
(e.g. decision_paralysis, analysis_paralysis) collapsed these
into a single string and produced 277 drifting variants in production. Consumers that
still want the composite read can derive it as ${behavior}_${primary} when
both are present.
trajectory is computed from the session's emotional history. An engine
should maintain at least the last 3 emotion classifications per
session to determine trajectory direction.
5.3 Dimension 2: Spatial
{
"currentSection": "pricing",
"visitCount": 3,
"trigger": "meet-seer-introduction"
}
| Field | Type | Required | Description |
|---|---|---|---|
currentSection | string | Yes | DOM section ID the user is currently viewing |
visitCount | number | Yes | How many times the user has visited this section |
trigger | string | null | No | Spatial event that triggered this packet |
A user returning to the same section repeatedly is a strong signal. Three visits to
“pricing” means something different than one visit. The visitCount
gives the consumer this context.
5.4 Dimension 3: Temporal
{
"sessionDuration": 234000,
"timeSinceLastEmotion": 45000
}
| Field | Type | Required | Description |
|---|---|---|---|
sessionDuration | number (ms) | Yes | Time since session start |
timeSinceLastEmotion | number (ms) | Yes | Time since last emotion classification |
5.5 Dimension 4: Behavioral
{
"scrollDepth": 72,
"clickCount": 4
}
| Field | Type | Required | Description |
|---|---|---|---|
scrollDepth | number (0–100) | Yes | Maximum scroll depth in current batch |
clickCount | number | Yes | Click events in current batch |
5.6 Dimension 5: Patterns
{
"detected": [
"extended_dwelling_on_pricing",
"comparison_shopping_tab_pattern"
]
}
| Field | Type | Required | Description |
|---|---|---|---|
detected | string[] | Yes | Behavioral patterns identified by the inference engine |
Pattern names are free-form strings assigned by the inference engine. Examples from
production: "rage_clicks", "exit_intent_with_rapid_scrolling",
"price_comparison_between_tiers", "repeated_section_revisit".
5.7 Dimension 6: Intent
{
"level": "high",
"action": "comparison",
"confidence": 0.75
}
| Field | Type | Required | Description |
|---|---|---|---|
level | string | Yes | Intent strength (e.g., "high", "medium", "low") |
action | string | Yes | Inferred action (e.g., "comparison", "purchase", "exit") |
confidence | number (0.0–1.0) | Yes | Confidence in intent classification |
5.8 Dimension 7: Page Content
This dimension gives the consumer awareness of what the user is looking at, not just how they are behaving. A paralyzed buyer hovering over a “2024 Arctic Wolf 3650SUITE” needs different help than one hovering over a “Financing Calculator.”
{
"pageTitle": "2024 Arctic Wolf 3650SUITE | Prosser's Premium RV",
"h1": "2024 Cherokee Arctic Wolf 3650SUITE",
"headings": ["Specifications", "Floor Plan", "Features"],
"tables": [{ "headers": ["Spec", "Value"], "rows": [["Length", "39ft"]] }],
"structuredData": [{ "type": "Vehicle", "name": "Arctic Wolf 3650SUITE", "price": "$59,995" }],
"visibleContent": "Experience the ultimate in fifth wheel living...",
"meta": {
"description": "Shop the 2024 Arctic Wolf 3650SUITE at Prosser's Premium RV"
}
}
| Field | Type | Required |
|---|---|---|
pageTitle | string | null | No |
h1 | string | null | No |
headings | string[] | No |
tables | array of {headers, rows} | No |
structuredData | array of objects | No |
visibleContent | string | null | No |
meta | object | No |
The entire pageContent dimension is optional — a packet may
set it to null. But when present, it transforms the consumer's ability to
generate contextually relevant responses.
5.9 Dimension 8: Global Intelligence Optional
Global intelligence aggregates emotional signals across all active sessions for a tenant. It provides market-level context that individual session data cannot.
{
"evi": 0.73,
"trend": "rising",
"percentile": 85,
"isAnomaly": false,
"anomalySeverity": "none",
"predictedEvi": 0.76,
"predictedDirection": "up",
"predictionConfidence": 0.68,
"contagion": null
}
| Field | Type | Description |
|---|---|---|
evi | number | Emotional Volatility Index (aggregate across sessions) |
trend | string | Current trend direction |
percentile | number | Percentile rank against historical baselines |
isAnomaly | boolean | Whether current conditions are anomalous |
predictedEvi | number | 30-minute EVI prediction |
contagion | object | null | Emotional contagion spread data |
This dimension is optional. It is only available in implementations
that track aggregate emotional state across sessions. A packet may set
globalIntelligence to null.
5.10 Dimension 9: Channel Origin Optional
Channel origin records where the visitor came from before arriving on the site. This is a pre-arrival intent signal — the referring source is a self-selection event that carries information about the visitor's decision stage before the first behavioral event is recorded.
{
"referrer": "rvtrader.com",
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "summer-inventory"
}
| Field | Type | Required | Description |
|---|---|---|---|
referrer | string | null | No | HTTP referrer hostname (e.g., "rvtrader.com", "google.com") |
utm_source | string | null | No | UTM source parameter from inbound URL |
utm_medium | string | null | No | UTM medium parameter (e.g., "cpc", "email", "social") |
utm_campaign | string | null | No | UTM campaign parameter |
Resolution order: utm_source takes precedence over referrer. If
both are null, the session originated from direct navigation. All fields are optional
— the entire channel dimension may be null.
Consumers should use channel origin to inform opening frame and tone, not to mention the source directly. A visitor from a comparison marketplace is already in active evaluation mode — the consumer should skip exploratory framing and engage on specifics. A visitor from a paid social ad is earlier in the funnel and should be treated accordingly.
referrer is the HTTP Referer hostname only
— never the full URL, never a path, never query parameters. UTM values are extracted
from the inbound URL and not re-transmitted beyond the bridge. No PII can be embedded in
these fields.
5.11 Experimental Extension
Packets may include an experimental context for A/B testing intervention strategies:
{
"isExperiment": true,
"experimentContext": {
"experimentId": "exp_001",
"hypothesis": "Empathy-led interventions outperform direct offers",
"vertical": "rv_dealer"
}
}
Consumers should log experimental context alongside outcomes to enable Thompson Sampling or other exploration/exploitation strategies.
5.12 Dimension 10: Provenance Normative (v1.3+)
Provenance records the inference engine's judgment about whether the telemetry batch underlying this packet was produced by a physically-attested human body. Human motion has physical limits: cursor jerk (the third derivative of position) rarely exceeds 104 px/s3; scroll velocity stays in the 50–8000 px/s band; interactive events cannot fire faster than human reaction time (~200 ms). When a batch violates these limits — cursor jerk above 106 px/s3, perfectly periodic event timing, 100+ Hz mouse-event density — the motion was not produced by a human body, and any emotion label drawn from it would be fiction.
{
"physical_plausibility": "plausible"
}
| Field | Type | Required | Description |
|---|---|---|---|
physical_plausibility | string | Yes | One of "plausible" or "declined". The inference engine's verdict on whether the underlying motion was physically possible. Default value: "plausible". A producer that does not implement plausibility classification must still emit "plausible" — the field is required so that any consumer can rely on its presence. |
| Value | Meaning |
|---|---|
"plausible" | The inference engine committed to an emotion classification on motion consistent with a human body. This is the default for every conformant batch. |
"declined" | The inference engine refused to label because the motion was physiologically impossible. Other fields on the packet (emotion.primary, behavior) may be null in this case. The packet is still published — it is substrate evidence of the engine's refusal — but consumers of aggregate signals should exclude it. |
This dimension complements the producer-side human-proof gate defined in
§8. The proof gate runs against raw telemetry before classification, with
finite signals (scroll variance, mouse tremor, speed variance) and hard-reject
guards (synthetic event, burst density). Provenance is a second, semantic layer
applied by the inference engine itself — flagging the small class of
sessions that pass the proof gate but produce motion an inferring model can
recognize as fabricated. The two layers are independent. A session
may pass the proof gate and still produce
"declined" packets; a session that fails the proof gate produces
no packets at all.
Consumer guidance. Producers of Dimension 8 (Global Intelligence)
— the Emotional Volatility Index and its derivatives —
must exclude "declined" packets from
aggregate computation. Single-session consumers (an LLM receiving a packet for
conversational context) may handle
"declined" as if the packet contained no emotion signal: skip the
intervention, do not engage on inferred emotional state.
6. The Two-Axis Taxonomy Normative
A conformant Inference Engine must classify into two orthogonal
axes: an emotion (how the buyer feels) and a behavior
(what the buyer is doing). Both vocabularies are closed. Implementations may
use additional internal labels but must map to a canonical value
when producing EITP packets. Invalid or unrecognized labels must
be emitted as null — the value "unknown" is explicitly
prohibited.
Composite states like analysis_paralysis, decision_paralysis, or
price_sensitivity decompose as (emotion, behavior, context) —
for example (paralysis, analysis, null),
(paralysis, deliberation, null), and (uncertainty, evaluation, "pricing")
respectively. Context (free-text, ≤64 lowercase chars) names what the emotion is
about when a subject exists ("pricing", "payment",
"trade_in", "delivery", …) and is stored separately from
the canonical emotion and behavior values so the triple can be queried independently.
6.1 Emotion Axis — 28 canonical values
The emotion axis describes the buyer's internal state. Loosely grouped for readability; the spec imposes no group structure beyond this listing.
Uncertainty & doubt
| Value | Description |
|---|---|
uncertainty | Ambiguous intent, insufficient signal to classify further. |
hesitation | Approach-retreat pattern near commitment points. Dwell near CTAs. |
indecision | Sustained inability to commit between visible options. |
ambivalence | Equal pull toward multiple options. Alternating without deepening. |
apprehension | Anticipatory tension before a decision point. |
doubt | Active skepticism about a previously held position. |
confusion | Misunderstanding interface or content. Circular motion, rage clicks on non-interactive elements. |
bewilderment | Surprise-flavored confusion in response to unexpected content. |
disorientation | Loss of orientation within site structure. Repeated nav use, back-button bursts. |
Tension & load
| Value | Description |
|---|---|
anxiety | General tension in interaction pattern. Hesitant mouse, frequent pauses. |
overwhelm | Choice or information volume exceeds processing capacity. Rapid shallow scrolling. |
paralysis | Decision lock. Repeated revisits without state change. |
frustration | Repeated failed interactions. Rage clicks, high jerk, erratic velocity. |
rage | Intense frustration with sustained signal. Compound rage-click episodes. |
impatience | Time pressure exceeding session pace. Rapid sequential clicks on slow-responding elements. |
fatigue | Cognitive depletion late in session. Declining engagement amplitude. |
Withdrawal
| Value | Description |
|---|---|
avoidance | Active steering away from a content region. |
withdrawal | Progressive disengagement without explicit exit signal. |
resignation | Acceptance of an unfavorable read (e.g. price out of reach) without abandonment. |
indifference | Low affective amplitude across the session. |
apathy | Sustained indifference with declining interaction. |
Engagement & commitment
| Value | Description |
|---|---|
curiosity | Active exploration, increasing engagement. Accelerating scroll depth, section discovery. |
anticipation | Forward-leaning posture toward a known target outcome. |
excitement | High-amplitude positive engagement. Burst clicks on high-value content. |
determination | Focused task pursuit, low extraneous motion. |
urgency | Time-pressured decision making. High velocity, direct navigation, minimal browsing. |
trust | Established confidence signal — e.g. dwell on testimonials or credentials after a price view. |
satisfaction | Positive outcome signal. Smooth navigation post-action, decreased urgency metrics. |
6.2 Behavior Axis — 10 canonical values
The behavior axis describes what the buyer is observably doing, independent of how they feel about it.
| Value | Description |
|---|---|
exploratory_browsing | Open-ended discovery with no fixed target. |
scanning | Rapid surface read across many items. |
passive_browsing | Low-effort consumption without commitment behaviors. |
deliberation | Active weighing of options — not stuck. Methodical section visits, steady pace. |
contemplation | Single-target consideration with dwell. |
evaluation | Criterion-based assessment against a specific element (price, spec, financing). |
analysis | Systematic decomposition across multiple criteria. Deep scrolling, tab switching, extended sessions. |
comparison_shopping | Cross-option comparison. Tab switching (especially long returns), multi-item viewing, pricing comparison events. |
disengagement | Declining interaction, imminent abandonment. Decreasing events, exit intent signals, long inactivity. |
abandonment | Explicit exit signal — tab close, navigation away, exit intent at high velocity. |
Production Evidence
(emotion, behavior, context) triples.
| Composite (v1.1) | v1.2 decomposition | Conversions | Avg Session | Avg Events |
|---|---|---|---|---|
decision_paralysis | (paralysis, deliberation) | 16 | 38.9 min | 25.1 |
overwhelm | (overwhelm, scanning) | 10 | 19.2 min | 45.6 |
cognitive_load | (overwhelm, analysis) | 9 | 126.5 min | 17.3 |
frustration | (frustration, evaluation) | 6 | 42.0 min | 50.0 |
Key finding: 62% of converting buyers convert under uncertainty or cognitive distress. Distressed buyers work 2.7x harder (measured by median message count) to reach a decision than engaged, confident buyers. This is the core thesis — these are the buyers who need help, and they are invisible without behavioral telemetry.
Extension Rules
Implementations may define additional internal labels for
vertical-specific reads (e.g. "trade_in_anxiety",
"spouse_approval_seeking"). However, when producing EITP v1.3 packets,
emotion.primary must be one of the 28 canonical
emotions and emotion.behavior must be one of the
10 canonical behaviors or null. Vertical-specific context belongs in
emotion.context (free text) or patterns.detected.
When the inference engine reaches for a label outside these enums, the raw output
should be persisted to an out-of-vocabulary record (the reference
implementation calls this emotion_classification_failures) so the taxonomy
can evolve based on real failures rather than hypothetical ones.
7. Inference Pipeline Informative
This section documents the reference implementation's inference approach. It is informative, not normative — alternative implementations may use different inference strategies (rules, statistical models, different AI providers, or hybrid approaches) as long as they produce conformant EITP packets.
7.1 Pipeline Architecture
7.2 Buffering
Raw telemetry is accumulated per session before inference. The reference implementation uses a 5–10 s window depending on signal density. Shorter windows lose temporal context across mouse-pause / scroll cycles; longer windows delay intervention beyond the buyer's natural attention span.
7.3 Single-Tier Inference
The reference implementation calls a single capable frontier model
(Anthropic claude-sonnet-4-6 at time of writing) once per qualifying batch.
It classifies the (emotion, behavior, context) triple, returns
confidence, intensity, trajectory, and an
intervention_needed boolean in one round trip.
v1.1's two-tier "fast triage → capable escalation" design was retired in May 2026. At the platform's volume, the fast-tier model wasn't materially improving cost-per-correct- classification once the rule gate was tightened: most fast-tier resolutions were either batches that didn't need any inference call (now caught by the rule gate) or batches that the capable model would have read differently. Removing the tier-1 hop also removed a class of label-disagreement bugs where the two tiers produced incompatible reads of the same batch.
Cost control now lives upstream of inference: a session-level inference budget (default: 6 capable-model calls per session lifetime), per-minute rate caps, dedup against the previous batch's classification, and a rule gate that suppresses inference on signal-sparse batches.
Implementations may still run a multi-tier pipeline if it pays for itself at their volume. The conformance contract is the packet, not the model count.
7.4 Prompt Caching
The capable-model system prompt is static and cached by the provider's prompt-cache facility. The reference implementation deliberately keeps the static prompt above the provider's minimum cache size (2048 tokens for Anthropic Sonnet at time of writing) so the cache hit pays for itself on every call. Tenant- and session-specific context is appended below the static block to avoid invalidating the cached prefix.
7.5 Knowledge Injection
The inference engine injects proven patterns from a knowledge store into the model's user-message context (below the cached system prompt). Patterns are filtered by tenant match, vertical match, confidence ≥ 70%, and sample size ≥ 5. This gives the model awareness of what has worked before for similar behavioral patterns without invalidating the prompt cache.
7.6 Thompson Sampling
The reference implementation uses Thompson Sampling with Beta distributions for exploration / exploitation of intervention strategies. Beta parameters are derived from historical conversion data — tenant-specific priors with global hierarchical shrinkage fallback (0.5x weight for global priors).
This means the system automatically balances between using proven interventions (exploit) and testing new approaches (explore), converging on optimal strategies as data accumulates.
7.7 Feedback Loop
A 60-second aggregation cycle processes emotional events and produces pattern intelligence:
- Multi-dimensional grouping: emotion × behavior × section × geography × vertical
- Wilson Score confidence: sample-size-aware confidence intervals
- Spike detection: recent 24h count / 7-day baseline > 1.5x
- Anomaly detection: Z-score > 2.0
- Trend direction: increasing (>1.2x), decreasing (<0.8x), stable
Validated patterns (sample size ≥ 10) are injected back into inference prompts, creating a closed loop where the system learns from its own observations.
7.8 Observability
Each inference call should log: the input batch hash, the
cached vs. uncached prompt-token split, the model's full structured output, the
resulting EITP packet, intervention decision, and per-call cost attribution. The
reference implementation persists these to llm_cost_log per
(tenant, session, batch) tuple, which is what makes the rule-gate and
budget thresholds tunable from production data rather than guesswork.
8. Bot Filtering Normative
Bot filtering is normative because — unlike analytics where bot traffic merely inflates numbers — in EITP, bot traffic triggers expensive LLM inference calls. A single bot flood can generate thousands of dollars in API costs. A conformant Inference Engine must implement bot detection.
8.1 Cheap Pre-Filter (Required)
Before any per-session signal analysis, an Inference Engine must apply two zero-cost checks:
- User-agent matching: Reject known crawler, headless-browser, SEO/monitoring, and automation patterns at the bridge layer. ~90% catch rate at near-zero cost.
- IP blocklist: A dynamic, hot-reloadable blocklist of individually confirmed bad actors. Hardcoded IP / ASN ranges should not be used — in practice they collide with legitimate VPN, mobile-carrier, and cloud-preview traffic.
8.2 Human-Proof Gate (Required)
After the pre-filter, sessions must pass a human-proof gate before any LLM inference call is made. A session is assumed bot until one of three physiological signals fires on a single batch. The signals exploit the fact that human hands produce variance that scripts don't reliably reproduce; the targets are small enough to enumerate.
| Proof path | Signal | Default threshold |
|---|---|---|
| Scroll variance | 5+ scrolls in the human velocity range (50–8000 px/s) whose stdDev exceeds 30% of mean. Human scroll coefficient of variation runs 35–60%; bots produce uniform or linearly-interpolated velocities. | session age ≥ 3 s |
| Mouse tremor | 10 mouse events within a 100–500 ms window whose mean jerk > 5 px/s³ AND stdDev between 8 and 5×10&sup6;. Upper bound excludes scripted teleport jumps (10&sup8;+) while admitting ordinary high-DPI cursor motion (10⁴–10&sup6;). | — |
| Speed variance | 10 mouse events with 5+ nonzero speeds, stdDev > 40% of mean, speed range > 200 px/s. Catches fast-cursor humans whose jerk signatures exceed the tremor upper bound. | session age ≥ 2 s |
8.3 Hard-Reject Guards (Required)
Two guards run before the proof paths and override them. Either firing must reject the session.
- Synthetic event guard: The first interactive event (
click,cta_hover,cta_proximity,cta_proximity_enter,circular_motion,tab_switch,long_tab_return) firing within 200 ms of page load is machine-generated. Humans cannot physically interact that fast. Scroll and mouse events are exempt because browsers can fire spurious ones before first render — those are covered by the burst guard. - Mouse burst guard: ≥ 7 mouse events within any 300 ms window during the first 2 s of session age. Physiological tremor at 8–12 Hz produces at most ~6 events per 500 ms at the reference 50 Hz sampling rate; tighter bursts indicate machine speed.
8.4 Why thresholds are tight, not loose
A session that fails to demonstrate human signal is held in a "not-yet-proven" state — the engine waits for the next batch rather than committing to a verdict on too little data. This is deliberately asymmetric: false negatives (real humans temporarily failing to qualify) cost only a delayed first intervention, but false positives (bots admitted to the inference pipeline) cost real LLM dollars per batch. The reference implementation publishes proven-human events on a dedicated NATS subject so downstream consumers can reason about session liveness without re-running the gate.
8.5 Detection Logging
Gate verdicts should be logged with both the verdict and the underlying joint distribution at decision time:
| Field | Type | Description |
|---|---|---|
session_id | string | Session identifier |
verdict | string | "proven_human" | "rejected" | "pending" |
proof_method | string | null | "scroll_variance" | "mouse_tremor" | "mouse_speed_variance" | null |
rejection_reason | string | null | "synthetic" | "burst" | "user_agent" | "ip_blocklist" | null |
metrics | object | Computed scroll / tremor / speed / burst / synthetic features at decision time (mean, stdDev, counts, windows). Persisted unconditionally so the training corpus for future gate revisions captures the full joint distribution, not only the path that short-circuited the verdict. |
event_count | number | Events analyzed in the batch |
session_age_ms | number | Time since session start |
8.6 Future Threat: LLM-driven crawlers
Today's automation doesn't produce physiological signatures — mouse tremor and
scroll-velocity variance remain reliable. Tomorrow's Opus-class models running site
crawlers may. The space of physiological human signal is finite; the gate's thresholds
are designed to evolve as that threat materializes, and the logged
metrics distribution is what makes that evolution data-driven.
9. Consumption Contract Normative
The consumption contract defines how a Consumer — typically an LLM or an application feeding an LLM — uses EITP packets. The goal is to produce responses that feel naturally informed, not surveilled.
9.1 Injection
The EITP packet should be injected into the LLM's system prompt or provided as a tool call parameter. The Consumer should present the packet dimensions in priority order:
- Spatial — where the user is on the page
- Temporal — how long they've been there
- Emotional — what they appear to be feeling
- Behavioral + Intent — what patterns are detected
- Page Content — what they're looking at
- Global Intelligence — market-level context
This priority order reflects what a skilled human salesperson would notice: where someone is standing, how long they've been there, their body language, what they're looking at, and broader market context — in that order.
9.2 Anti-Creepiness Rules
These rules are normative. A conformant Consumer:
- Must not reference mouse movements, scroll behavior, or click patterns in user-facing output
- Must not mention exit intent detection
- Must not say “I noticed you were looking at…” or equivalent surveillance language
- Must not reference dwell time, hover behavior, or proximity events
- Must use language that implies understanding, not observation
- May reference page content naturally (as a human salesperson would know what is on the shelf)
decision_paralysis,
the right response is: “Choosing between these two can feel tough — want me to
break down the key differences?” Not: “I detected you hovering between two options
for 3 minutes.”
9.3 Cooldown Compliance
Consumers must respect these timing constraints:
| Constraint | Default | Description |
|---|---|---|
| Intervention cooldown | 300 seconds (5 min) | Minimum time between proactive interventions for the same session. Per-tenant override via seer_config.intervention_gap_ms. |
| Window suppression | 120 seconds | Suppress proactive interventions when the user has the chat window open |
| Minimum session time | 10 seconds | Grace period before any intervention is allowed |
These defaults prevent the system from being overly aggressive. Consumers may use longer cooldowns but should not use shorter ones.
10. Privacy and Security
10.1 PII Sanitization
Producers must sanitize all text payloads before transmission.
The following PII patterns must be replaced with [REDACTED]:
email addresses, phone numbers, Social Security numbers, credit card numbers, and zip codes.
The specific regex patterns are defined in Section 3.9.
10.2 No Form Values
Form field values must not be transmitted. The form tracker captures only metadata: field type, field name, focus duration, and a boolean indicating whether the field has a value. The actual content the user types is never part of the telemetry stream.
10.3 IP Address Handling
- IP addresses must be injected server-side by the Bridge (Section 4.5)
- IP addresses should be used only for geolocation (city/region) and bot detection
- IP addresses should not be persisted beyond the session lifetime
- IP addresses must not appear in EITP packets delivered to Consumers
10.4 Tenant Isolation
All message subjects must be tenant-scoped. A Bridge must not
allow cross-tenant telemetry. An Inference Engine receiving a batch with a mismatched
tenantId must reject it.
10.5 Consent
EITP does not define a consent mechanism. Implementations should comply with applicable privacy regulations (GDPR, CCPA, etc.) regarding behavioral data collection. The protocol is designed to minimize data collection — no PII, no form values, no keystroke logging — but consent for behavioral telemetry remains the implementer's responsibility.
11. Wire Format Examples
11.1 Telemetry Batch
A complete telemetry batch as published to telemetry.raw:
{
"type": "telemetry_batch",
"sessionId": "sq_1740700800000_k8f2m9x1q",
"tenantId": "org_39lXO2Ptox5W4kHBttbNUfOovN1",
"vertical": "rv_dealer",
"timestamp": 1740700800000,
"pageContent": {
"pageTitle": "2024 Arctic Wolf 3650SUITE | Prosser's Premium RV",
"h1": "2024 Cherokee Arctic Wolf 3650SUITE",
"headings": ["Specifications", "Floor Plan", "Features"],
"structuredData": [{
"type": "Vehicle",
"name": "Arctic Wolf 3650SUITE",
"price": "$59,995"
}]
},
"events": [
{
"type": "mouse",
"timestamp": 1740700800100,
"currentSection": "pricing",
"data": {
"x": 450, "y": 320,
"vx": 12, "vy": -5,
"ax": -80, "ay": 15,
"jx": 200, "jy": -100,
"speed": 13, "jerk": 224
}
},
{
"type": "mouse_pause",
"timestamp": 1740700802500,
"currentSection": "pricing",
"data": {
"duration": 2400,
"x": 455, "y": 318,
"viewport": { "relX": 0.32, "relY": 0.42 }
}
},
{
"type": "price_proximity_enter",
"timestamp": 1740700803000,
"currentSection": "pricing",
"data": {
"distance": 45,
"over": false,
"element_type": "price",
"currency": "$",
"value": 59995,
"isSale": true
}
},
{
"type": "scroll",
"timestamp": 1740700805000,
"currentSection": "specifications",
"data": {
"depth": 45,
"maxDepth": 45,
"velocity": 120,
"direction": "down"
}
}
]
}
11.2 EITP v1.3 Packet
A complete packet as published to seer.{tenantId}.intervention.request.{sessionId}:
{
"sessionId": "sq_1740700800000_k8f2m9x1q",
"tenantId": "org_39lXO2Ptox5W4kHBttbNUfOovN1",
"timestamp": 1740700810000,
"emotion": {
"primary": "paralysis",
"behavior": "deliberation",
"context": "pricing",
"confidence": 0.82,
"intensity": 0.65,
"trajectory": "intensifying"
},
"spatial": {
"currentSection": "pricing",
"visitCount": 3,
"trigger": null
},
"temporal": {
"sessionDuration": 234000,
"timeSinceLastEmotion": 45000
},
"behavioral": {
"scrollDepth": 72,
"clickCount": 4
},
"patterns": {
"detected": [
"extended_dwelling_on_pricing",
"repeated_section_revisit"
]
},
"intent": {
"level": "high",
"action": "comparison",
"confidence": 0.75
},
"pageContent": {
"pageTitle": "2024 Arctic Wolf 3650SUITE | Prosser's Premium RV",
"h1": "2024 Cherokee Arctic Wolf 3650SUITE",
"headings": ["Specifications", "Floor Plan", "Features"],
"structuredData": [{
"type": "Vehicle",
"name": "Arctic Wolf 3650SUITE",
"price": "$59,995"
}],
"visibleContent": "Experience the ultimate in fifth wheel living..."
},
"globalIntelligence": {
"evi": 0.73,
"trend": "rising",
"percentile": 85,
"isAnomaly": false,
"anomalySeverity": "none",
"predictedEvi": 0.76,
"predictedDirection": "up",
"predictionConfidence": 0.68,
"contagion": null
},
"channel": {
"referrer": "rvtrader.com",
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "summer-inventory"
}
}
12. Implementation Guidance Informative
12.1 Building a Minimal Producer
The fastest path to an EITP producer is three tracker modules with batched WebSocket delivery:
- Mouse tracker — position + velocity at 20Hz. Skip acceleration/jerk for v1.
- Click tracker — position, target element, rage click detection.
- Scroll tracker — depth and direction.
- Batch buffer — accumulate events, flush every 3 seconds over WebSocket.
This minimal producer gives an inference engine enough signal to detect: frustration (rage clicks), hesitation (slow mouse near CTAs), engagement depth (scroll), and comparison behavior (scroll direction reversals).
12.2 Building a Minimal Consumer
A minimal consumer parses three fields from the EITP packet and prepends them to the LLM system prompt:
// Minimum viable consumption
const emotionLine = packet.emotion.behavior
? `${packet.emotion.primary} / ${packet.emotion.behavior}`
: packet.emotion.primary;
const context = [
`User state: ${emotionLine} (${packet.emotion.trajectory})`,
packet.emotion.context && `About: ${packet.emotion.context}`,
`Currently viewing: ${packet.spatial.currentSection}`,
`Page: ${packet.pageContent?.h1 || packet.pageContent?.pageTitle || 'unknown'}`,
`Time on site: ${Math.round(packet.temporal.sessionDuration / 60000)} minutes`
].filter(Boolean).join('\n');
// Inject into system prompt
systemPrompt = context + '\n\n' + systemPrompt;
Even this minimal injection transforms the LLM's first response from generic to contextual. The full reference implementation adds priority ordering, anti-creepiness instructions, intervention history, and page content awareness.
12.3 Building a Bridge
A Bridge requires:
- WebSocket server accepting connections from producers
- Message bus client (NATS, Redis Pub/Sub, Kafka, or equivalent) for internal routing
- IP injection — extract
req.socket.remoteAddress(orX-Forwarded-Forbehind a proxy) and inject into the payload before forwarding - Subject whitelist — untrusted clients can only publish to
telemetry.raw - Rate limiter — per-IP or per-session throttling
- Max message size — reject payloads exceeding 50KB
Appendix A: Complete Event Type Catalog
| Event Type | Source | Key Fields |
|---|---|---|
mouse | Mouse Tracker | x, y, vx, vy, ax, ay, jx, jy, speed, jerk |
circular_motion | Gesture Detection | circularity, radius, duration |
direction_changes | Gesture Detection | count, duration, avgSpeed |
mouse_pause | Gesture Detection | duration, x, y, viewport |
mouse_exit | Mouse Tracker | angle, velocity, edge, x, y |
iframe_exit | Mouse Tracker | x, y, iframeSrc |
click | Click Tracker | x, y, target, mouseVelocity, mouseJerk |
rage_click | Click Tracker | count, area |
scroll | Scroll Tracker | depth, maxDepth, velocity, direction |
{kind}_proximity_enter | Proximity Engine | distance, over, element_type, intent, signals |
{kind}_hover | Proximity Engine | over, element_type, meta |
{kind}_proximity | Proximity Engine | distance, element_type |
{kind}_proximity_leave | Proximity Engine | element_type |
pricing_comparison | Proximity Engine | tier_count, tiers |
viewport_resize | Viewport Tracker | width, height, orientation |
viewport_boundary | Viewport Tracker | boundary, scrollDepth |
viewport_approach | Viewport Tracker | edge, distance, velocity, vector |
tab_switch | Tab Tracker | action, awayDuration, totalSwitches |
long_tab_return | Tab Tracker | awayDuration, sessionTime |
form_focus | Form Tracker | type, name, id |
form_blur | Form Tracker | type, name, duration, hasValue |
form_submit | Form Tracker | id, action, method |
text_selection | Selection Tracker | text, length, hasPrice |
text_selection_with_price | Selection Tracker | text, price, length |
Additionally, the 11 proximity element kinds produce 44 templated event strings (4 events × 11 kinds), bringing the total event vocabulary to approximately 63 unique strings.
Appendix B: Proximity Element Selectors
The proximity engine identifies elements using CSS selectors. Implementations may customize these selectors for their domain. The reference implementation uses:
| Kind | Selectors (abbreviated) |
|---|---|
price | [class*="price"], [data-price], .cost, .amount, .msrp, .sale-price |
purchase_button | button:has-text("buy"), button:has-text("add to cart"), [data-action="purchase"] |
checkout_button | button:has-text("checkout"), button:has-text("complete"), .checkout-btn |
subscribe_button | button:has-text("subscribe"), button:has-text("upgrade"), .subscribe-cta |
demo_cta | a:has-text("demo"), a:has-text("trial"), button:has-text("schedule") |
signup_cta | a:has-text("sign up"), button:has-text("register"), button:has-text("get started") |
tier_card | [class*="tier"], [class*="plan-card"], [class*="pricing-card"] |
pricing_nav | a[href*="pricing"], a:has-text("pricing"), nav a:has-text("plans") |
feature_nav | a[href*="features"], a:has-text("features") |
learn_more | a:has-text("learn more"), a:has-text("read more") |
cta | .cta, [class*="cta"], button[class*="primary"] |
Appendix C: Configuration Reference
Default values for all configurable parameters in the reference implementation:
Signal Capture
| Parameter | Default | Unit | Module |
|---|---|---|---|
| Sampling rate | 20 | Hz | Mouse |
| History buffer size | 100 | samples | Mouse |
| Micro-movement threshold | 20 | px/s | Gestures (dwell) |
| Dwell threshold | 500 | ms | Gestures (dwell) |
| Circularity threshold | 0.85 | unitless | Gestures (circular) |
| Circle minimum radius | 20 | px | Gestures (circular) |
| Zigzag direction changes | >6 | count | Gestures (zigzag) |
| Zigzag minimum movement | 30 | px | Gestures (zigzag) |
| Zigzag debounce | 300 | ms | Gestures (zigzag) |
| Rage click threshold | 3 | clicks | Click |
| Rage click window | 500 | ms | Click |
| Scroll debounce | 100 | ms | Scroll |
| Proximity grid cell size | 160 | px | Proximity |
| Proximity enter radius | 140 | px | Proximity |
| Proximity spatial hysteresis | 12 | px | Proximity |
| Proximity time hysteresis | 600 | ms | Proximity |
| Viewport boundary | 100 | px | Viewport |
| Exit vector threshold | 400 | px/s | Viewport |
| Resize debounce | 250 | ms | Viewport |
| Long absence threshold | 30,000 | ms | Tab |
| Field abandonment duration | 2,000 | ms | Form |
| Inactivity threshold | 5,000 | ms | Mouse |
Transport
| Parameter | Default | Unit |
|---|---|---|
| Batch size | 50 | events |
| Flush interval | 3,000 | ms |
| Priority flush threshold | 40 | events |
| Maximum buffer | 500 | events |
| Reconnect base delay | 1,000 | ms |
| Reconnect max delay | 30,000 | ms |
| Reconnect max attempts | 5 | count |
| Keepalive interval | 30,000 | ms |
| Max message size | 50,000 | bytes |
| Max events per second | 500 | count |
Inference & Intervention
| Parameter | Default | Unit |
|---|---|---|
| Buffer flush interval | 10,000 | ms |
| Intervention cooldown | 60,000 | ms |
| Window suppression | 120,000 | ms |
| Minimum session time | 10,000 | ms |
| Bot classification wait | 3 batches | ~30s |
| EITP enrichment timeout | 1,500 | ms |
| Session timeout | 300,000 | ms (5 min) |
EITP v1.3 Specification — May 2026
Published by SentientIQ
Licensed under CC BY 4.0