@odla-ai/kg 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -287,6 +287,8 @@ interface PersonRow {
287
287
  /** Natural key of a company the person is already linked to — the strongest disambiguator. */
288
288
  companySlug?: string;
289
289
  companyName?: string;
290
+ /** The employer's website. What makes first-party evidence reachable at all. */
291
+ companyWebsite?: string;
290
292
  role?: string;
291
293
  city?: string;
292
294
  region?: string;
@@ -351,6 +353,96 @@ declare function validateLinks(raw: PersonDraft["links"], limit?: number): Perso
351
353
  */
352
354
  declare function personToFragment(person: PersonRow, draft: PersonDraft, retrievedAt?: string): PersonResult;
353
355
 
356
+ /** Where a source sits on the scale that decides how much it counts. */
357
+ type SourceClass =
358
+ /** The organization's own site: team page, staff directory, press release.
359
+ * The subject cannot assert this about themselves, which is what makes it
360
+ * the strongest ordinary evidence. */
361
+ "first-party"
362
+ /** A registry that assigns identifiers: ORCID, SEC EDGAR, a licensing board. */
363
+ | "authoritative"
364
+ /** The person's own profile or site. Real, but self-asserted. */
365
+ | "self-asserted"
366
+ /** Press, directories, anything else. */
367
+ | "third-party";
368
+ /** What reading a source established about the subject. */
369
+ type Corroboration =
370
+ /** Named the subject and tied them to their organization. */
371
+ "confirmed"
372
+ /** Named the subject, but nothing tied it to their work. */
373
+ | "name-only"
374
+ /** Loaded, and mentioned neither. */
375
+ | "absent"
376
+ /** Could not be read: blocked, gone, or refused. */
377
+ | "unreachable"
378
+ /** Not checked yet. */
379
+ | "unchecked";
380
+ /** One dated, classified source and what it corroborated. */
381
+ interface VerifiedSource {
382
+ sourceUrl: string;
383
+ sourceClass: SourceClass;
384
+ /** Which facts this source supported: "employer", "title", "location", … */
385
+ facts: string[];
386
+ /** When it was read. Every fact is a fact *as of* a date. */
387
+ verifiedOn: string;
388
+ corroboration: Corroboration;
389
+ }
390
+ /** How well an identity is established. */
391
+ type Grade =
392
+ /** Enough independent sources, anchored, agreeing, with no contradiction. */
393
+ "high"
394
+ /** Corroborated, but thinly or only by sources the subject controls. */
395
+ | "medium"
396
+ /** One source, or a contradiction, or nothing that settles it. */
397
+ | "unresolved"
398
+ /**
399
+ * Nothing could be read at all.
400
+ *
401
+ * Deliberately distinct from `unresolved`. In a real run 60 of 93 claimed
402
+ * URLs came back unreachable, almost all login walls. Scoring those as "not
403
+ * established" blames a lane for a wall it cannot walk through; scoring them
404
+ * as established would be a lie. Neither is true, so this says so.
405
+ */
406
+ | "unverifiable";
407
+ /** The bar a `high` grade has to clear. */
408
+ interface GradeStandard {
409
+ /** Distinct hosts that must corroborate. */
410
+ minimumSources: number;
411
+ /** Distinct facts that must be corroborated across them. */
412
+ minimumFacts: number;
413
+ /** Classes that can anchor a `high` grade. */
414
+ anchors: readonly SourceClass[];
415
+ /** Whether `medium` also requires an anchor. Measured: two third-party
416
+ * sources agreeing about someone who does not exist is a thing that
417
+ * happens, so anything acted on should usually require one. */
418
+ anchorRequiredForMedium: boolean;
419
+ }
420
+ /** Two independent sources, one first-party or authoritative, three facts. */
421
+ declare const DEFAULT_STANDARD: GradeStandard;
422
+ /** What the grade was based on, so a reviewer can disagree with it. */
423
+ interface GradeReport {
424
+ grade: Grade;
425
+ independentSources: number;
426
+ factsCorroborated: string[];
427
+ hasAnchor: boolean;
428
+ contradictions: string[];
429
+ reason: string;
430
+ }
431
+ /** Two sources are independent when they are not the same site. */
432
+ declare function independentHosts(sources: readonly VerifiedSource[]): string[];
433
+ /**
434
+ * Grade an identity from the sources that were checked.
435
+ *
436
+ * Only sources that corroborated something count toward the grade. One that was
437
+ * read and did NOT name the subject is a contradiction; one that could not be
438
+ * read is neither, which is the whole reason `unverifiable` exists.
439
+ */
440
+ declare function gradeSources(sources: readonly VerifiedSource[], standard?: GradeStandard): GradeReport;
441
+ /** Grades that justify acting on the identity. */
442
+ declare const ACTIONABLE: readonly Grade[];
443
+ /** Whether a grade is good enough to act on. */
444
+ declare function isActionable(value: Grade): boolean;
445
+
354
446
  /** Which model serves each verb (search must be a web-grounded model). */
355
447
  interface EnrichModels {
356
448
  /** Web-search-grounded research model. */
@@ -393,6 +485,325 @@ declare function resolveCounties(ai: Ai, model: string, rows: CompanyRow[], gaze
393
485
  fips: string;
394
486
  }>>;
395
487
 
488
+ /** Normalize for containment testing: fold case, accents, and punctuation. */
489
+ declare function fold(value: string): string;
490
+ /**
491
+ * Whether a folded haystack names a person.
492
+ *
493
+ * Requires every name token, and requires them to sit within a couple of words
494
+ * of each other, which is how names are actually written: "Abbey Chrystal",
495
+ * "Chrystal, Abbey", and "Abbey R. Chrystal" all qualify.
496
+ *
497
+ * Proximity is the load-bearing part, and it was added because of a measured
498
+ * failure rather than in anticipation of one. Requiring only that the tokens
499
+ * appear somewhere on the page matched "Abbey Chrystal" against a staff
500
+ * directory that listed "Abbey Fornier" and "Dale Chrystal" — on a team page
501
+ * most name tokens appear somewhere, so a document-wide match corroborates
502
+ * almost anybody.
503
+ *
504
+ * Single-token names are rejected outright: matching "Deere" against arbitrary
505
+ * page text would corroborate almost anything.
506
+ *
507
+ * @param haystack Page text, already passed through {@link fold}.
508
+ * @param name The person's name, in any casing or accenting.
509
+ */
510
+ declare function mentionsName(haystack: string, name: string): boolean;
511
+ /**
512
+ * Pull a window of text around the person's name, as evidence a reviewer can
513
+ * read without re-fetching the page.
514
+ */
515
+ declare function snippetAround(text: string, name: string, width?: number): string;
516
+ /**
517
+ * Reduce HTML to readable text.
518
+ *
519
+ * Script and style bodies are dropped first: a page's inline JSON often repeats
520
+ * unrelated names, and matching against it corroborates the wrong person.
521
+ */
522
+ declare function stripMarkup(html: string): string;
523
+
524
+ /**
525
+ * Read a URL and return its body, or null if it could not be read.
526
+ *
527
+ * Supplied by the caller rather than implemented here, and deliberately so.
528
+ * Fetched pages are attacker-influenced: a strategy reads whatever a third
529
+ * party chose to serve. Making the reader an explicit dependency means the
530
+ * caller decides what boundary it crosses — in odla that is
531
+ * `@odla-ai/camel`'s artifact ingress, which caps bytes, screens redirects for
532
+ * SSRF, and labels the body Unsafe. kg does not reach for the network itself,
533
+ * so it can never quietly bypass that.
534
+ */
535
+ type PageReader = (url: string) => Promise<string | null>;
536
+ /** What a strategy is given to work with. */
537
+ interface StrategyContext {
538
+ person: PersonRow;
539
+ /** The date this evidence is being gathered on. Every fact is a fact *as of* a date. */
540
+ retrievedAt: string;
541
+ /** Present for strategies that call a model. */
542
+ ai?: Ai;
543
+ models?: EnrichModels;
544
+ /** Present for strategies that read pages. */
545
+ readPage?: PageReader;
546
+ }
547
+ /** What one strategy established. */
548
+ interface Finding {
549
+ strategy: string;
550
+ /** Dated, classified sources — the input the grade is computed from. */
551
+ sources: VerifiedSource[];
552
+ /** Profile and site URLs this strategy proposes. */
553
+ links: PersonLink[];
554
+ /** Human-readable account of what happened, for adjudication. */
555
+ notes: string;
556
+ /** Set when the strategy failed outright, which is not the same as finding nothing. */
557
+ error?: string;
558
+ }
559
+ /**
560
+ * One way of gathering evidence about a person.
561
+ *
562
+ * A strategy never decides whether the person was found. It reports what it
563
+ * saw and how strong the sources were; {@link investigatePerson} grades. That
564
+ * split is the point — it means adding a strategy cannot inflate a verdict, and
565
+ * it means the standard can be raised later and re-applied to stored evidence
566
+ * without paying for the inference again.
567
+ */
568
+ interface Strategy {
569
+ /** Stable identifier, recorded on findings so results stay attributable. */
570
+ name: string;
571
+ /** Whether this strategy can run at all for this subject and context. */
572
+ applicable(context: StrategyContext): boolean;
573
+ run(context: StrategyContext): Promise<Finding>;
574
+ }
575
+ /** The result of investigating one person with a set of strategies. */
576
+ interface Investigation {
577
+ person: PersonRow;
578
+ /** How well the identity is established, per the standard used. */
579
+ grade: Grade;
580
+ /** Why, in enough detail to disagree with. */
581
+ report: GradeReport;
582
+ /** Every source gathered, deduplicated by URL. */
583
+ sources: VerifiedSource[];
584
+ /** Every link proposed, deduplicated by URL. */
585
+ links: PersonLink[];
586
+ /** Per-strategy detail, including strategies that ran and found nothing. */
587
+ findings: Finding[];
588
+ /** Strategies skipped because they were not applicable. */
589
+ skipped: string[];
590
+ }
591
+ /**
592
+ * Run every applicable strategy and grade what they found together.
593
+ *
594
+ * Strategies run concurrently and are isolated from each other: one that throws
595
+ * becomes a `Finding` carrying its error rather than failing the investigation.
596
+ * A search provider being down should cost you that strategy's evidence, not
597
+ * the first-party page another strategy already read.
598
+ *
599
+ * @param context The person, the date, and whichever capabilities are available.
600
+ * @param strategies Which strategies to attempt, in any order.
601
+ * @param standard The bar a `high` grade must clear. Defaults to
602
+ * {@link DEFAULT_STANDARD}: two independent sources, one anchoring, three facts.
603
+ */
604
+ declare function investigatePerson(context: StrategyContext, strategies: readonly Strategy[], standard?: GradeStandard): Promise<Investigation>;
605
+
606
+ /** Paths an organization is most likely to describe its people on. */
607
+ declare const TEAM_PATHS: readonly string[];
608
+ /** The site root, which is the only URL on a domain guaranteed to exist. */
609
+ declare function rootOf(website: string): string | null;
610
+ /**
611
+ * Guessed people-page paths. A fallback only.
612
+ *
613
+ * Guessing produced 67 refusals against 23 successful reads in a real run: most
614
+ * sites do not use these paths, and a 404 teaches nothing about whether the
615
+ * person works there. Following the site's own links is the primary route; this
616
+ * is what remains when a homepage offers no usable navigation.
617
+ */
618
+ declare function candidatePages(website: string, limit?: number): string[];
619
+ /**
620
+ * Extract the links a homepage itself offers toward its people pages.
621
+ *
622
+ * Reading the site's own navigation beats guessing at it on both counts: it is
623
+ * far more likely to resolve, and it lands on the page the organization
624
+ * actually maintains. Same-origin only — an off-site link is not first-party
625
+ * evidence, which is the entire reason this strategy carries weight.
626
+ */
627
+ declare function peopleLinks(html: string, origin: string, limit?: number): string[];
628
+ /** What one organization page said about the person. */
629
+ interface OrgHit {
630
+ url: string;
631
+ named: boolean;
632
+ /** Text around the name, for the reviewer. */
633
+ snippet: string;
634
+ }
635
+ /**
636
+ * Decide whether an organization page establishes the person.
637
+ *
638
+ * Deliberately strict about the name and silent about the title: a team page
639
+ * naming the person is the finding, and inferring their exact title from
640
+ * surrounding prose would manufacture a fact the page never stated plainly.
641
+ */
642
+ declare function judgeOrgPage(name: string, url: string, text: string): OrgHit;
643
+ /**
644
+ * Walk the employer's site looking for the person.
645
+ *
646
+ * Requires `companyWebsite` on the person and a `readPage`; without either it
647
+ * reports itself inapplicable rather than pretending to have checked.
648
+ */
649
+ declare const orgSiteStrategy: Strategy;
650
+
651
+ /**
652
+ * Class a cited source.
653
+ *
654
+ * The employer's own domain is first-party even when a search found it rather
655
+ * than a crawl — what makes evidence first-party is who published it, not how
656
+ * it was reached.
657
+ */
658
+ declare function classifySource(url: string, companyWebsite?: string): SourceClass;
659
+ /**
660
+ * Whether a provider actually grounded a claim in this source, as opposed to
661
+ * merely consulting it.
662
+ *
663
+ * A citation carrying cited text or a text span is attributed: the provider is
664
+ * saying *this sentence came from here*. One carrying only a URL means the
665
+ * source was visited. The difference matters enough to grade differently —
666
+ * consulted-but-unquoted sources are the ones that pad a bibliography without
667
+ * supporting anything in it.
668
+ */
669
+ declare function isAttributed(citation: SearchCitation): boolean;
670
+ /**
671
+ * Research a person with a grounded search, and report the sources rather than
672
+ * the conclusion.
673
+ */
674
+ declare const researchStrategy: Strategy;
675
+
676
+ /** Build the narrow query. Quoting both names is what makes the count meaningful. */
677
+ declare function targetedQuery(person: PersonRow, platform?: string): string;
678
+ /** One candidate profile the search turned up. */
679
+ interface Candidate {
680
+ url: string;
681
+ host: string;
682
+ /** The profile handle, which tells two results apart from one result cited twice. */
683
+ handle: string;
684
+ }
685
+ /**
686
+ * Reduce a citation set to distinct candidate profiles.
687
+ *
688
+ * Deduplicates by (host, handle) rather than by URL. The same profile arrives
689
+ * as `linkedin.com/in/x`, `www.linkedin.com/in/x/`, and again with tracking
690
+ * suffixes; counting those as three candidates turns every confident answer
691
+ * into a false ambiguity.
692
+ */
693
+ declare function candidatesFrom(citations: readonly SearchCitation[]): Candidate[];
694
+ /**
695
+ * Look up one person by a single narrow query.
696
+ *
697
+ * Emits a source only when exactly one distinct profile came back. Emitting one
698
+ * per candidate when the name is ambiguous would manufacture precisely the
699
+ * confidence this strategy exists to withhold — and because the profile is the
700
+ * person's own, it is classed `self-asserted`, which under the default standard
701
+ * cannot anchor a `high` grade by itself.
702
+ */
703
+ declare const targetedProfileStrategy: Strategy;
704
+
705
+ /**
706
+ * How an ontology stores the alias index.
707
+ *
708
+ * kg does not own your ontology — you declare the entity, kg reads and writes
709
+ * it through these names. The alias entity needs a unique natural key and one
710
+ * attribute holding the person's key.
711
+ */
712
+ interface PersonIdentityConfig {
713
+ /** Entity type indexing aliases, e.g. `"person_alias"`. */
714
+ aliasType: string;
715
+ /** Unique natural-key attr on the alias entity. Default `"alias"`. */
716
+ aliasKeyAttr?: string;
717
+ /** Attr on the alias entity holding the person's key. Default `"personKey"`. */
718
+ personKeyAttr?: string;
719
+ /** Entity type of the person the alias points at. Default `"person"`. */
720
+ personType?: string;
721
+ /** Edge label from alias to person. Default `"of_person"`. */
722
+ edgeLabel?: string;
723
+ }
724
+ /**
725
+ * Mint an opaque person key.
726
+ *
727
+ * Random rather than derived, and that is the entire point. Any key computed
728
+ * from the facts — a hash of name plus employer — collides two namesakes at one
729
+ * company and mints a second identity the moment someone changes job. A key
730
+ * that means nothing cannot be wrong about anything.
731
+ *
732
+ * @param random Injectable for tests; defaults to `crypto.randomUUID`.
733
+ */
734
+ declare function mintPersonKey(random?: () => string): string;
735
+ /** Whether a key was minted by {@link mintPersonKey} rather than derived from a name. */
736
+ declare function isMintedKey(key: string): boolean;
737
+ /**
738
+ * The alias for a public profile: durable, because it belongs to the person
739
+ * rather than to their current job.
740
+ */
741
+ declare function profileAlias(kind: PersonLink["kind"], url: string): string | null;
742
+ /**
743
+ * The bootstrap alias for someone with no profile found yet.
744
+ *
745
+ * Employer is part of the key on purpose: without it every namesake resolves to
746
+ * one person, which is the bug this module exists to close. With it, a job
747
+ * change produces a *new* alias rather than a wrong match — the person keeps
748
+ * their key, and the old alias keeps pointing at them.
749
+ *
750
+ * Returns null without an employer. A bare name is not an identity, and
751
+ * indexing one would reintroduce the collision by the back door.
752
+ */
753
+ declare function nameAlias(name: string, companyName?: string): string | null;
754
+ /**
755
+ * Every alias a person is recognizable by, strongest first.
756
+ *
757
+ * Order is what makes resolution correct rather than merely convenient: a
758
+ * profile match is trusted over a name-plus-employer match, so someone who
759
+ * changed jobs still resolves to themselves.
760
+ */
761
+ declare function aliasesFor(person: PersonRow, links?: readonly PersonLink[]): string[];
762
+ /**
763
+ * Resolve aliases to an existing person key.
764
+ *
765
+ * One batched read, then the *caller's* order decides — the first alias with a
766
+ * hit wins, so a profile match beats a name match even when the name row was
767
+ * written first. Returns null when nothing matches, which means mint.
768
+ */
769
+ declare function resolvePersonKey(db: AdminDb, config: PersonIdentityConfig, aliases: readonly string[]): Promise<string | null>;
770
+ /**
771
+ * Rewrite a fragment onto a minted person key and index the aliases that lead
772
+ * back to it.
773
+ *
774
+ * Everything referencing the old key moves with it — edges included — so an
775
+ * affiliation written against the name-derived key does not end up orphaned.
776
+ * The alias rows are what make the *next* pass idempotent: without them a
777
+ * minted key would be re-minted every run, which is worse than the name key it
778
+ * replaced.
779
+ */
780
+ declare function applyPersonIdentity(fragment: GraphFragment, from: string, to: string, aliases: readonly string[], config: PersonIdentityConfig): GraphFragment;
781
+
782
+ /** What identifying a person settled on. */
783
+ interface PersonIdentity {
784
+ /** The opaque key the person is stored under. */
785
+ personKey: string;
786
+ /** True when nobody matched and a fresh key was minted. */
787
+ minted: boolean;
788
+ /** The aliases now indexed against that key, strongest first. */
789
+ aliases: string[];
790
+ /** The fragment, rekeyed onto `personKey` with the alias rows appended. */
791
+ fragment: GraphFragment;
792
+ }
793
+ /**
794
+ * Give a person a stable opaque key, reusing the one they already have.
795
+ *
796
+ * Aliases found by this pass are indexed too, not just the one that resolved.
797
+ * That is how someone graduates off the fragile name-plus-employer alias: the
798
+ * first pass that finds their LinkedIn attaches it to the key they already
799
+ * have, and every later pass — including after they change jobs — resolves on
800
+ * the profile instead.
801
+ *
802
+ * @param fragment A fragment keyed on `person.slug`, as `personToFragment`
803
+ * produces. Its person node and every edge touching it are rekeyed.
804
+ */
805
+ declare function identifyPerson(db: AdminDb, config: PersonIdentityConfig, person: PersonRow, links: readonly PersonLink[], fragment: GraphFragment): Promise<PersonIdentity>;
806
+
396
807
  /**
397
808
  * The LLM seam, now a thin adapter over @odla-ai/ai (which was itself
398
809
  * generalized from this package's original Provider). Three verbs stay:
@@ -703,4 +1114,4 @@ declare function htmlToText(html: string, maxChars?: number): string;
703
1114
  /** Fetch a URL and return readable text. Best-effort: returns "" on any failure. */
704
1115
  declare function fetchText(url: string, maxChars?: number): Promise<string>;
705
1116
 
706
- export { type AttrSpec, type AttrType, COMMIT_STEP_CONFIG, type CompanyRow, type Connector, type ConnectorConfig, type ConnectorContext, type ConnectorJob, type ConnectorLens, type ConnectorReport, type ContextConfig, type EdgeSpec, type EnrichModels, type EntitySpec, type Evidence, type ExportEntitySpec, type ExportSpec, type ExtractCall, ExtractResult, FACTS_EXTRACT_SYSTEM, FACTS_EXTRACT_TOOL, FACTS_SEARCH_SYSTEM, type FactsDraft, GraphFragment, IO_STEP_CONFIG, type KVLike, LLM_STEP_CONFIG, LOCATE_SYSTEM, LOCATE_TOOL, type LlmConfig, type LlmEnv, MODEL_TIERS, type MergeRule, type NaturalKeys, type Ontology, PERSON_EXTRACT_SYSTEM, PERSON_EXTRACT_TOOL, PERSON_SEARCH_SYSTEM, type PersonDraft, type PersonEnrichment, type PersonLink, type PersonLinkKind, type PersonResult, type PersonRow, type PipelineDeps, type Provider, type ProviderFamily, RawItem, type SearchCall, type SourceConnector, type SourceEntry, type StepConfig, type StepLike, Store, Usage, type Verdict, type Verification, WRITABLE_VERDICTS, type WriteCall, allJobs, asStepLike, canonicalizeFragment, connectorIds, countyKey, createProvider, enabledSources, enrichCompany, enrichPerson, exportFragment, extractJson, extractJsonArray, factsToFragment, fetchText, fetchUrl, findConnectors, fragmentToOps, getConnector, getProvider, htmlToText, humanAttrsOf, identityKey, ingestFresh, isHttpUrl, isLikelyPersonName, isVerdict, isWritable, jobsForHour, locatePrompt, locationFragment, makeContext, mergeAttrs, mergeRulesOf, mergeScalar, naturalKeysOf, normalizeUrl, normalizeVerification, personPrompt, personToFragment, resilientStep, resolveCounties, resolveName, searchEntities, slugify, toSerializedSchema, urlHash, validateLinks, writeFragment };
1117
+ export { ACTIONABLE, type AttrSpec, type AttrType, COMMIT_STEP_CONFIG, type Candidate, type CompanyRow, type Connector, type ConnectorConfig, type ConnectorContext, type ConnectorJob, type ConnectorLens, type ConnectorReport, type ContextConfig, type Corroboration, DEFAULT_STANDARD, type EdgeSpec, type EnrichModels, type EntitySpec, type Evidence, type ExportEntitySpec, type ExportSpec, type ExtractCall, ExtractResult, FACTS_EXTRACT_SYSTEM, FACTS_EXTRACT_TOOL, FACTS_SEARCH_SYSTEM, type FactsDraft, type Finding, type Grade, type GradeReport, type GradeStandard, GraphFragment, IO_STEP_CONFIG, type Investigation, type KVLike, LLM_STEP_CONFIG, LOCATE_SYSTEM, LOCATE_TOOL, type LlmConfig, type LlmEnv, MODEL_TIERS, type MergeRule, type NaturalKeys, type Ontology, type OrgHit, PERSON_EXTRACT_SYSTEM, PERSON_EXTRACT_TOOL, PERSON_SEARCH_SYSTEM, type PageReader, type PersonDraft, type PersonEnrichment, type PersonIdentity, type PersonIdentityConfig, type PersonLink, type PersonLinkKind, type PersonResult, type PersonRow, type PipelineDeps, type Provider, type ProviderFamily, RawItem, type SearchCall, type SourceClass, type SourceConnector, type SourceEntry, type StepConfig, type StepLike, Store, type Strategy, type StrategyContext, TEAM_PATHS, Usage, type Verdict, type Verification, type VerifiedSource, WRITABLE_VERDICTS, type WriteCall, aliasesFor, allJobs, applyPersonIdentity, asStepLike, candidatePages, candidatesFrom, canonicalizeFragment, classifySource, connectorIds, countyKey, createProvider, enabledSources, enrichCompany, enrichPerson, exportFragment, extractJson, extractJsonArray, factsToFragment, fetchText, fetchUrl, findConnectors, fold, fragmentToOps, getConnector, getProvider, gradeSources, htmlToText, humanAttrsOf, identifyPerson, identityKey, independentHosts, ingestFresh, investigatePerson, isActionable, isAttributed, isHttpUrl, isLikelyPersonName, isMintedKey, isVerdict, isWritable, jobsForHour, judgeOrgPage, locatePrompt, locationFragment, makeContext, mentionsName, mergeAttrs, mergeRulesOf, mergeScalar, mintPersonKey, nameAlias, naturalKeysOf, normalizeUrl, normalizeVerification, orgSiteStrategy, peopleLinks, personPrompt, personToFragment, profileAlias, researchStrategy, resilientStep, resolveCounties, resolveName, resolvePersonKey, rootOf, searchEntities, slugify, snippetAround, stripMarkup, targetedProfileStrategy, targetedQuery, toSerializedSchema, urlHash, validateLinks, writeFragment };