@vortex-os/ontos
v0.4.1
Published
Add-on — a lightweight ontology of your operational world (objects, typed relations, multi-hop traversal) built from VortEX records, layered on @vortex-os/base. Phase 1 is text-first and sqlite-backed; Phase 2 adds agent-mediated extraction from records A
Readme
@vortex-os/ontos
A lightweight ontology of your operational world — objects, typed relations,
and multi-hop traversal — built from your VortEX records, layered on
@vortex-os/base. It is a distinct add-on from @vortex-os/memory-extended:
memory-extended recalls what was said; ontos models what is and how it
connects, and acts on recurring situations.
Sibling add-ons, one line: remember (
memory-extended) · perceive (computer-use) · model (ontos).
Status — Phase 2 stable
Phase 1 is text-first: the ontology is built only from existing markdown
records (the failure ledger, decision-log, memories, runbooks, [[wikilinks]]),
with no screen perception and no background daemon (deferred — Phase 1 had to prove
its value first; Phase 2 became conversation extraction, see below). Storage is sqlite (better-sqlite3) with recursive-CTE
traversal — no graph DB (TypeDB/Neo4j) and no formal OWL/RDF reasoner.
Phase 1 (text-first graph) is the foundation below; Phase 2 (conversation
extraction + cross-model verification) is the section after it. Implemented and
verified (typecheck/build/verify 146/146; confirmed on a real instance — e.g.
literal-control-bytes → 6 occurrences, guard fires):
sqlite/OntosStore— schema; idempotent object/relation upsert (relId =sha256(source⏎relation⏎target)); alias-based identity resolution; hop-capped (≤ 3) cycle-guarded recursive-CTE traversal; recurrence count;clear()+ a transaction wrapper for a true rebuild. Plus therelation_proposalstable (kept SEPARATE from the trusted graph), additive migration, andconfirmProposal/rejectProposal/proposalsFor.extract/— the deterministic extractor (§4): failure ledger → recurring-failure subgraph (recurs-asoccurrence→class,covered-by→rule); decision-log →decision:objects (+ wikilink candidates, now STORED as a typing worklist with their anchor sentence rather than counted and dropped); memory →rule:objects; runbooks →action:objects (resolved-bytargets). A record'srelations:frontmatter field is read into TYPED, durable relations (applies→rule,builds-onlineage,sibling-ofrule↔rule family/boundary,enforced-by→the guard or runbook that carries a rule or decision out,relates-togeneric) — validated for domain/range at extraction, with rejected entries and orphaned targets surfaced by the rebuild — so the decision graph carries the cross-references records actually make, not just wikilink candidates.enrich/— agent-mediated LLM enrichment: build an extraction request, validate the agent's proposals (vocab + domain/range), tier them (deterministic HIGH / cross-model MEDIUM / self LOW / disagreed rejected), and store them as PROPOSALS only. A proposal never reaches the trusted graph until human-confirmed. Economy workers use a role policy rather than pinned model IDs:resolve-ontos-model --backend claude|codexresolves the current low-cost model from the shipped policy and the active Codex account's model catalog, and fails closed instead of falling through to an unknown expensive default.writeback(§5-bis) — on confirm, persist the relation to the source record'sontos:frontmatter block via a format-preservingparseDocumentround-trip (idempotent; malformed/colliding/ambiguous frontmatter untouched), so confirmed knowledge syncs across machines via git. The entry is self-contained (source+target node ids), so a deterministicrebuild-ontosre-derives it — confirmed record-sourced relations survive a rebuild.actions/—recurringFailureGuard(the §5A fast-proof; a hit carries the remedy too —coveredByrules /resolvedByactions joined from the trusted graph),why(§5B decision archaeology, evidence-first ≤ 3 hops), andproposalHints(surface unconfirmed links, hedged — actions read the trusted graph only). Hook-latency readers open the index withOntosStore.openReadonly(never creates the db, never waits on a writer).scripts/—rebuild-ontos(read-only deterministic rebuild from_failures/decision-log/_memory/runbooks);enrich-ontos --emit|--ingest(the agent I/O wrapper) and--gc(reconcile orphans after rename/delete). All three resolve their data dir the same way:--data-dirwins, thenVORTEX_REPO_ROOT, then the nearest enclosing instance — the closest ancestor of the cwd carrying.agent/vortex.json— and only then the old<cwd>/data. Run from a project folder inside an instance they used to point at that folder instead of the instance, finding nothing.- CLI —
vortex ontos check | why <target>via@vortex-os/base(optional; base works without ontos installed).
Deferred (later): identity embedding-candidates (optional reuse of
@vortex-os/memory-extended's embedder when present), broader object coverage
(Component extraction beyond runbooks), session-start ambient surfacing, and
Phase 2 (vision / daemon).
Design: the concept and Phase 1 build spec live in the maintainer's VortEX instance (Codex-reviewed, 2 rounds).
Phase 2 — conversation extraction + cross-model verification
Phase 2 mines closed session transcripts (not just curated records) for ontology candidates, and proposes new objects so a conversation's substance is captured — not only relations between pre-existing objects. Everything stays a human-confirmed proposal, kept separate from the trusted graph.
Safe by construction. Conversation candidates go to
relation_proposals/object_proposals, never the trusted graph, until you confirm. Conversation trust is capped at MEDIUM (a matching record may be derived from the same conversation — circular corroboration), so HIGH stays human-only.Anchored. Each candidate cites a verbatim quote from a named message, validated against the transcript (forged/paraphrased quotes are dropped). Corroboration =
COUNT(DISTINCT transcript)over append-only evidence.Durable. Confirmed conversation knowledge is written to an ontos-owned record under
data/ontos/that the rebuild reads back, so it survives a rebuild + syncs via git.CLI (
enrich-conversation):--scan(auto pass: emit only transcripts not yet extracted;--limitcaps transcripts/run) ·--emit(→ agent) ·--ingest(agent →) ·--review·--confirm <id>·--reject <id>·--write-record. Extraction is agent-mediated (ontos has no LLM of its own); the transcript is treated as inert data.No re-extraction. A transcript-level extraction watermark (machine-local, keyed by the transcript's content hash) records which closed sessions are already mined, so
--scanskips them. It survives re-chunking (hash, not the positional chunk id); an edited transcript re-extracts, and its stale unconfirmed conversation candidates are purged (confirmed knowledge and record-sourced proposals are never touched).Visible skip + date anchors. With mining opt-in OFF (the default),
--scan/--emitrecord acurated-skipstatus (so "off by design" is distinguishable from "the scan broke" — surfaced on the session-start 🧠 line), and every extracted fact carries the cited message's own timestamp (message_at), taken from the transcript, as its date anchor. A deterministicnpm run eval(dev-only) scores curated-vs-mining coverage, merge precision, and source-pointer dereference with no LLM.Cross-model verification. A self-checked candidate is LOW; an independent second model (e.g. Codex) agreeing raises it to cross-model / MEDIUM. It is OPT-IN and privacy-gated: only with
.agent/vortex.json → ontos.verify.crossVendorEgress(or--cross-vendor-egress) does ingest honor across-modelverdict, and every candidate must clear a fail-closed gate — config on · deterministic secret/PII filter · the extractor's affirmativesensitive:false· only the triple + a ≤ 240-char quote ever leaves the host · ingest re-checks the whole gate and downgrades anything that fails.Used by default, hedged. Extracted knowledge is used immediately, scaled by
conversationUse(high/medium/low — never asserted as fact); the user corrects rather than approves. It earns confidence from salient un-objected use and loses it from a correction. A re-ingest can demote a stale verdict (same conversation source only; a different transcript's corroboration, and confirmed/rejected rows, are protected).Surfacing. Session start shows a
🧠 ontos:line; a fresh install shows a one-timerebuild-ontosbootstrap cue instead of sitting dormant.
Still a follow-up: sensitive-transcript handling (local model / redaction) is not done — pilot on non-sensitive transcripts only. Verified by 119 tests + a real-conversation dogfood (cross-model verification + the privacy gate, end-to-end on real data).
Automatic lookup — the graph reads itself into the conversation
A graph nobody opens is worth nothing, and "remember to check the graph" is an instruction that
loses against whatever the agent is busy with. So the lookup is wired to the prompt instead of to
the agent's discipline: scripts/prompt-context.mjs is a UserPromptSubmit hook that takes the
terms out of what the user just wrote, looks them up, and injects the FACTS — matched objects with
their typed relations, a failure class's recurrence count and the rules covering it, and proposals
labelled as proposals so they are hedged rather than asserted. When the recall add-on is installed
it also reaches memory bodies and past-conversation chunks, because the graph knows typed relations
while recall knows what was actually said.
{ "hooks": { "UserPromptSubmit": [ { "hooks": [
{ "type": "command", "command": "node \"node_modules/@vortex-os/ontos/scripts/prompt-context.mjs\" || exit 0", "timeout": 10 }
] } ] } }It is quiet when nothing matches, capped (2 objects · 3 relations · 2 proposals), ranked by how many distinct prompt terms pointed at the same object, and silent on every failure path — a lookup helper must never block a turn.
Documents, and the privacy line that lets them in
doc objects come from the instance's own document roots (projects, reference, reports, personal
records). They exist because references the author had already written — [[작성필요-문서-리스트]],
"see the tax summary" — had nothing to point at. Collection is a sorted recursive walk that never
follows symlinks, and an object's id is its path relative to the source root, so a file at a root's
top level keeps the id it always had while nested files stop colliding on their basename.
Letting personal documents into the graph requires the egress line to be real rather than assumed.
Every object carries the privacy class of the record it came from; the cross-vendor gate resolves a
triple back to those classes and keeps a cross-model verdict off anything private, before the content
scan runs. An unrecognised privacy: value is treated as private (fail closed); an ABSENT field falls
back to the source root's declared default, because failing closed there would mute the entire
failure ledger, which carries no privacy field at all. Worklogs and personal records default to
private.
Wikilink candidates — the pile the extractor used to throw away
Prose wikilinks are found, stored with the sentence they sit on, and offered as a typing worklist
(pendingLinkCandidates), instead of being counted and dropped. A human verdict on a candidate is
keyed to that sentence: editing elsewhere in the file keeps the verdict, rewriting the sentence brings
the candidate back for another look. The worklist hides candidates whose target does not resolve and
those already connected in the trusted graph.
Dashboard integration
@vortex-os/base's status dashboard drives this add-on's mining and graph pages
with assets that ship in this package: scripts/ontos-extract-runner.ps1
(+ ontos-model-resolver.psm1) — the bounded per-item worklog/conversation
extraction batch behind the dashboard's per-item 추출 buttons — and
scripts/graph-viewer/ (a fully offline 2D canvas generator and a 3D WebGPU
generator) behind the dashboard's /graph page. The dashboard resolves them
from the installed package (an instance-local data/Tools copy, if present,
is used as a fallback). Without this add-on installed, those dashboard features
show an install notice instead of erroring.
Develop
npm run typecheck --workspace=@vortex-os/ontos
npm run build --workspace=@vortex-os/ontos
npm run verify --workspace=@vortex-os/ontos