npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@framers/agentos-ext-public-sources

v0.2.2

Published

Public sources for AgentOS citation checks: English Wikipedia's search and page HTML as attributed passages

Readme

@framers/agentos-ext-public-sources

Public sources for AgentOS citation checks. AgentOS's CitationVerifier grades a claim against the passages it is given; this library fetches those passages from a public source and keeps their attribution. It ships English Wikipedia's provider behind a PublicSourceProvider interface: a search by a short topic phrase, a read of a hit's page HTML by its key, and the article as blocks of plain text with the page's own title, the reference markers, maintenance tags, tables, figures, quotations, code, mathematics and navigation removed.

citationSources(document, cut) turns a fetched article into passages, one for each sentence your cut finds in each block. A passage is a VerificationSource (content, title, url) that also carries the site, the article's key, its revision, the time it was read, its licence, and the sentence's block and offsets. The library holds no sentence rule of its own: pass the one the rest of your pipeline uses, so the claims and the passages are cut the same way.

Installation

npm install @framers/agentos-ext-public-sources

It needs Node.js 22 or later, as @framers/agentos does. Its one runtime dependency is htmlparser2; @framers/agentos is a peer.

The example below takes its sentence rule, sentenceSpans, from the values entry of the grounding guard, a package of its own. Install it beside this one to run the example as written, or pass any function that answers a block's sentences as offsets:

npm install @framers/agentos-ext-grounding-guard

Example

import { citationSources, SourceLimiter, WikipediaSourceProvider } from '@framers/agentos-ext-public-sources';
import { sentenceSpans } from '@framers/agentos-ext-grounding-guard/values';

const wikipedia = new WikipediaSourceProvider({
  client: { name: 'MyApp', version: '1.2.0', contact: 'https://example.org/contact' },
  limiter: new SourceLimiter(), // one for every caller on this host: Wikimedia's limits apply per client
});
const found = await wikipedia.search('Treaty of Versailles', { limit: 2 });
if (found.kind === 'hits' && found.hits[0]) {
  const page = await wikipedia.read(found.hits[0]);
  if (page.kind === 'document') {
    const passages = citationSources(page.document, sentenceSpans); // VerificationSource[] with attribution
  }
}

Reading any page

htmlToBlocks(html, { mode: 'page' }) reads a page that is not an encyclopedia article, such as a page a citation names. It keeps the tables, figures, quotations, code and notes the page shows, and makes each table cell, caption, quotation and code block a block of its own, so two cells never read as one sentence. It still drops the page's own title, scripts, styles, templates, inline SVG, mathematics, elements with the hidden attribute, and Wikipedia's reference markers, maintenance tags, reference lists and navigation boxes. The article mode, the default, is the one the Wikipedia provider reads with.

import { htmlToBlocks } from '@framers/agentos-ext-public-sources';

const html = '<table><tr><th>Plan</th><td>$20 per month</td></tr></table>';
htmlToBlocks(html, { mode: 'page' }); // ['Plan', '$20 per month']
htmlToBlocks(html); // []: the article mode drops tables

Outcomes

search answers one of:

  • { kind: 'hits', hits }: at most limit hits (2 by default), each with a key and a title;
  • { kind: 'limited' }: the limiter refused, or your onSend did, and nothing was sent;
  • { kind: 'failed', status }: the HTTP status when an answer came, null for a transport failure, the deadline, or a phrase no address can carry (one holding half of a surrogate pair), for which nothing is sent.

read answers { kind: 'document', document }, { kind: 'limited' }, or { kind: 'skipped', status } for an answer that is not 200 HTML, one over the size caps, a transport failure, the deadline, or a key no address can carry. Neither method throws for a refusal or a failure; aborting your own signal throws its reason.

onSend runs once a search has its slot and before it is sent. Throw new LimiterRefused('caller') from it to hold searches to a quota of your own; the search then answers limited with nothing sent and none of the limiter's starts spent. The limiter stamps a search's start when the search is sent, after onSend has settled, so an onSend that takes a while never brings two requests closer than the spacing. A search can still answer limited after onSend has run, with nothing sent: when a 429 or a 503 blocked the limiter meanwhile, or when the spacing would take its wait past the 2 seconds (waitMs); the time onSend takes is not counted as waiting.

Wikimedia's limits and the User-Agent

Every request runs through a SourceLimiter: at most 3 at once, starts at least 250 ms apart (under 5 a second), at most 200 starts in any 60 seconds, a wait of at most 2 seconds for a slot or the spacing, and, after a 429 or a 503, a block until Retry-After has passed (5 seconds when the header is missing, an hour at most). The 3 at once, the 5 a second, the 200 a minute and the 5 seconds are what Wikimedia's rate limits and robot policy give an unauthenticated client with a compliant User-Agent; the 2-second wait and the hour's ceiling are this library's own.

The limits apply per client, and a limiter counts only the requests of its own process. A provider makes a limiter of its own when none is passed, so give every provider in a process the same one, and when several processes on one host read Wikipedia, send their requests through one of them.

The provider sends the User-Agent <name>/<version> (<contact>), the form of Wikimedia's User-Agent policy with its library part left out, as the policy allows. Give your client's own name and version and a contact the policy accepts: a website, an email address or a wiki user. Every request also sends Accept-Encoding: gzip, as the robot policy asks, follows no redirect and sends no cookie.

Sizes and the deadline

| Option | Default | What it bounds | |---|---|---| | timeoutMs | 3,000 | each request, from sending it to reading its body | | searchMaxBytes | 65,536 | a search answer's body | | pageMaxSentBytes | 1,500,000 | the content-length a page answer declares | | pageMaxBytes | 6,000,000 | a page's body once unzipped | | textBudget | 300,000 | the characters of text kept from a page |

A page answer may declare no length (a chunked one never does), so pageMaxBytes is the cap that bounds every read.

Attribution

Wikipedia's text is under CC BY-SA 4.0, and every document and passage carries it as licence (WIKIPEDIA_LICENCE). Wherever you show a passage, show with it:

  • the sentence, as content holds it;
  • a link to the article (url), named by its title;
  • "CC BY-SA 4.0", linked to licence.url;
  • that the text was modified: the library removes reference markers and maintenance tags and collapses white space, and CC BY-SA 4.0 asks a reuser to "indicate if You modified the Licensed Material" (section 3(a)(1)(B) of its legal code).

Wikipedia's page on reusing its content describes the attribution in full. revision and readAt name the exact text a passage came from.

The page path

The provider reads page HTML from /api/rest_v1/page/html/{key} (PAGE_PATH), the cached interface the robot policy names. RESTBase, the service behind it, is being deprecated: pass pagePath: '/w/rest.php/v1/page/{key}/html' to read the same HTML from MediaWiki's own REST API. A read uses the search hit's key, so it follows no redirect; a title that redirects answers 307 at both paths, and the read answers skipped.

API

| Export | What it is | |---|---| | PublicSourceProvider | The interface a public source implements: site, search(phrase, options), read(hit, options) | | WikipediaSourceProvider | English Wikipedia's provider, or another wiki's through origin | | SourceLimiter, LimiterRefused, retryAfterMs | The limiter, its refusal and the Retry-After reading | | htmlToBlocks(html, { budget, mode }) | HTML as blocks of plain text, white space collapsed, in NFC: an article's prose (article, the default) or everything a page shows (page) | | citationSources(document, cut) | A fetched article as passages with their attribution and place | | wikipediaUserAgent, articleUrl, WIKIPEDIA_LICENCE, SEARCH_PATH, PAGE_PATH | The User-Agent, the article's address, the licence and the paths the provider uses | | SourceHit, SourceDocument, CitationSource, SourceLicence, SearchOutcome, ReadOutcome, CallOptions, WikipediaOptions, WikipediaClient, LimiterOptions, BlockMode, HtmlToBlocksOptions | The types of the hits, documents, passages, outcomes and options |

License

Apache-2.0