UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

101 lines (100 loc) • 6.13 kB
/** * ontology/fromSkos — a customer's SKOS concept scheme (JSON-LD, already * parsed) becomes OUR map: `readSkos` is the pure parse, `fromSkos` joins * the host's sources onto it and returns an `OntologySpec` for * `defineOntology` — the ONE shape everything else reads. * * Pattern: reader = adapter. Two pure functions over plain JSON; no I/O, no * registry, no second validation path — the result goes through * `defineOntology` unchanged, which is where an id that is not * identifier-safe, an alias that collides with an id, or a source * nobody declared is refused. * Role: Map. Nothing here infers: a concept is a node typed * `skos:Concept`, its id is the last segment of its IRI, its * meaning is its `definition`, else its `scopeNote`, else its * `prefLabel`; `broader` is `is-a`, `related` is `related`; every * other name and every label in another language is left where it * was. SKOS cannot say which SOURCE holds a term or which TOOL reads * it — the host binds those by hand in `SkosJoin.bind`; a term with * no binding is the honest state "declared, no source holds it". * Emits: N/A. * * THE OWNER'S RULING (2026-09-18): customers already have taxonomies, almost * always as SKOS. The library takes THEIRS in and turns it into OUR map; * readers are adapters, `defineOntology` stays canonical. OWL is out of * scope; a Turtle reader is a follow-up; serving the map back in SKOS * vocabulary waits for the bench. * * REFUSALS — every one an `Error` with a `code` (`ERR_SKOS_…`), the IRI(s) * involved and what was expected; the reader never returns a partial map: * ERR_SKOS_INPUT not a JSON-LD document, or a node that is not an object * ERR_SKOS_NAMED_GRAPH a node that is itself a named graph (`{ "@id", "@graph": [...] }`) — * its concepts would be dropped without a word; flatten the document first * ERR_SKOS_UNTYPED a node carrying SKOS properties with no `@type` * ERR_SKOS_NO_CONCEPTS no `skos:Concept` in the document * ERR_SKOS_NO_LABEL a concept with no prefLabel in the asked language * ERR_SKOS_ID an IRI whose last segment is empty * ERR_SKOS_ID_COLLISION two concepts collapsing to one id * ERR_SKOS_UNKNOWN_CONCEPT a broader/narrower/related target the document does not hold * ERR_SKOS_CYCLE a cycle in `broader` (SKOS forbids it) * ERR_SKOS_SCHEME_MISSING no id or no version, from the join or the scheme node * ERR_SKOS_SCHEME_AMBIGUOUS several scheme nodes and the join does not settle id and version * ERR_SKOS_BIND_UNKNOWN_TERM a `bind` key naming a term the scheme does not hold */ import type { OntologyEdge, OntologyNode, OntologyNodeSource, OntologySource, OntologySpec } from './types.js'; import { type JsonLdNode } from './skosJsonLd.js'; /** A JSON-LD document, already parsed: one node, an array of nodes, or `{ "@context"?, "@graph": [...] }`. */ export type SkosInput = JsonLdNode | readonly JsonLdNode[]; export type SkosErrorCode = 'ERR_SKOS_INPUT' | 'ERR_SKOS_NAMED_GRAPH' | 'ERR_SKOS_UNTYPED' | 'ERR_SKOS_NO_CONCEPTS' | 'ERR_SKOS_NO_LABEL' | 'ERR_SKOS_ID' | 'ERR_SKOS_ID_COLLISION' | 'ERR_SKOS_UNKNOWN_CONCEPT' | 'ERR_SKOS_CYCLE' | 'ERR_SKOS_SCHEME_MISSING' | 'ERR_SKOS_SCHEME_AMBIGUOUS' | 'ERR_SKOS_BIND_UNKNOWN_TERM'; /** The one error the reader throws: a `code`, the IRI(s) involved, what was expected. */ export declare class SkosError extends Error { readonly code: SkosErrorCode; readonly iris: readonly string[]; constructor(code: SkosErrorCode, message: string, iris?: readonly string[]); } /** How to read the document: which language's labels to take (default `'en'`). */ export interface ReadSkosOptions { readonly language?: string; } /** One concept as read: its IRI, its derived id, and the node it becomes (no sources — SKOS has none). */ export interface SkosConcept { readonly iri: string; readonly id: string; readonly prefLabel: string; readonly node: OntologyNode; } /** The pure parse: the scheme's identity when the document carries it, the concepts, the edges. */ export interface SkosScheme { readonly iri?: string; readonly id?: string; readonly version?: string; readonly language: string; readonly concepts: readonly SkosConcept[]; /** `is-a` from `broader`/`narrower` (once per pair), then `related` (once per pair, ends ordered by id), then `footprint:edge` entries — each group sorted, so node order and the broader/narrower spelling never move the hash. */ readonly edges: readonly OntologyEdge[]; } /** What SKOS cannot say and the host binds by hand: sources, and which term is held where. */ export interface SkosJoin { /** The map's id — else the scheme node's last IRI segment. */ readonly id?: string; /** The map's version — else the scheme node's `dcterms:modified` / `owl:versionInfo` / `schema:version`. */ readonly version?: string; /** Which language's labels to take (default `'en'`). */ readonly language?: string; readonly sources: Readonly<Record<string, OntologySource>>; /** term id → the sources holding it; a key naming a term the scheme does not hold is refused. */ readonly bind?: Readonly<Record<string, readonly OntologyNodeSource[]>>; } /** * The pure parse: concepts (sorted by id), edges, and the scheme's identity * when ONE `skos:ConceptScheme` node carries it. Refuses by code; never * partial. Exported so a host can look before it joins, and for tests. */ export declare function readSkos(input: SkosInput, options?: ReadSkosOptions): SkosScheme; /** * The customer's scheme joined with the host's sources → an `OntologySpec`. * The host calls `defineOntology(fromSkos(input, join))`: nothing is * validated twice, and nothing is guessed — a term the join does not bind * has no sources, which is the honest state. */ export declare function fromSkos(input: SkosInput, join: SkosJoin): OntologySpec;