Documentation
¶
Overview ¶
Package graph turns a raw scroll capture into a content graph: text runs reassembled, chrome separated from content, hidden content quarantined, heading levels inferred from typography rather than tag names, and reading order recomputed from geometry.
Everything downstream -- Markdown, HTML, JSON, the MCP tool surface, the benchmark -- renders from this one structure, so the formats cannot drift apart.
Index ¶
- Constants
- func AdoptHydrationText(g *Graph, payload []string, links []HydrationLink) int
- func AdoptServedText(g *Graph, served []capture.LatentNode) (adopted int, proof int)
- func PlainText(g *Graph) string
- func RoundTo(v, step float64) float64
- type Action
- type Audit
- type Block
- type BlockType
- type Confidence
- type DropCount
- type Field
- type Gap
- type Graph
- type HydrationLink
- type Input
- type LatentBlock
- type Link
- type Media
- type Outcome
- type OutcomeInput
- type Provenance
- type QAPair
- type Region
- type Section
- type Source
- type Stats
- type Status
- type StructuredFact
- type StyleInfo
- type Verification
Constants ¶
const ( // DropSingleChar is a one-character run: a fragment reassembly could not // place. resend.com produced blocks reading "t", "b" and "s". DropSingleChar = "a single character: a fragment left over from reassembly " + "rather than a run of text" // elsewhere on the page, which is the shape of a menu item. // // This is the one worth reading closely when a fact is missing. It is right // on menus and wrong on reference documentation, where the name of the // thing being documented is short, linked, and looks identical to a menu // item. Code and contact links are exempt for that reason; anything else // short and linked is still assumed to be navigation. DropNavLabel = "a short linked run that repeats a link label elsewhere on the " + "page: usually a menu item, and listed under navigation rather than content" // DropTemplateLabel is a short run repeated at least three times, of which // the first occurrence is kept. DropTemplateLabel = "a short line repeated across a card or list template: " + "the first occurrence is kept and the repetitions are not" )
The other three exclusions the prune performs. They went unrecorded for far too long, and the cost of that was not theoretical.
Three separate content losses -- every term in curl's libcurl reference, the only contact address on basement.studio, and the option names on any API documentation page shaped like them -- were all this rule firing, and all three were invisible in the artifact's own account of itself. The audit said nothing had been dropped except a few decorative glyphs. They were found by writing question sets and noticing the answers were missing, which is a filter that catches roughly what a person happens to ask about.
An artifact that reports what it withheld can be checked. One that withholds silently has to be caught by someone asking the right question, and most people will not.
const DropNonLexical = "no letter or digit in the run: a separator, a progress " +
"indicator or decorative glyphs rather than prose"
DropCount is one reason runs were excluded, and how many.
It deliberately carries no sample of the excluded text. An earlier version did, so that the reason could be checked rather than trusted, and the adversarial corpus caught it within one run: the runs being excluded are precisely the hidden ones, and quoting one into the audit section handed the text straight back to the reader through the diagnostic meant to explain why it had been withheld.
Counts and reasons carry the diagnostic value. Anyone who needs to see the text itself can replay a snapshot, where it is labelled for what it is. DropNonLexical is the reason recorded for a run containing no letter and no digit. It is named because the CLI recognises it: it is the one exclusion that can account for a whole page, and a page excluded entirely for this reason is a page whose words are not in the DOM at all.
const LatentTrustMarker = "untrusted-hidden-content"
LatentTrustMarker is the fixed string every latent block carries.
const SchemaVersion = "1.0"
SchemaVersion is bumped when the artifact shape changes in a way consumers must notice.
Variables ¶
This section is empty.
Functions ¶
func AdoptHydrationText ¶
func AdoptHydrationText(g *Graph, payload []string, links []HydrationLink) int
AdoptHydrationText builds blocks from a framework's server-rendered state.
This runs only when the page produced nothing readable of its own. It is a recovery channel in the same sense that canvas OCR is: the answer of last resort on a page that would otherwise be reported empty, never a supplement to a page that rendered.
The ordering caveat is the honest part. Rendered text has a position, so its reading order is measured; these strings have only the order they happen to sit in the payload, which is the order the framework serialised them and not necessarily the order anyone reads them. That is recorded on the artifact rather than smoothed over, because an agent quoting a heading as though it introduced the paragraph after it would be wrong in a way it could not detect.
func AdoptServedText ¶
func AdoptServedText(g *Graph, served []capture.LatentNode) (adopted int, proof int)
AdoptServedText folds text from the served HTML into a rendered artifact, but only where the render itself has proved that text of that kind is shown to visitors.
Why this exists ¶
A scroll-driven page can hold its content in two places at once, and sieve sees both. pear.no ships its whole argument -- the terms, the selectivity, the application, the FAQ -- in the served HTML, inside sections marked aria-hidden with opacity zero until they scrub into view. It builds a different DOM after hydration, mounting those sections only as the viewport reaches them. So the static tier holds the sections the browser never travels far enough to see, and the browser holds the hero and the pillars that are not in the served bytes at all. Each tier is missing exactly what the other has, and sieve used to keep one and discard the other.
Why it is not a hole in the visibility defence ¶
The rule that hidden text is not content is what makes this tool safe to point at the open web, and it is not being relaxed. What is added is a second, independent observation standing behind the first.
The promotion needs proof, and the proof is the render: if text that the served HTML marked hidden was afterwards seen on screen by the browser, then on this page that marking is a reveal state and not a concealment. Pages prove that about themselves, one at a time, in the same run. A page that hides text and never shows any of it proves nothing, and nothing is adopted from it -- which is precisely the injection case, where an attacker's hidden block has no visible counterpart anywhere on the page.
Everything adopted is marked: speculative verification, an explicit flag, and a line in the audit. It is offered as what it is -- text the site served and sieve did not personally watch appear -- and never as observed content.
func PlainText ¶
PlainText renders exactly what a caller receives by default.
It is the basis for the headline token estimate, so it must match the default payload precisely: latent content is excluded by construction, and uncorroborated pixel recoveries are excluded because no default rendering emits them either. A token count that included material no caller receives would misstate the project's central claim in its own favour.
Types ¶
type Action ¶
type Action struct {
ID string `json:"id"`
Type string `json:"type"` // link | form | button
Label string `json:"label"`
Href string `json:"href,omitempty"`
Method string `json:"method,omitempty"`
Fields []Field `json:"fields,omitempty"`
Region Region `json:"region,omitempty"`
// Internal marks links that stay on the same registrable domain.
Internal bool `json:"internal,omitempty"`
Disabled bool `json:"disabled,omitempty"`
}
Action is a link, button or form: the things a visitor can do, which most extraction tools omit entirely.
type Audit ¶
type Audit struct {
// GraphRetention is the share of the text the capture observed that
// survived into the emitted graph.
//
// The name is narrow on purpose. It measures the graph stage, not the
// capture stage: if the sweep missed half the page, this number cheerfully
// reports that all of the remaining half was retained. That is true and it
// is not what "coverage" would be read to mean. True coverage against
// hand-written ground truth is a benchmark number, measured where the right
// answer is actually known, and it lives in the benchmark report rather
// than here.
GraphRetention float64 `json:"graph_retention"`
// ObservedChars and EmittedChars are the raw terms, so the ratio can be
// checked rather than trusted.
ObservedChars int `json:"observed_chars,omitempty"`
EmittedChars int `json:"emitted_chars,omitempty"`
// OrderConfidence is how much to trust the reading order.
OrderConfidence Confidence `json:"order_confidence"`
OrderScore float64 `json:"order_score,omitempty"`
// OrderBasis is how the order was computed: "geometry" when document
// coordinates were meaningful, "checkpoint" when the page scrolled by
// transform and coordinates were not.
OrderBasis string `json:"order_basis,omitempty"`
// OrderAgreement is how much the geometric ordering and the
// first-appearance ordering agree. Both are computed anyway, so comparing
// them is nearly free, and divergence is exactly where reading order goes
// wrong.
OrderAgreement float64 `json:"order_agreement,omitempty"`
// HeadingSeparation measures how cleanly the type scale separated into
// levels. A clean gap between size clusters means the heading inference had
// something real to work with; an overlapping continuum means it guessed.
HeadingSeparation float64 `json:"heading_separation,omitempty"`
HeadingConfidence Confidence `json:"heading_confidence"`
// Dropped counts the runs that were captured but did not become blocks,
// grouped by why.
//
// This is the difference between an artifact that is empty and an artifact
// that explains itself. A page that comes back with nothing is the single
// most alarming output this tool can produce, and without this the user has
// no way to tell "the page really is a canvas" from "every run was excluded
// by a threshold". Both have happened; only one is a bug.
Dropped []DropCount `json:"dropped,omitempty"`
// ReachedBottom is false when a budget stopped the sweep early.
ReachedBottom bool `json:"reached_bottom"`
// FramesBlocked counts cross-origin iframes whose content could not be read.
FramesBlocked int `json:"frames_blocked,omitempty"`
// CanvasesUnrecovered counts canvases that passed the size gate but yielded
// nothing.
CanvasesUnrecovered int `json:"canvases_unrecovered,omitempty"`
// Notes are human-readable limitations discovered during the render.
Notes []string `json:"notes,omitempty"`
}
Audit is the artifact's account of its own reliability.
An artifact that reports its own uncertainty is a categorically different object from one that does not, and no competing extractor ships this.
type Block ¶
type Block struct {
ID string `json:"id"`
Type BlockType `json:"type"`
// Level is the heading level, 1 to 6. Zero for non-headings.
Level int `json:"level,omitempty"`
Text string `json:"text"`
Order int `json:"order"`
Source Source `json:"source"`
// Confidence is the reported bucket. Score is the underlying number, kept
// for calibration work and deliberately not the headline.
Confidence Confidence `json:"confidence"`
Score float64 `json:"score,omitempty"`
// Verified is set only on blocks recovered from pixels.
Verified Verification `json:"verified,omitempty"`
Checkpoint int `json:"checkpoint"`
BBox [4]float64 `json:"bbox"`
Region Region `json:"region"`
// SectionID is the id of the section this block belongs to, which is what
// lets an agent pull one section instead of the whole artifact.
SectionID string `json:"section_id,omitempty"`
// Href is set when the whole block is a link.
Href string `json:"href,omitempty"`
// MediaID links an image or media block to its media entry.
MediaID string `json:"media_id,omitempty"`
// ListOrdered distinguishes numbered from bulleted lists.
ListOrdered bool `json:"list_ordered,omitempty"`
// Style is the typographic evidence the heading inference used. Kept
// because a wrong heading level is the most common extraction complaint,
// and without this there is no way to see why the level was chosen.
Style StyleInfo `json:"style,omitempty"`
// Flags records anything unusual the extraction noticed about this run:
// text hidden by colour, metadata that hit its cap, control characters
// stripped. A consumer that wants to be suspicious has something to be
// suspicious about.
Flags []string `json:"flags,omitempty"`
}
Block is one addressable unit of content.
type BlockType ¶
type BlockType string
BlockType is the shape of a block.
const ( TypeHeading BlockType = "heading" TypeParagraph BlockType = "paragraph" TypeList BlockType = "list" TypeListItem BlockType = "list_item" TypeQuote BlockType = "quote" TypeImage BlockType = "image" TypeMedia BlockType = "media" TypeCode BlockType = "code" TypeTable BlockType = "table" TypeLabel BlockType = "label" )
type Confidence ¶
type Confidence string
Confidence is reported as a coarse bucket, not a decimal.
A wrong number is worse than no number, because people trust numbers more than prose: "0.87" reads as a measurement and invites arithmetic, while "medium" reads as a judgement and invites checking. Until there is a benchmark corpus large enough to calibrate against, the honest output is a bucket. The underlying score is retained for tuning but is not the headline.
const ( ConfidenceHigh Confidence = "high" ConfidenceMedium Confidence = "medium" ConfidenceLow Confidence = "low" )
func Bucket ¶
func Bucket(score float64) Confidence
Bucket converts an internal score to the reported bucket.
type Field ¶
type Field struct {
Name string `json:"name"`
Type string `json:"type"`
Label string `json:"label,omitempty"`
Required bool `json:"required,omitempty"`
Options []string `json:"options,omitempty"`
Pattern string `json:"pattern,omitempty"`
}
Field is one form control a visitor is expected to fill in.
type Gap ¶
type Gap struct {
Label string `json:"label"`
Kind string `json:"kind"`
// Reason says why it was not opened.
Reason string `json:"reason"`
// LatentIDs are the latent blocks this control would reveal, when they are
// already captured.
LatentIDs []string `json:"latent_ids,omitempty"`
}
Gap is a disclosure control whose content was not opened.
An agent that knows a Specifications tab exists can go and get it another way. An agent told nothing assumes the page had no specifications.
type Graph ¶
type Graph struct {
SchemaVersion string `json:"schema_version"`
URL string `json:"url"`
FinalURL string `json:"final_url,omitempty"`
DistilledAt time.Time `json:"distilled_at"`
// ContentHash covers the normalised semantic graph -- block text, type,
// level, order -- and not the serialised output. Trivial variation in
// whitespace or asset URLs then does not churn the hash, and re-distilling
// an unchanged site is genuinely a no-op.
ContentHash string `json:"content_hash"`
Generator string `json:"generator"`
// Outcome is the first thing a caller should read: whether this artifact
// describes the page that was asked for, or something that stood in front
// of it. Placed above the content for the same reason.
Outcome Outcome `json:"outcome"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
Summary string `json:"summary"`
Lang string `json:"lang,omitempty"`
Sections []Section `json:"sections"`
Blocks []Block `json:"blocks"`
Actions []Action `json:"actions"`
MediaAll []Media `json:"media"`
Links []Link `json:"links"`
// Structured holds whitelisted facts lifted from JSON-LD. Never raw.
Structured []StructuredFact `json:"structured,omitempty"`
// FAQ holds question-and-answer pairs published as FAQPage structured data.
//
// They are kept apart from Blocks because their provenance is different:
// nobody saw these words rendered, they were declared. A consumer that
// wants only what a visitor could read can ignore this array; one answering
// a question about the site would be foolish to.
FAQ []QAPair `json:"faq,omitempty"`
// Latent is the quarantine. It is a separate top-level key by design and
// must never be merged into Blocks. See LatentBlock.
Latent []LatentBlock `json:"latent,omitempty"`
// Gaps are disclosure controls whose content was not opened.
Gaps []Gap `json:"gaps,omitempty"`
Stats Stats `json:"stats"`
Audit Audit `json:"audit"`
Provenance Provenance `json:"provenance"`
// contains filtered or unexported fields
}
Graph is the whole artifact.
func Build ¶
Build turns a capture into a content graph.
The stages run in a fixed order because each depends on the last: text must be normalised before anything hashes it, reassembled before type sizes mean anything, type sizes must be known before headings can be inferred, and headings must exist before the document can be cut into sections.
func (*Graph) ContentBlocks ¶
ContentBlocks returns only the blocks that are part of the page's content, in reading order. Latent content is structurally unreachable from here.
func (*Graph) Recount ¶
func (g *Graph) Recount()
Recount refreshes the figures that describe the payload.
Blocks are still added after Build returns: canvas recovery and the 3D scene walk both append text that no DOM walk could have produced. Everything derived from the block list has to be recomputed when they do, and it was not -- so a page whose entire content came from a scene reported the token count of the empty document it started from. igloo.inc, whose whole site is glyph geometry, announced twenty-eight tokens for twenty-three paragraphs and a ninety-four per cent saving it had not made.
The hash is deliberately not recomputed here: it identifies the semantic graph, and the callers that append re-derive it themselves when they are done. Recount is about what the artifact claims of itself.
func (*Graph) SectionBlocks ¶
SectionBlocks returns the blocks belonging to a section, in order.
type HydrationLink ¶
HydrationLink mirrors static.HydrationLink without importing it, so the graph package keeps depending on nothing above it.
type Input ¶
type Input struct {
RequestedURL string
FinalURL string
Merged *capture.Merged
Notes []string
// OriginalBytes is the transfer size of the undistilled page, used for the
// stats that justify the project.
OriginalBytes int64
// OriginalText is the raw page text an unaided agent would have had to
// read, used to estimate the token cost that was avoided.
OriginalText string
// ReachedBottom reports whether the sweep saw the end of the document.
ReachedBottom bool
// EntryGate names an interstitial standing between the visitor and the
// site, when one was detected.
EntryGate string
// Now is injectable so golden tests are not time-dependent.
Now time.Time
// Generator identifies the build, e.g. "sieve/0.1.0".
Generator string
// Provenance carries the render-level facts through unchanged.
Provenance Provenance
// Outcome describes what happened when the page was read: whether it was
// refused, challenged, unhydrated, empty or fine. Every path that builds a
// graph fills it in, because the one thing a caller must never have to
// infer is whether reading worked.
Outcome OutcomeInput
}
Input is everything the builder needs. It is deliberately not the render package's Result: the graph must be buildable from a recorded capture with no browser present, which is what makes golden-file tests and offline bug reproduction possible.
type LatentBlock ¶
type LatentBlock struct {
ID string `json:"id"`
Type BlockType `json:"type"`
Text string `json:"text"`
// Reason is why this text was never rendered.
Reason string `json:"reason"`
// ControlLabel is the accessible name of the widget that would reveal it --
// the tab, the accordion header, the summary. This is the whole point of
// keeping the tier: an artifact can say "there is a section behind a tab
// labelled Pricing" instead of silently omitting it.
ControlLabel string `json:"control_label,omitempty"`
ControlKind string `json:"control_kind,omitempty"`
Region Region `json:"region,omitempty"`
Href string `json:"href,omitempty"`
Level int `json:"level,omitempty"`
// Trust is the marker that survives into every format. It is a constant,
// not a computed field, because a computed field can compute to empty.
Trust string `json:"trust"`
Flags []string `json:"flags,omitempty"`
}
LatentBlock is text that exists in the document but was never rendered.
Read this before touching anything that handles it ¶
This is the quarantine, and it holds the exact material the visibility filter exists to exclude. That is deliberate and it is safe only while it stays in its own box. A collapsed accordion body is content a reader can reach with a click; an off-screen paragraph addressed to whatever AI is reading the page is an attack. They are indistinguishable to any walker, so both are kept and neither is promoted.
Four structural rules keep the box shut, and every one of them is load-bearing:
- Latent content lives under its own top-level key. It is never appended to Blocks, and there is no code path that merges the two.
- It is retrieved by a separate tool, never by a flag on the normal content call. A flag is one typo away from being set by default.
- Every latent block carries its trust marker into every output format, so a reader that receives one always knows what it received.
- It is excluded from the headline token count, or the feature quietly makes the project's own numbers worse for everyone who never uses it.
A test asserts rule 1 on every emitted format. If that test ever fails, the security claim this project leads with is gone, not weakened.
type Link ¶
type Link struct {
Href string `json:"href"`
Text string `json:"text"`
Internal bool `json:"internal"`
Region Region `json:"region,omitempty"`
}
Link is an outbound reference.
type Media ¶
type Media struct {
ID string `json:"id"`
Type string `json:"type"` // image | video | model
Src string `json:"src"`
Alt string `json:"alt,omitempty"`
Caption string `json:"caption,omitempty"`
// Source says where the description came from: the page's own alt text, a
// figure caption, or a vision model.
Source string `json:"source"`
Confidence Confidence `json:"confidence,omitempty"`
Width float64 `json:"width,omitempty"`
Height float64 `json:"height,omitempty"`
// Local is the path inside the artifact's media/ folder, when the asset
// was downloaded.
Local string `json:"local,omitempty"`
// Flags records metadata that hit its cap.
Flags []string `json:"flags,omitempty"`
}
Media is an image, video or model.
type Outcome ¶
type Outcome struct {
Status Status `json:"status"`
// Evidence lists what led to the status, shortest first. It is never empty
// for a status other than ok: a verdict a caller cannot check is a verdict
// they have to trust.
Evidence []string `json:"evidence,omitempty"`
// HTTPStatus is the code the server answered with, recorded whatever it
// was. An agent that knows a read failed because of a 429 stops retrying;
// one that only knows the page was empty keeps going.
HTTPStatus int `json:"http_status,omitempty"`
// BodyExcerpt is the beginning of the response body on an error, which is
// where a proxy or policy filter says who blocked the request and why.
BodyExcerpt string `json:"body_excerpt,omitempty"`
}
Outcome is the machine-readable verdict, with its evidence.
func DecideOutcome ¶
func DecideOutcome(in OutcomeInput, contentBlocks int) Outcome
DecideOutcome derives the status from the signals and the finished graph.
The order is a precedence, not a sequence of guesses: a page that is both refused and empty is refused, because that is the fact that explains the other one and the one a caller must act on.
type OutcomeInput ¶
type OutcomeInput struct {
HTTPStatus int
Body string
Blocked bool
BlockedReason string
// RobotsRefused records a robots.txt disallow, which is a refusal by policy
// rather than by the server.
RobotsRefused bool
// EntryGate is an interstitial that was found and not passed.
EntryGate string
// ShellHTML reports that the served document was judged a shell.
ShellHTML bool
// Rendered reports that a browser ran the page. Without it, an empty result
// cannot be called empty-after-render.
Rendered bool
// TierFellBack reports that a higher tier was chosen and did not deliver.
TierFellBack bool
// TierReason explains that fallback, and is used as the evidence for it.
TierReason string
// SweepTruncated reports that the sweep did not finish the document.
//
// It is evidence rather than a verdict. Long pages routinely end a sweep
// before the bottom, so a status driven by it would mark most of the web
// partial and mean nothing; it is recorded when the artifact is partial for
// a reason that does distinguish it.
SweepTruncated bool
}
OutcomeInput is what deciding a status needs. It is separate from the graph because most of it is known before the graph exists.
type Provenance ¶
type Provenance struct {
// Tier is which rung of the escalation ladder answered: fetch, render,
// sweep or recover.
Tier string `json:"tier"`
// TierReason is the scoring that led to that decision, so a caller can see
// why a page was judged cheap or expensive rather than having to guess.
TierReason string `json:"tier_reason,omitempty"`
// TierScore is the escalation score itself.
TierScore float64 `json:"tier_score,omitempty"`
// TierFellBack reports that a higher tier was chosen and did not deliver,
// so the artifact rests on a cheaper one than the escalator asked for. It
// is the difference between "this page only needed a fetch" and "this page
// needed a browser and did not get a usable one", which read identically in
// the tier field alone.
TierFellBack bool `json:"tier_fell_back,omitempty"`
// TierPinned reports that the tier came from a per-domain memory rather
// than from a fresh judgement. Once a domain escalates it stays escalated,
// so a page near the threshold does not oscillate between runs.
TierPinned bool `json:"tier_pinned,omitempty"`
// Libraries are the animation, scroll and 3D libraries detected.
Libraries []string `json:"libraries,omitempty"`
// NormalizerVersion is part of the content hash. Changing normalization
// invalidates every cached artifact, so it is recorded rather than implied.
NormalizerVersion int `json:"normalizer_version"`
// SchemaVersion of the trace payload.
Trace any `json:"trace,omitempty"`
// Blocked records that the site refused this client.
Blocked bool `json:"blocked,omitempty"`
BlockedReason string `json:"blocked_reason,omitempty"`
// Private marks an artifact produced against an authenticated or
// user-profile session. It is never eligible for a shared cache and never
// included in a diagnostic bundle.
Private bool `json:"private,omitempty"`
// Incomplete marks a partial artifact from progressive emission. It is
// checked at the cache boundary: a partial result must never be stored as
// if it were final.
Incomplete bool `json:"incomplete,omitempty"`
// CorpusBytes and CorpusSaturated describe the confirm-only index used for
// canvas cross-checking. A saturated index makes a negative result weaker
// evidence, and saying so is more useful than pretending otherwise.
CorpusBytes int `json:"corpus_bytes,omitempty"`
CorpusSaturated bool `json:"corpus_saturated,omitempty"`
}
Provenance records how this artifact was produced, in enough detail to reproduce it.
type QAPair ¶
QAPair is one question and its answer, lifted from FAQPage structured data.
Questions and answers get their own shape rather than being flattened into StructuredFacts, because the pairing is the content. "What share of the revenue do you take?" and "It's agreed per partnership" are worth little apart and are exactly what an agent is sent to a page to find.
func ParseFAQ ¶
ParseFAQ lifts question-and-answer pairs out of FAQPage structured data.
This is the one place where reading JSON-LD earns its risk. A page like pear.no publishes its entire FAQ as schema.org Question/acceptedAnswer and renders the same words into a scroll-revealed section that a static read cannot see and a browser has to travel to reach. The structured copy is exact, free, deterministic, and available in the first six hundred milliseconds of a run.
The shape is narrow on purpose: only Question objects, only their name and the text of their acceptedAnswer, both normalised and capped, both marked with their provenance so a consumer can tell them from text a visitor saw. Nothing else in the FAQPage object is read.
type Region ¶
type Region string
Region says which part of the page a block belongs to. Chrome is kept rather than discarded: "what is the navigation hierarchy" is one of the questions the benchmark asks, and a distiller that threw the navigation away could not answer it.
type Section ¶
type Section struct {
ID string `json:"id"`
Title string `json:"title"`
Level int `json:"level"`
// FirstBlock and LastBlock are inclusive block ids bounding the section.
FirstBlock string `json:"first_block"`
LastBlock string `json:"last_block"`
BlockCount int `json:"block_count"`
// Chars is the section's text length, which is what an agent needs to
// decide whether pulling it will fit in its budget.
Chars int `json:"chars"`
// Tokens is an estimate, not a measurement. See the tokens package.
Tokens int `json:"tokens"`
}
Section is a run of blocks under one heading. Sections are the unit the MCP tools page over, so an agent can read the part it needs without pulling the artifact into its context.
type Source ¶
type Source string
Source records where a piece of content came from. Consumers must be able to tell text that was really in the document from text a model inferred by looking at pixels, and that distinction has to survive every format change.
const ( // SourceDOM is text that existed in the rendered document. It is exact. SourceDOM Source = "dom" // SourceStatic is text recovered from the served HTML without a browser. SourceStatic Source = "static" // SourceCanvasFallback is a canvas element's own accessibility fallback // content, authored by the site. Exact, and the cheapest recovery there is. SourceCanvasFallback Source = "canvas_fallback" // SourceCanvasScene is text recovered from a 3D scene graph: node names, // mesh names, embedded metadata. Exact when it works. SourceCanvasScene Source = "canvas_scene" // SourceCanvasOCR is text read off rasterised pixels by optical character // recognition. Deterministic, and it fails loudly rather than quietly. SourceCanvasOCR Source = "canvas_ocr" // SourceCanvasVision is a description generated by a vision model from // rasterised pixels. Plausible, not authoritative. SourceCanvasVision Source = "canvas_vision" )
type Stats ¶
type Stats struct {
OriginalBytes int64 `json:"original_bytes"`
ArtifactBytes int64 `json:"artifact_bytes"`
OriginalTokens int `json:"original_tokens"`
// ArtifactTokens counts the default payload only. Latent content is
// excluded: it is not delivered by default, and counting it would make the
// headline number worse for every caller who never asks for it.
ArtifactTokens int `json:"artifact_tokens"`
// LatentTokens is reported separately so the exclusion is visible rather
// than convenient.
LatentTokens int `json:"latent_tokens,omitempty"`
Checkpoints int `json:"checkpoints"`
RawNodes int `json:"raw_nodes"`
ContentNodes int `json:"content_blocks"`
ChromeNodes int `json:"chrome_blocks"`
LatentNodes int `json:"latent_blocks"`
DroppedNodes int `json:"dropped_nodes"`
}
Stats is the evidence for the project's central claim.
type Status ¶
type Status string
Status is what happened when sieve tried to read the page.
It exists because the worst failure in this category is an agent that cannot tell reading failed. A bot challenge, a login wall and an unhydrated single-page shell all arrive as a valid HTTP 200 carrying valid HTML and no content, and an agent handed that either reports the page as empty or invents something to fill the gap. sieve already wrote the evidence into prose notes, which a human reads and a program cannot: an artifact for a 403 carried the number 403 exactly once, inside a Chrome error string, with no field anywhere saying the request had been refused.
So this is deliberately a small closed set, checked before the content is read, and every value carries the evidence that produced it.
const ( // StatusOK: content was extracted normally. StatusOK Status = "ok" // StatusBlocked: the site refused -- HTTP 4xx or 5xx, robots.txt, or a rate // limit. The artifact describes the refusal, not the page. StatusBlocked Status = "blocked" // StatusChallenge: a bot-protection interstitial answered instead of the // page. Distinct from blocked because it is often passable by other means // and is not a policy decision about this client. StatusChallenge Status = "challenge" // StatusAuthRequired: a login wall stands in front of the content. StatusAuthRequired Status = "auth_required" // StatusSPAShell: the served document is an unhydrated shell and the // browser did not fill it in. This is the failure that most looks like // success: valid markup, correct status code, nothing in it. StatusSPAShell Status = "spa_shell" // StatusEmptyAfterRender: the page rendered and genuinely has no extractable // text. A real answer, and a different one from every case above. StatusEmptyAfterRender Status = "empty_after_render" // StatusPartial: content was extracted, but something was reached for and // missed -- a sweep cut short, an entry screen never passed, a tier that // failed and fell back. StatusPartial Status = "partial" )
type StructuredFact ¶
type StructuredFact struct {
// Type is the schema.org type it came from, e.g. "Organization".
Type string `json:"type"`
// Field is the whitelisted property name.
Field string `json:"field"`
// Value is the normalised, length-capped value.
Value string `json:"value"`
}
StructuredFact is one whitelisted field lifted out of a page's JSON-LD.
func ParseJSONLD ¶
func ParseJSONLD(blobs []string) []StructuredFact
ParseJSONLD extracts whitelisted facts from a page's structured data.
Malformed JSON is not an error worth reporting: a great many sites ship broken structured data and it says nothing about the page's content.
type StyleInfo ¶
type StyleInfo struct {
FontSize float64 `json:"font_size,omitempty"`
Weight int `json:"weight,omitempty"`
Tracking float64 `json:"tracking,omitempty"`
Uppercase bool `json:"uppercase,omitempty"`
Italic bool `json:"italic,omitempty"`
Family string `json:"family,omitempty"`
MaxOpacity float64 `json:"max_opacity,omitempty"`
}
StyleInfo is the rendered typography of a block.
type Verification ¶
type Verification string
Verification records whether a recovered string was confirmed against the text the site actually shipped.
This is what converts an open-ended hallucination problem into a bounded confidence-scoring one. Most canvas headlines exist somewhere in the payload before they become pixels; finding the string there proves the recovery is real without ever using the payload as a source of content.
const ( // VerificationNone means no cross-check applies: the text came from the DOM. VerificationNone Verification = "" // VerificationConfirmed means the recovered string was found in the text // the site shipped. VerificationConfirmed Verification = "confirmed" // VerificationSpeculative means it was not found. The block is excluded // from the default payload. VerificationSpeculative Verification = "speculative" )