Configuration

September 19, 2026 · View on GitHub

Every tunable lives in src/config.ts. Thresholds are exposed as sliders on the options page and as knobs the eval grid search can move; everything else is a constant you change in code. This page is generated from that file; if they disagree, the file wins.

Thresholds (THRESHOLD_META)

Each entry has a label and help (rendered on the options page), a slider range, and a default. DEFAULT_THRESHOLDS is derived from the defaults; Settings.thresholds stores the user's values merged over them, so a new key gets its default without a migration. The rules that read each key are in how-judgments-work.md.

KeyLabelHelpMinMaxStepDefault
read_now_min_densityRead now: min insight density1–10 display scale.11017
read_now_max_knownRead now: max already-knownP(reader already knows this) must be at or below.010.050.5
read_now_min_goal_fitRead now: min goal fitP(serves reader goals) must be at or above.010.050.6
skip_max_densitySkip: max insight densityAt or below this density, skip unless something else rescues it.11013
skip_min_knownSkip: min already-knownAt or above this, skip.010.050.8
skip_min_sales_pitchSkip: min undisclosed sales pitchAt or above this, skip.010.050.7
skip_min_ai_writtenSkip: min AI-writtenAt or above this AND density below read-now, skip.010.050.85
skip_max_claims_supportedSkip: max claims supportedBelow this, opinion/news is downgraded one step.010.050.3
save_min_minutesSave: min reading/watch minutesRead-now-quality items longer than this become save.360115
min_confidenceMin confidence to show a verdictBelow this the badge shows '?' instead of a label.010.050.4

Some cut-offs in computeVerdict are fixed numbers, not sliders: the rage-bait/advertorial rule needs content_type confidence ≥ 0.6; the long-item save rule needs display density ≥ 5; the Jev tie-break needs jev_verdict confidence ≥ 0.5; a runner-up payload segment needs probability ≥ 0.25.

The options page renders one <input type="range"> per key from this table (src/options/main.ts), and Reset to defaults writes DEFAULT_THRESHOLDS back.

Model and endpoint

ConstantValueNotes
JEV_MODELjev-1.13.0Pinned; thresholds were tuned against it. The response model field is stored on every Judgment.
JEV_ENDPOINThttps://api.typesafe.ai/v1/systemoneThe only judgment endpoint
JEV_MODELS_ENDPOINThttps://api.typesafe.ai/v1/modelsUsed by verifyKey when you save a key

Pinning or moving the model

  1. Check what your account can see: pnpm probe prints the answering model in the response body, and GET /v1/models (with your key) lists the aliases. jev-latest and jev-preview are aliases; versioned ids like jev-1.13.0 are accepted directly.
  2. Change JEV_MODEL in src/config.ts. Do not use an alias in the pin: the whole point is that the thresholds and the eval cache correspond to one version.
  3. Run pnpm eval --no-cache (the on-disk eval cache is keyed by the request hash, which includes the model string, so a plain pnpm eval would also miss, but --no-cache makes it explicit). Compare the report-split numbers with the ones in eval.md.
  4. If the numbers moved, decide whether to adjust defaults by reading the confusion matrix, not by pasting the grid-search output. Record the new model and numbers in CHANGELOG.md and docs/eval.md.

Cached judgments in users' browsers are not invalidated by a model change; they age out after 7 days, or Clear cache on the options page drops them immediately.

Retry policy (JEV_RETRY)

FieldValue
maxRetries2
backoffInitialMs500
backoffMaxMs5,000
backoffJitter0.25 (±25%)
maxRetryAfterMs60,000 (cap on a retry-after header)
timeoutMs15,000 per attempt

Retried: 408, 429, 5xx (including 529), network errors. Never retried: 400, 401, 422. There is no client-side throttle against the account rate limits (250k tokens/s, 1,200 req/min, documented by TypeSafe and subject to change).

Content caps

ConstantValueWhere it applies
MAX_ARTICLE_CHARS24,000Article body sent in page mode (~6k tokens); cut at a sentence end past the halfway mark when possible
MAX_TRANSCRIPT_CHARS24,000Transcript text in page mode, both when fetching and when splitting into segments
MAX_FEED_ITEM_CHARS3,000Prefetched body per feed item (~500 words, enough to reach full depth)
VIDEO_SEGMENTS8Maximum segments a transcript is split into for payload_segment
MIN_ARTICLE_WORDS120Below this the extractor returns nothing and no card appears
MAX_REQUEST_CHARS (questions.ts)256,000Safety net: state + questions are trimmed to this (~64k tokens at 4 chars/token)

Feed mode

ConstantValueWhere it applies
FEED_BATCH_SIZE12Items per Jev call in judgeFeed
FEED_BATCH_DEBOUNCE_MS400Coalescing window before a batch of newly visible feed items is sent
PREFETCH_CONCURRENCY3Parallel link-text fetches in the service worker
GENERIC_MIN (feed.ts)15External links with ≥ 15-char titles needed before the generic adapter activates; also the minimum title length
GENERIC_CAP (feed.ts)150Maximum links the generic scanner returns
Prefetch timeout / body cap (prefetch.ts)8 s / 300 KBPer fetch; HTML content types only, credentials: "omit"

Cache and reader state

ConstantValue
CACHE_TTL_MS7 days
CACHE_MAX_ENTRIES3,000 (oldest by insertion evicted on write)
TOPIC_WINDOW_DAYS30
FEEDBACK_LOG_MAX200 entries
READER_SUMMARY_MAX_CHARS2,400 (JSON length of the summary sent to Jev)

Storage keys (STORAGE_KEYS)

All in chrome.storage.local.

KeyHolds
jev_api_keyThe API key (string)
settingsSettings
reader_stateReaderState
c:<sha256>One cached Judgment with storedAt
cache_index[{ key, at }], oldest first

Default excluded hosts (DEFAULT_EXCLUDED_HOSTS)

Seeded into Settings.excludedHosts on first run; edit the list on the options page. A host matches exactly or as a suffix (. + host).

mail.google.com
outlook.live.com
outlook.office.com
docs.google.com
sheets.google.com
slides.google.com
drive.google.com
notion.so
www.notion.so
localhost

Independently of this list, isExcluded in src/background/router.ts refuses every non-https: URL and every loopback or private-network hostname.

Settings defaults (DEFAULT_SETTINGS in src/background/storage.ts)

FieldDefault
pageModetrue
feedModetrue
feedSites{ hn: true, youtube: true, generic: true }
excludedHoststhe list above
prefetchLinkTextfalse
thresholdsDEFAULT_THRESHOLDS