"Lesson 6 zooms into the JSON object that flows through every part of ChromeEmbed: the host-context payload. Every field justified, every field a contract the panel iframe relies on."
The Full Shape
The content script's extractContext() returns:
{
type: 'pippa:host-context',
payload: {
host_kind: 'web-page',
snapshot_at: '2026-05-16T01:23:45.000Z',
host_id: chrome.runtime.id,
source: location.href,
human_label: document.title || location.hostname,
favicon: faviconHref(),
selection,
viewportText,
readableText: viewportText, // compat alias
wordCount,
viewport: { width, height, scrollX, scrollY, devicePixelRatio },
scrollPosition: { y: scrollY, ofPageHeight: pageHeight },
language: document.documentElement?.lang || navigator.language || '',
frameUrl: location.href,
isTopFrame: window.top === window
}
}
The Identity Fields
host_kind— always 'web-page' for ChromeEmbed. The PIPPA-EMBEDS framework expects this; Adobe-embed would be 'adobe-app', Calendar would be 'gcal-event', etc. Discriminator for downstream rendering.host_id— the extension's runtime id. Lets cwkPippa distinguish messages from different installed instances (multi-Chrome-profile setups).source— the canonical URL the user is on. cwkPippa surfaces this as 'capturing from example.com/article'.human_label— fallback-chained title. Used as the chat thread label.favicon— for UI affordance.
The Content Fields
selection— current highlighted text (or cached selection from up to 5 minutes ago).viewportText— the text inside the user's current viewport, walker-extracted and spatially sorted (Lesson 3).readableText— alias for viewportText. Older cwkPippa code paths read 'readableText'; the alias means ChromeEmbed doesn't break them. Plan: deprecate when cwkPippa migrates fully to 'viewportText'.wordCount— quick metric for cwkPippa UI ('1,247 words in view').
The Viewport Fields
viewport: { width, height, scrollX, scrollY, devicePixelRatio }— physical-pixel measurements of the user's window. Lets cwkPippa adapt UI to small viewports or hi-DPI displays.scrollPosition: { y, ofPageHeight }— where the user is in the page vertically, plus total page height. cwkPippa can show 'you are 60% through this article.'
The Metadata
snapshot_at— ISO timestamp. Lets cwkPippa display 'context from 5 seconds ago.'language— useful for cwkPippa to format responses in matching locale.frameUrl,isTopFrame— for the merge logic in the SW (Lesson 2). Sub-frame pushes carry these so the SW can decide whether to override the top-frame snapshot.
Why a Schema, Not 'Just Send the DOM'
The discipline of a fixed payload shape pays back when:
- Other embeds compose. The PIPPA-EMBEDS framework expects every embed (Chrome, Adobe, Mail, Calendar) to produce host-context with the same field names. cwkPippa renders them uniformly.
- Versioning. If you ever need to bump payload v1 → v2, you can add a
version: 1field today and check it tomorrow. - Compatibility. The
readableTextalias is exactly the kind of migration aid a fixed schema enables. Without a schema, every consumer would have to guess. - Observability. When cwkPippa's logs show a context payload, you can read it like a structured record instead of guessing what fields exist.
What's NOT in the Payload
Deliberately absent:
- Full document HTML — way too large and loses the viewport semantics.
- Screenshot — costs storage, screenshot crops can be a future field.
- Tab history — outside ChromeEmbed's scope; cwkPippa already has its own conversation history.
- User identity — Pippa knows who you are from the cwkPippa session, not from extension context.
Adding fields is cheap when justified by a feature; adding them speculatively makes the schema noisy and the bridge slower.
One payload shape, every field justified, every field a contract. The schema is what makes ChromeEmbed composable with other embeds; without it, each embed reinvents 'context' and cwkPippa has to special-case all of them.
The PIPPA-EMBEDS framework angle. ChromeEmbed v0.1 is the first PIPPA embed. The host-context schema it ships is the proposed contract for future embeds (Adobe, Mail, Calendar, IDE). cwkPippa renders them uniformly; the embed's only job is to populate the schema fields from whatever its native host provides. That's the win — each new embed is a content-script-and-bus shape with one payload format, not a from-scratch UI.