@vortex-os/ontos 0.1.0
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/README.md +50 -0
- package/dist/actions/index.d.ts +26 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/index.js +34 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/extract/deterministic.d.ts +9 -0
- package/dist/extract/deterministic.d.ts.map +1 -0
- package/dist/extract/deterministic.js +110 -0
- package/dist/extract/deterministic.js.map +1 -0
- package/dist/extract/index.d.ts +12 -0
- package/dist/extract/index.d.ts.map +1 -0
- package/dist/extract/index.js +12 -0
- package/dist/extract/index.js.map +1 -0
- package/dist/extract/types.d.ts +29 -0
- package/dist/extract/types.d.ts.map +1 -0
- package/dist/extract/types.js +2 -0
- package/dist/extract/types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/frontmatter.d.ts +6 -0
- package/dist/internal/frontmatter.d.ts.map +1 -0
- package/dist/internal/frontmatter.js +30 -0
- package/dist/internal/frontmatter.js.map +1 -0
- package/dist/sqlite/index.d.ts +4 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +3 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/schema.d.ts +10 -0
- package/dist/sqlite/schema.d.ts.map +1 -0
- package/dist/sqlite/schema.js +40 -0
- package/dist/sqlite/schema.js.map +1 -0
- package/dist/sqlite/store.d.ts +51 -0
- package/dist/sqlite/store.d.ts.map +1 -0
- package/dist/sqlite/store.js +169 -0
- package/dist/sqlite/store.js.map +1 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
- package/scripts/rebuild-ontos.mjs +82 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @vortex-os/ontos
|
|
2
|
+
|
|
3
|
+
A lightweight **ontology of your operational world** — objects, typed relations,
|
|
4
|
+
and multi-hop traversal — built from your VortEX records, layered on
|
|
5
|
+
`@vortex-os/base`. It is a distinct add-on from `@vortex-os/memory-extended`:
|
|
6
|
+
memory-extended *recalls* what was said; ontos *models* what is and how it
|
|
7
|
+
connects, and acts on recurring situations.
|
|
8
|
+
|
|
9
|
+
> Sibling add-ons, one line: **remember** (`memory-extended`) · **perceive**
|
|
10
|
+
> (`computer-use`) · **model** (`ontos`).
|
|
11
|
+
|
|
12
|
+
## Status — Phase 1 (0.1.0)
|
|
13
|
+
|
|
14
|
+
Phase 1 is **text-first**: the ontology is built only from existing markdown
|
|
15
|
+
records (the failure ledger, decision-log, memories, `[[wikilinks]]`), with **no
|
|
16
|
+
screen perception and no background daemon** (those are Phase 2, only if Phase 1
|
|
17
|
+
proves its value). Storage is **sqlite** (better-sqlite3) with recursive-CTE
|
|
18
|
+
traversal — no graph DB (TypeDB/Neo4j) and no formal OWL/RDF reasoner.
|
|
19
|
+
|
|
20
|
+
Implemented and verified (typecheck/build/verify 15/15; confirmed on a real
|
|
21
|
+
instance — e.g. `literal-control-bytes` → 6 occurrences, guard fires):
|
|
22
|
+
|
|
23
|
+
- **`sqlite/OntosStore`** — schema; idempotent object/relation upsert
|
|
24
|
+
(relId = `sha256(source⏎relation⏎target)`); alias-based identity resolution;
|
|
25
|
+
hop-capped (≤ 3) cycle-guarded recursive-CTE traversal; recurrence count;
|
|
26
|
+
`clear()` + a transaction wrapper for a true rebuild.
|
|
27
|
+
- **`extract/`** — the deterministic extractor (Phase 1 §4): failure ledger →
|
|
28
|
+
recurring-failure subgraph (`recurs-as` occurrence→class, `covered-by` →rule);
|
|
29
|
+
decision-log → `decision:` objects (+ wikilink candidates); memory → `rule:`
|
|
30
|
+
objects. LLM enrichment is a documented `TODO`.
|
|
31
|
+
- **`actions/`** — `recurringFailureGuard` (the §5A fast-proof) and `why`
|
|
32
|
+
(§5B decision archaeology, evidence-first ≤ 3 hops).
|
|
33
|
+
- **`scripts/rebuild-ontos.mjs`** — read-only walk of `data/_failures`,
|
|
34
|
+
`data/decision-log`, `data/_memory` → extract → true-rebuild the index.
|
|
35
|
+
|
|
36
|
+
**Deferred** (Phase 1 follow-ups): frontmatter `ontos:` writeback, LLM
|
|
37
|
+
enrichment, confirmation UX, identity embedding-candidates (optional reuse of
|
|
38
|
+
`@vortex-os/memory-extended`'s embedder when present), and the `vortex ontos`
|
|
39
|
+
CLI / session-start trigger wiring.
|
|
40
|
+
|
|
41
|
+
Design: the concept and Phase 1 build spec live in the maintainer's VortEX
|
|
42
|
+
instance (Codex-reviewed, 2 rounds).
|
|
43
|
+
|
|
44
|
+
## Develop
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
npm run typecheck --workspace=@vortex-os/ontos
|
|
48
|
+
npm run build --workspace=@vortex-os/ontos
|
|
49
|
+
npm run verify --workspace=@vortex-os/ontos
|
|
50
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { OntosStore } from "../sqlite/index.js";
|
|
2
|
+
import type { WalkStep } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* §5B — decision archaeology. Walks the reasoning relations (supersedes /
|
|
5
|
+
* contradicts / causes) from an object, returning short, evidence-first chains
|
|
6
|
+
* capped at ≤ 3 hops (cycle-guarded by the store).
|
|
7
|
+
*
|
|
8
|
+
* TODO(phase1): shape the raw walk into chains with the backing evidence
|
|
9
|
+
* snippets (the store already carries `evidence` on each relation).
|
|
10
|
+
*/
|
|
11
|
+
export declare function why(store: OntosStore, objectId: string, maxHops?: number): WalkStep[];
|
|
12
|
+
export interface GuardHit {
|
|
13
|
+
readonly failureId: string;
|
|
14
|
+
readonly recurrence: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* §5A — recurring-failure guard. Given a situation already resolved to a failure
|
|
18
|
+
* object id (the §6 trigger surface does that resolution), report whether it
|
|
19
|
+
* recurs at/over the threshold, so the caller can surface the covering rule /
|
|
20
|
+
* guard.
|
|
21
|
+
*
|
|
22
|
+
* TODO(phase1): join `covered-by` (→Rule) and `resolved-by` (→Action/Guard) to
|
|
23
|
+
* return the remedy alongside the recurrence signal.
|
|
24
|
+
*/
|
|
25
|
+
export declare function recurringFailureGuard(store: OntosStore, failureId: string, threshold?: number): GuardHit | null;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,QAAQ,EAAE,CAKhF;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,SAAI,GACZ,QAAQ,GAAG,IAAI,CASjB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §5B — decision archaeology. Walks the reasoning relations (supersedes /
|
|
3
|
+
* contradicts / causes) from an object, returning short, evidence-first chains
|
|
4
|
+
* capped at ≤ 3 hops (cycle-guarded by the store).
|
|
5
|
+
*
|
|
6
|
+
* TODO(phase1): shape the raw walk into chains with the backing evidence
|
|
7
|
+
* snippets (the store already carries `evidence` on each relation).
|
|
8
|
+
*/
|
|
9
|
+
export function why(store, objectId, maxHops = 3) {
|
|
10
|
+
return store.traverse(objectId, {
|
|
11
|
+
maxHops,
|
|
12
|
+
rels: ["supersedes", "contradicts", "causes"],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* §5A — recurring-failure guard. Given a situation already resolved to a failure
|
|
17
|
+
* object id (the §6 trigger surface does that resolution), report whether it
|
|
18
|
+
* recurs at/over the threshold, so the caller can surface the covering rule /
|
|
19
|
+
* guard.
|
|
20
|
+
*
|
|
21
|
+
* TODO(phase1): join `covered-by` (→Rule) and `resolved-by` (→Action/Guard) to
|
|
22
|
+
* return the remedy alongside the recurrence signal.
|
|
23
|
+
*/
|
|
24
|
+
export function recurringFailureGuard(store, failureId, threshold = 2) {
|
|
25
|
+
// Recurrence is counted on the canonical CLASS id. Resolve an occurrence to
|
|
26
|
+
// its class STRUCTURALLY — follow its recurs-as edge — rather than by string
|
|
27
|
+
// surgery on "@" (a recurrence_key could itself contain "@"). A class id has
|
|
28
|
+
// no outgoing recurs-as edge, so it resolves to itself.
|
|
29
|
+
const viaEdge = store.neighbors(failureId, ["recurs-as"]).find((r) => r.sourceId === failureId);
|
|
30
|
+
const classId = viaEdge ? viaEdge.targetId : failureId;
|
|
31
|
+
const recurrence = store.recurrenceCount(classId);
|
|
32
|
+
return recurrence >= threshold ? { failureId: classId, recurrence } : null;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,CAAC,KAAiB,EAAE,QAAgB,EAAE,OAAO,GAAG,CAAC;IAClE,OAAO,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE;QAC9B,OAAO;QACP,IAAI,EAAE,CAAC,YAAY,EAAE,aAAa,EAAE,QAAQ,CAAC;KAC9C,CAAC,CAAC;AACL,CAAC;AAOD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAiB,EACjB,SAAiB,EACjB,SAAS,GAAG,CAAC;IAEb,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,wDAAwD;IACxD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC;IAChG,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,MAAM,UAAU,GAAG,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAClD,OAAO,UAAU,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ExtractResult, RecordInput } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic extraction (Phase 1 §4 step 2) — the fast-proof core, no LLM.
|
|
4
|
+
* Dispatches by record category. Confidently extracts the failure-recurrence +
|
|
5
|
+
* coverage subgraph and the object set; supersedes/causes chains arrive as
|
|
6
|
+
* wikilink candidates until an `ontos:` block or LLM enrichment types them.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractDeterministic(record: RecordInput): ExtractResult;
|
|
9
|
+
//# sourceMappingURL=deterministic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic.d.ts","sourceRoot":"","sources":["../../src/extract/deterministic.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAiB,WAAW,EAAE,MAAM,YAAY,CAAC;AAkG5E;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,aAAa,CAWvE"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { makeRelId } from "../sqlite/store.js";
|
|
2
|
+
const WIKILINK = /\[\[([^\]]+)\]\]/g;
|
|
3
|
+
function basenameSlug(path) {
|
|
4
|
+
const base = path.replace(/\\/g, "/").split("/").pop() ?? path;
|
|
5
|
+
return base.replace(/\.md$/i, "");
|
|
6
|
+
}
|
|
7
|
+
function str(v) {
|
|
8
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
9
|
+
}
|
|
10
|
+
function rel(sourceId, r, targetId, evidence) {
|
|
11
|
+
return {
|
|
12
|
+
relId: makeRelId(sourceId, r, targetId),
|
|
13
|
+
rel: r,
|
|
14
|
+
sourceId,
|
|
15
|
+
targetId,
|
|
16
|
+
provenance: "deterministic",
|
|
17
|
+
confidence: 1,
|
|
18
|
+
confirmed: null,
|
|
19
|
+
evidence,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function wikilinks(sourceId, body, evidence) {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const m of body.matchAll(WIKILINK)) {
|
|
25
|
+
const parts = (m[1] ?? "").split("|");
|
|
26
|
+
const targetRef = (parts[0] ?? "").trim();
|
|
27
|
+
if (targetRef)
|
|
28
|
+
out.push({ sourceId, targetRef, evidence });
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
function empty() {
|
|
33
|
+
return { objects: [], relations: [], links: [] };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Failure ledger -> the recurring-failure subgraph (powers the §5A guard,
|
|
37
|
+
* fully deterministic). One canonical `problem:failure:<key>` class object;
|
|
38
|
+
* each occurrence file becomes its own object with a `recurs-as` edge to the
|
|
39
|
+
* class (so the store's recurrence count = number of occurrences). The optional
|
|
40
|
+
* `rule` field becomes a `covered-by` edge to a `rule:<slug>` object.
|
|
41
|
+
*/
|
|
42
|
+
function fromFailure(record) {
|
|
43
|
+
const key = str(record.frontmatter["recurrence_key"]);
|
|
44
|
+
if (!key)
|
|
45
|
+
return empty();
|
|
46
|
+
const classId = `problem:failure:${key}`;
|
|
47
|
+
// Occurrence id uses the file basename. The extracted categories (_failures,
|
|
48
|
+
// decision-log, _memory) are FLAT directories, so basenames are unique there;
|
|
49
|
+
// nested categories (worklog) are not extracted. If a category ever nests,
|
|
50
|
+
// switch this to a full-relative-path discriminator.
|
|
51
|
+
const occId = `${classId}@${basenameSlug(record.path)}`;
|
|
52
|
+
const objects = [
|
|
53
|
+
{ id: classId, type: "problem", subtype: "failure", label: key, source: null },
|
|
54
|
+
{ id: occId, type: "problem", subtype: "failure", label: key, source: record.path },
|
|
55
|
+
];
|
|
56
|
+
const relations = [rel(occId, "recurs-as", classId, record.path)];
|
|
57
|
+
const ruleSlug = str(record.frontmatter["rule"]);
|
|
58
|
+
if (ruleSlug) {
|
|
59
|
+
objects.push({ id: `rule:${ruleSlug}`, type: "rule", label: ruleSlug, source: null });
|
|
60
|
+
relations.push(rel(classId, "covered-by", `rule:${ruleSlug}`, record.path));
|
|
61
|
+
}
|
|
62
|
+
return { objects, relations, links: [] };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Decision log -> a `decision:<slug>` object (label = the H1 title). Typed
|
|
66
|
+
* relations like `supersedes` are not structured in the records yet, so they
|
|
67
|
+
* surface as wikilink candidates for human typing (§4).
|
|
68
|
+
*/
|
|
69
|
+
function fromDecision(record) {
|
|
70
|
+
const slug = basenameSlug(record.path);
|
|
71
|
+
const id = `decision:${slug}`;
|
|
72
|
+
const title = (record.body.match(/^#\s+(.+)$/m)?.[1] ?? slug).trim();
|
|
73
|
+
return {
|
|
74
|
+
objects: [{ id, type: "decision", label: title, source: record.path }],
|
|
75
|
+
relations: [],
|
|
76
|
+
links: wikilinks(id, record.body, record.path),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Memory -> a `rule:<slug>` object (label = frontmatter `name`); body wikilinks as candidates. */
|
|
80
|
+
function fromMemory(record) {
|
|
81
|
+
const slug = basenameSlug(record.path);
|
|
82
|
+
if (slug === "_INDEX" || slug === "MEMORY")
|
|
83
|
+
return empty();
|
|
84
|
+
const id = `rule:${slug}`;
|
|
85
|
+
const label = str(record.frontmatter["name"]) ?? slug;
|
|
86
|
+
return {
|
|
87
|
+
objects: [{ id, type: "rule", label, source: record.path }],
|
|
88
|
+
relations: [],
|
|
89
|
+
links: wikilinks(id, record.body, record.path),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Deterministic extraction (Phase 1 §4 step 2) — the fast-proof core, no LLM.
|
|
94
|
+
* Dispatches by record category. Confidently extracts the failure-recurrence +
|
|
95
|
+
* coverage subgraph and the object set; supersedes/causes chains arrive as
|
|
96
|
+
* wikilink candidates until an `ontos:` block or LLM enrichment types them.
|
|
97
|
+
*/
|
|
98
|
+
export function extractDeterministic(record) {
|
|
99
|
+
switch (record.category) {
|
|
100
|
+
case "failure":
|
|
101
|
+
return fromFailure(record);
|
|
102
|
+
case "decision":
|
|
103
|
+
return fromDecision(record);
|
|
104
|
+
case "memory":
|
|
105
|
+
return fromMemory(record);
|
|
106
|
+
default:
|
|
107
|
+
return empty();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=deterministic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deterministic.js","sourceRoot":"","sources":["../../src/extract/deterministic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI/C,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AAErC,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC;IAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,GAAG,CAAC,CAAU;IACrB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED,SAAS,GAAG,CAAC,QAAgB,EAAE,CAAe,EAAE,QAAgB,EAAE,QAAgB;IAChF,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,CAAC;QACvC,GAAG,EAAE,CAAC;QACN,QAAQ;QACR,QAAQ;QACR,UAAU,EAAE,eAAe;QAC3B,UAAU,EAAE,CAAC;QACb,SAAS,EAAE,IAAI;QACf,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB,EAAE,IAAY,EAAE,QAAgB;IACjE,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,KAAK;IACZ,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AACnD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,MAAmB;IACtC,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,mBAAmB,GAAG,EAAE,CAAC;IACzC,6EAA6E;IAC7E,8EAA8E;IAC9E,2EAA2E;IAC3E,qDAAqD;IACrD,MAAM,KAAK,GAAG,GAAG,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;IACxD,MAAM,OAAO,GAAgB;QAC3B,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;QAC9E,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;KACpF,CAAC;IACF,MAAM,SAAS,GAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACjF,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IACjD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACtF,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,QAAQ,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CAAC,MAAmB;IACvC,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,YAAY,IAAI,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACrE,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QACtE,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED,mGAAmG;AACnG,SAAS,UAAU,CAAC,MAAmB;IACrC,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,EAAE,CAAC;IAC3D,MAAM,EAAE,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC;IACtD,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;QAC3D,SAAS,EAAE,EAAE;QACb,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAmB;IACtD,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,SAAS;YACZ,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;QAC7B,KAAK,UAAU;YACb,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5B;YACE,OAAO,KAAK,EAAE,CAAC;IACnB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type { RecordCategory, RecordInput, LinkCandidate, ExtractResult } from "./types.js";
|
|
2
|
+
export { extractDeterministic } from "./deterministic.js";
|
|
3
|
+
import type { ExtractResult, RecordInput } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* LLM enrichment (Phase 1 §4 step 3) — runs ONLY on changed, weakly-structured
|
|
6
|
+
* files after deterministic extraction shows gaps, proposing typed relations
|
|
7
|
+
* with confidence + evidence (never auto-confirmed).
|
|
8
|
+
*
|
|
9
|
+
* TODO(phase1): implement against the host LLM. The skeleton is a pass-through.
|
|
10
|
+
*/
|
|
11
|
+
export declare function enrichWithLlm(_record: RecordInput, deterministic: ExtractResult): ExtractResult;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extract/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,GAAG,aAAa,CAE/F"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { extractDeterministic } from "./deterministic.js";
|
|
2
|
+
/**
|
|
3
|
+
* LLM enrichment (Phase 1 §4 step 3) — runs ONLY on changed, weakly-structured
|
|
4
|
+
* files after deterministic extraction shows gaps, proposing typed relations
|
|
5
|
+
* with confidence + evidence (never auto-confirmed).
|
|
6
|
+
*
|
|
7
|
+
* TODO(phase1): implement against the host LLM. The skeleton is a pass-through.
|
|
8
|
+
*/
|
|
9
|
+
export function enrichWithLlm(_record, deterministic) {
|
|
10
|
+
return deterministic;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/extract/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAI1D;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,OAAoB,EAAE,aAA4B;IAC9E,OAAO,aAAa,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { OntObject, OntRelation } from "../types.js";
|
|
2
|
+
/** Which record family a file came from (the rebuild walk knows this from the dir). */
|
|
3
|
+
export type RecordCategory = "failure" | "decision" | "memory" | "worklog" | "handoff" | "other";
|
|
4
|
+
/** A parsed VortEX markdown record, the input to extraction. */
|
|
5
|
+
export interface RecordInput {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly category: RecordCategory;
|
|
8
|
+
readonly frontmatter: Readonly<Record<string, unknown>>;
|
|
9
|
+
readonly body: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* An untyped `[[wikilink]]` seed (§4). NOT stored as a typed relation — it is a
|
|
13
|
+
* candidate connection that still needs a human to *type* it (supersedes? causes?)
|
|
14
|
+
* and confirm. Kept separate from `relations` on purpose.
|
|
15
|
+
*/
|
|
16
|
+
export interface LinkCandidate {
|
|
17
|
+
readonly sourceId: string;
|
|
18
|
+
/** The wikilink target text (a slug/title), `[[target|alias]]` reduced to `target`. */
|
|
19
|
+
readonly targetRef: string;
|
|
20
|
+
readonly evidence: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ExtractResult {
|
|
23
|
+
readonly objects: OntObject[];
|
|
24
|
+
/** Explicit, deterministic typed relations (recurrence_key, the `rule` field, an `ontos:` block). */
|
|
25
|
+
readonly relations: OntRelation[];
|
|
26
|
+
/** Untyped wikilink seeds — for human typing + confirmation, never auto-stored. */
|
|
27
|
+
readonly links: LinkCandidate[];
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/extract/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1D,uFAAuF;AACvF,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEjG,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,uFAAuF;IACvF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC;IAClC,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC;CACjC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/extract/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,mBAAmB,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/internal/frontmatter.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAe/F"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { parse as parseYaml } from "yaml";
|
|
2
|
+
/**
|
|
3
|
+
* Inlined YAML-frontmatter parser — mirrors memory-extended's own inlined copy.
|
|
4
|
+
*
|
|
5
|
+
* ontos ships as a public add-on, so it cannot import the internal, unpublished
|
|
6
|
+
* `@vortex-os/core` `parseFrontmatter`. The single helper needed is inlined here
|
|
7
|
+
* (only `yaml` is an external dependency). BOM-safe; returns empty frontmatter +
|
|
8
|
+
* the source unchanged when no fence is present; tolerates a malformed fence by
|
|
9
|
+
* treating it as no frontmatter.
|
|
10
|
+
*/
|
|
11
|
+
const FENCE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
12
|
+
const BOM = "";
|
|
13
|
+
export function parseFrontmatter(source) {
|
|
14
|
+
const cleaned = source.startsWith(BOM) ? source.slice(1) : source;
|
|
15
|
+
const match = cleaned.match(FENCE);
|
|
16
|
+
if (!match) {
|
|
17
|
+
return { frontmatter: {}, body: cleaned };
|
|
18
|
+
}
|
|
19
|
+
const yaml = match[1] ?? "";
|
|
20
|
+
const body = cleaned.slice(match[0].length);
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = (parseYaml(yaml) ?? {});
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
parsed = {};
|
|
27
|
+
}
|
|
28
|
+
return { frontmatter: parsed, body };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/internal/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,KAAK,GAAG,mCAAmC,CAAC;AAClD,MAAM,GAAG,GAAG,GAAG,CAAC;AAOhB,MAAM,UAAU,gBAAgB,CAA8B,MAAc;IAC1E,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAClE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,WAAW,EAAE,EAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACjD,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,MAAS,CAAC;IACd,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAM,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,EAAO,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sqlite/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite schema for the ontos derived index.
|
|
3
|
+
*
|
|
4
|
+
* The markdown frontmatter `ontos:` block is the source of truth; this index is
|
|
5
|
+
* a regenerable derivative (delete the .sqlite file and re-run `rebuild-ontos`).
|
|
6
|
+
* Multi-hop traversal is a recursive CTE over `relations` — no graph DB
|
|
7
|
+
* (TypeDB/Neo4j) is needed at Phase 1 scale (see the build spec, storage section).
|
|
8
|
+
*/
|
|
9
|
+
export declare const SCHEMA = "\nCREATE TABLE IF NOT EXISTS objects (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n subtype TEXT,\n label TEXT NOT NULL,\n source TEXT\n);\nCREATE INDEX IF NOT EXISTS idx_objects_type ON objects(type);\n\nCREATE TABLE IF NOT EXISTS relations (\n rel_id TEXT PRIMARY KEY,\n rel TEXT NOT NULL,\n source_id TEXT NOT NULL,\n target_id TEXT NOT NULL,\n provenance TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 1.0,\n confirmed TEXT,\n evidence TEXT,\n FOREIGN KEY (source_id) REFERENCES objects(id) ON DELETE CASCADE,\n FOREIGN KEY (target_id) REFERENCES objects(id) ON DELETE CASCADE\n);\nCREATE INDEX IF NOT EXISTS idx_relations_source ON relations(source_id, rel);\nCREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_id, rel);\n\nCREATE TABLE IF NOT EXISTS aliases (\n alias TEXT PRIMARY KEY,\n canonical_id TEXT NOT NULL,\n FOREIGN KEY (canonical_id) REFERENCES objects(id) ON DELETE CASCADE\n);\n";
|
|
10
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/sqlite/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,86BA8BlB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite schema for the ontos derived index.
|
|
3
|
+
*
|
|
4
|
+
* The markdown frontmatter `ontos:` block is the source of truth; this index is
|
|
5
|
+
* a regenerable derivative (delete the .sqlite file and re-run `rebuild-ontos`).
|
|
6
|
+
* Multi-hop traversal is a recursive CTE over `relations` — no graph DB
|
|
7
|
+
* (TypeDB/Neo4j) is needed at Phase 1 scale (see the build spec, storage section).
|
|
8
|
+
*/
|
|
9
|
+
export const SCHEMA = `
|
|
10
|
+
CREATE TABLE IF NOT EXISTS objects (
|
|
11
|
+
id TEXT PRIMARY KEY,
|
|
12
|
+
type TEXT NOT NULL,
|
|
13
|
+
subtype TEXT,
|
|
14
|
+
label TEXT NOT NULL,
|
|
15
|
+
source TEXT
|
|
16
|
+
);
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_objects_type ON objects(type);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS relations (
|
|
20
|
+
rel_id TEXT PRIMARY KEY,
|
|
21
|
+
rel TEXT NOT NULL,
|
|
22
|
+
source_id TEXT NOT NULL,
|
|
23
|
+
target_id TEXT NOT NULL,
|
|
24
|
+
provenance TEXT NOT NULL,
|
|
25
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
26
|
+
confirmed TEXT,
|
|
27
|
+
evidence TEXT,
|
|
28
|
+
FOREIGN KEY (source_id) REFERENCES objects(id) ON DELETE CASCADE,
|
|
29
|
+
FOREIGN KEY (target_id) REFERENCES objects(id) ON DELETE CASCADE
|
|
30
|
+
);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_relations_source ON relations(source_id, rel);
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_id, rel);
|
|
33
|
+
|
|
34
|
+
CREATE TABLE IF NOT EXISTS aliases (
|
|
35
|
+
alias TEXT PRIMARY KEY,
|
|
36
|
+
canonical_id TEXT NOT NULL,
|
|
37
|
+
FOREIGN KEY (canonical_id) REFERENCES objects(id) ON DELETE CASCADE
|
|
38
|
+
);
|
|
39
|
+
`;
|
|
40
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/sqlite/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BrB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { OntObject, OntRelation, RelationType, WalkStep } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* §5-bis relation-id recipe. Stable over the (source, relation, target) triple,
|
|
4
|
+
* so re-accepting the same relation is idempotent (never duplicates).
|
|
5
|
+
*/
|
|
6
|
+
export declare function makeRelId(sourceId: string, rel: RelationType, targetId: string): string;
|
|
7
|
+
export interface TraverseOptions {
|
|
8
|
+
/** Hop limit, capped at 3 (§5B). */
|
|
9
|
+
readonly maxHops?: number;
|
|
10
|
+
/** Restrict to these edge types (empty = all). */
|
|
11
|
+
readonly rels?: readonly RelationType[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* SQLite-backed ontos index. Markdown `ontos:` frontmatter is the source of
|
|
15
|
+
* truth; this is a regenerable derived index. Sync API (better-sqlite3); call
|
|
16
|
+
* `close()` to release the file lock (matters on Windows).
|
|
17
|
+
*/
|
|
18
|
+
export declare class OntosStore {
|
|
19
|
+
readonly dbPath: string;
|
|
20
|
+
private db;
|
|
21
|
+
constructor(dbPath: string);
|
|
22
|
+
close(): void;
|
|
23
|
+
/** Run a function inside one transaction (bulk upserts during a rebuild). */
|
|
24
|
+
transaction(fn: () => void): void;
|
|
25
|
+
/**
|
|
26
|
+
* Empty all tables so a walk-and-upsert becomes a TRUE rebuild: stale objects
|
|
27
|
+
* and relations from deleted/renamed records, or from a changed recurrence_key
|
|
28
|
+
* / removed rule, do not linger (which would skew recurrenceCount). Call before
|
|
29
|
+
* re-populating from a full record walk.
|
|
30
|
+
*/
|
|
31
|
+
clear(): void;
|
|
32
|
+
upsertObject(obj: OntObject): void;
|
|
33
|
+
/** Idempotent on relId (§5-bis): re-accepting the same relation updates, never duplicates. */
|
|
34
|
+
upsertRelation(rel: OntRelation): void;
|
|
35
|
+
addAlias(alias: string, canonicalId: string): void;
|
|
36
|
+
/** §3 identity: resolve a name to a canonical object id (a real object id wins, then an alias). */
|
|
37
|
+
resolveAlias(name: string): string | null;
|
|
38
|
+
getObject(id: string): OntObject | null;
|
|
39
|
+
/** Outgoing relations from an object, optionally filtered by relation type. */
|
|
40
|
+
neighbors(id: string, rels?: readonly RelationType[]): OntRelation[];
|
|
41
|
+
/**
|
|
42
|
+
* Multi-hop walk via recursive CTE — capped at maxHops (default 3, §5B) and
|
|
43
|
+
* cycle-guarded: a node never re-enters its own path. The path is delimited
|
|
44
|
+
* with ">" so the membership check is token-exact (not a loose substring).
|
|
45
|
+
* No graph DB is needed for this scale.
|
|
46
|
+
*/
|
|
47
|
+
traverse(startId: string, opts?: TraverseOptions): WalkStep[];
|
|
48
|
+
/** §5A helper: how many failures recur as (share a pattern with) this one. */
|
|
49
|
+
recurrenceCount(problemId: string): number;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sqlite/store.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAElF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKvF;AAED,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACzC;AA0BD;;;;GAIG;AACH,qBAAa,UAAU;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,EAAE,MAAM;IAS1B,KAAK,IAAI,IAAI;IAIb,6EAA6E;IAC7E,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAIjC;;;;;OAKG;IACH,KAAK,IAAI,IAAI;IAIb,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAkBlC,8FAA8F;IAC9F,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;IAqBtC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IASlD,mGAAmG;IACnG,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAWzC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAgBvC,+EAA+E;IAC/E,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,YAAY,EAAE,GAAG,WAAW,EAAE;IAapE;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,QAAQ,EAAE;IAyBjE,8EAA8E;IAC9E,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;CAQ3C"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import Database from "better-sqlite3";
|
|
5
|
+
import { SCHEMA } from "./schema.js";
|
|
6
|
+
/**
|
|
7
|
+
* §5-bis relation-id recipe. Stable over the (source, relation, target) triple,
|
|
8
|
+
* so re-accepting the same relation is idempotent (never duplicates).
|
|
9
|
+
*/
|
|
10
|
+
export function makeRelId(sourceId, rel, targetId) {
|
|
11
|
+
// Newline separates the triple in the preimage: ids derive from filenames /
|
|
12
|
+
// frontmatter keys and never contain a newline, so distinct triples cannot
|
|
13
|
+
// collide on the preimage (a "|"-separated preimage could, since ids may hold "|").
|
|
14
|
+
return createHash("sha256").update(`${sourceId}\n${rel}\n${targetId}`).digest("hex").slice(0, 16);
|
|
15
|
+
}
|
|
16
|
+
function rowToRelation(r) {
|
|
17
|
+
return {
|
|
18
|
+
relId: r.rel_id,
|
|
19
|
+
rel: r.rel,
|
|
20
|
+
sourceId: r.source_id,
|
|
21
|
+
targetId: r.target_id,
|
|
22
|
+
provenance: r.provenance,
|
|
23
|
+
confidence: r.confidence,
|
|
24
|
+
confirmed: r.confirmed,
|
|
25
|
+
evidence: r.evidence,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* SQLite-backed ontos index. Markdown `ontos:` frontmatter is the source of
|
|
30
|
+
* truth; this is a regenerable derived index. Sync API (better-sqlite3); call
|
|
31
|
+
* `close()` to release the file lock (matters on Windows).
|
|
32
|
+
*/
|
|
33
|
+
export class OntosStore {
|
|
34
|
+
dbPath;
|
|
35
|
+
db;
|
|
36
|
+
constructor(dbPath) {
|
|
37
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
38
|
+
this.dbPath = dbPath;
|
|
39
|
+
this.db = new Database(dbPath);
|
|
40
|
+
this.db.pragma("journal_mode = WAL");
|
|
41
|
+
this.db.pragma("foreign_keys = ON");
|
|
42
|
+
this.db.exec(SCHEMA);
|
|
43
|
+
}
|
|
44
|
+
close() {
|
|
45
|
+
this.db.close();
|
|
46
|
+
}
|
|
47
|
+
/** Run a function inside one transaction (bulk upserts during a rebuild). */
|
|
48
|
+
transaction(fn) {
|
|
49
|
+
this.db.transaction(fn)();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Empty all tables so a walk-and-upsert becomes a TRUE rebuild: stale objects
|
|
53
|
+
* and relations from deleted/renamed records, or from a changed recurrence_key
|
|
54
|
+
* / removed rule, do not linger (which would skew recurrenceCount). Call before
|
|
55
|
+
* re-populating from a full record walk.
|
|
56
|
+
*/
|
|
57
|
+
clear() {
|
|
58
|
+
this.db.exec("DELETE FROM relations; DELETE FROM aliases; DELETE FROM objects;");
|
|
59
|
+
}
|
|
60
|
+
upsertObject(obj) {
|
|
61
|
+
this.db
|
|
62
|
+
.prepare(`INSERT INTO objects (id, type, subtype, label, source)
|
|
63
|
+
VALUES (@id, @type, @subtype, @label, @source)
|
|
64
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
65
|
+
type = excluded.type, subtype = excluded.subtype,
|
|
66
|
+
label = excluded.label, source = excluded.source`)
|
|
67
|
+
.run({
|
|
68
|
+
id: obj.id,
|
|
69
|
+
type: obj.type,
|
|
70
|
+
subtype: obj.subtype ?? null,
|
|
71
|
+
label: obj.label,
|
|
72
|
+
source: obj.source ?? null,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** Idempotent on relId (§5-bis): re-accepting the same relation updates, never duplicates. */
|
|
76
|
+
upsertRelation(rel) {
|
|
77
|
+
this.db
|
|
78
|
+
.prepare(`INSERT INTO relations (rel_id, rel, source_id, target_id, provenance, confidence, confirmed, evidence)
|
|
79
|
+
VALUES (@relId, @rel, @sourceId, @targetId, @provenance, @confidence, @confirmed, @evidence)
|
|
80
|
+
ON CONFLICT(rel_id) DO UPDATE SET
|
|
81
|
+
provenance = excluded.provenance, confidence = excluded.confidence,
|
|
82
|
+
confirmed = excluded.confirmed, evidence = excluded.evidence`)
|
|
83
|
+
.run({
|
|
84
|
+
relId: rel.relId,
|
|
85
|
+
rel: rel.rel,
|
|
86
|
+
sourceId: rel.sourceId,
|
|
87
|
+
targetId: rel.targetId,
|
|
88
|
+
provenance: rel.provenance,
|
|
89
|
+
confidence: rel.confidence,
|
|
90
|
+
confirmed: rel.confirmed ?? null,
|
|
91
|
+
evidence: rel.evidence ?? null,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
addAlias(alias, canonicalId) {
|
|
95
|
+
this.db
|
|
96
|
+
.prepare(`INSERT INTO aliases (alias, canonical_id) VALUES (?, ?)
|
|
97
|
+
ON CONFLICT(alias) DO UPDATE SET canonical_id = excluded.canonical_id`)
|
|
98
|
+
.run(alias, canonicalId);
|
|
99
|
+
}
|
|
100
|
+
/** §3 identity: resolve a name to a canonical object id (a real object id wins, then an alias). */
|
|
101
|
+
resolveAlias(name) {
|
|
102
|
+
const direct = this.db.prepare(`SELECT id FROM objects WHERE id = ?`).get(name);
|
|
103
|
+
if (direct)
|
|
104
|
+
return direct.id;
|
|
105
|
+
const row = this.db.prepare(`SELECT canonical_id FROM aliases WHERE alias = ?`).get(name);
|
|
106
|
+
return row ? row.canonical_id : null;
|
|
107
|
+
}
|
|
108
|
+
getObject(id) {
|
|
109
|
+
const row = this.db
|
|
110
|
+
.prepare(`SELECT id, type, subtype, label, source FROM objects WHERE id = ?`)
|
|
111
|
+
.get(id);
|
|
112
|
+
if (!row)
|
|
113
|
+
return null;
|
|
114
|
+
return {
|
|
115
|
+
id: row.id,
|
|
116
|
+
type: row.type,
|
|
117
|
+
subtype: row.subtype ?? null,
|
|
118
|
+
label: row.label,
|
|
119
|
+
source: row.source,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** Outgoing relations from an object, optionally filtered by relation type. */
|
|
123
|
+
neighbors(id, rels) {
|
|
124
|
+
const rows = (rels && rels.length
|
|
125
|
+
? this.db
|
|
126
|
+
.prepare(`SELECT * FROM relations WHERE source_id = ? AND rel IN (${rels.map(() => "?").join(",")})`)
|
|
127
|
+
.all(id, ...rels)
|
|
128
|
+
: this.db.prepare(`SELECT * FROM relations WHERE source_id = ?`).all(id));
|
|
129
|
+
return rows.map(rowToRelation);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Multi-hop walk via recursive CTE — capped at maxHops (default 3, §5B) and
|
|
133
|
+
* cycle-guarded: a node never re-enters its own path. The path is delimited
|
|
134
|
+
* with ">" so the membership check is token-exact (not a loose substring).
|
|
135
|
+
* No graph DB is needed for this scale.
|
|
136
|
+
*/
|
|
137
|
+
traverse(startId, opts = {}) {
|
|
138
|
+
const maxHops = Math.min(opts.maxHops ?? 3, 3);
|
|
139
|
+
const rels = opts.rels ?? [];
|
|
140
|
+
const relFilter = rels.length ? `AND r.rel IN (${rels.map(() => "?").join(",")})` : "";
|
|
141
|
+
// Path tokens are newline-delimited: ids never contain a newline, so the
|
|
142
|
+
// membership check is exact (a ">"-delimiter could false-match an id that
|
|
143
|
+
// legitimately contains ">").
|
|
144
|
+
const sql = `
|
|
145
|
+
WITH RECURSIVE walk(node, depth, path) AS (
|
|
146
|
+
SELECT ?, 0, char(10) || ? || char(10)
|
|
147
|
+
UNION ALL
|
|
148
|
+
SELECT r.target_id, w.depth + 1, w.path || r.target_id || char(10)
|
|
149
|
+
FROM relations r JOIN walk w ON r.source_id = w.node
|
|
150
|
+
WHERE w.depth < ? AND instr(w.path, char(10) || r.target_id || char(10)) = 0 ${relFilter}
|
|
151
|
+
)
|
|
152
|
+
SELECT node, depth, path FROM walk WHERE depth > 0 ORDER BY depth`;
|
|
153
|
+
const params = [startId, startId, maxHops, ...rels];
|
|
154
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
155
|
+
return rows.map((r) => ({
|
|
156
|
+
nodeId: r.node,
|
|
157
|
+
depth: r.depth,
|
|
158
|
+
path: r.path.split("\n").filter(Boolean),
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
/** §5A helper: how many failures recur as (share a pattern with) this one. */
|
|
162
|
+
recurrenceCount(problemId) {
|
|
163
|
+
const row = this.db
|
|
164
|
+
.prepare(`SELECT COUNT(*) AS n FROM relations WHERE rel = 'recurs-as' AND (source_id = ? OR target_id = ?)`)
|
|
165
|
+
.get(problemId, problemId);
|
|
166
|
+
return row.n;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/sqlite/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vortex-os/ontos — core types (Phase 1, text-first).
|
|
3
|
+
*
|
|
4
|
+
* Six object types, eight relation types. Object identity ("same-as") is handled
|
|
5
|
+
* by the alias layer (see the sqlite store), NOT as a graph relation — keeping
|
|
6
|
+
* dedup separate from `recurs-as` (pattern recurrence). See the maintainer's
|
|
7
|
+
* instance Phase 1 build spec for the full rationale.
|
|
8
|
+
*/
|
|
9
|
+
export type ComponentKind = "host" | "service" | "module" | "repo";
|
|
10
|
+
export type ProblemKind = "incident" | "failure";
|
|
11
|
+
/** §1 — the six object types. `action` includes executable/checkable Guards. */
|
|
12
|
+
export type ObjectType = "component" | "problem" | "decision" | "rule" | "action" | "person";
|
|
13
|
+
export interface OntObject {
|
|
14
|
+
/** Canonical id, e.g. "component:vortex-os/base" or "problem:failure:dist-trap". */
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly type: ObjectType;
|
|
17
|
+
/** Component/Problem subtype, when applicable. */
|
|
18
|
+
readonly subtype?: ComponentKind | ProblemKind | null;
|
|
19
|
+
/** Human-readable label. */
|
|
20
|
+
readonly label: string;
|
|
21
|
+
/** Record path this object was extracted from (provenance). */
|
|
22
|
+
readonly source?: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** §2 — the eight relation types (standard-friendly names). */
|
|
25
|
+
export type RelationType = "causes" | "affects" | "depends-on" | "supersedes" | "contradicts" | "resolved-by" | "covered-by" | "recurs-as";
|
|
26
|
+
/** Where a relation came from (§4 / §5-bis provenance). */
|
|
27
|
+
export type RelationProvenance = "deterministic" | "llm" | "wikilink-promoted";
|
|
28
|
+
export interface OntRelation {
|
|
29
|
+
/** §5-bis: relId = hash(sourceId | rel | targetId) — the idempotent upsert key. */
|
|
30
|
+
readonly relId: string;
|
|
31
|
+
readonly rel: RelationType;
|
|
32
|
+
readonly sourceId: string;
|
|
33
|
+
readonly targetId: string;
|
|
34
|
+
readonly provenance: RelationProvenance;
|
|
35
|
+
/** 0..1; deterministic extraction = 1.0. */
|
|
36
|
+
readonly confidence: number;
|
|
37
|
+
/** ISO date a human confirmed it; null = proposed/unconfirmed. */
|
|
38
|
+
readonly confirmed?: string | null;
|
|
39
|
+
/** Record path / snippet pointer backing the relation. */
|
|
40
|
+
readonly evidence?: string | null;
|
|
41
|
+
}
|
|
42
|
+
/** §3 — alias → canonical object id (identity layer; replaces a `same-as` relation). */
|
|
43
|
+
export interface OntAlias {
|
|
44
|
+
readonly alias: string;
|
|
45
|
+
readonly canonicalId: string;
|
|
46
|
+
}
|
|
47
|
+
/** A single result of a multi-hop walk (§5B; capped at ≤ 3 hops, cycle-guarded). */
|
|
48
|
+
export interface WalkStep {
|
|
49
|
+
readonly nodeId: string;
|
|
50
|
+
readonly depth: number;
|
|
51
|
+
readonly path: readonly string[];
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AACnE,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;AAEjD,gFAAgF;AAChF,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE7F,MAAM,WAAW,SAAS;IACxB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,WAAW,GAAG,IAAI,CAAC;IACtD,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,aAAa,GACb,YAAY,GACZ,WAAW,CAAC;AAEhB,2DAA2D;AAC3D,MAAM,MAAM,kBAAkB,GAAG,eAAe,GAAG,KAAK,GAAG,mBAAmB,CAAC;AAE/E,MAAM,WAAW,WAAW;IAC1B,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,4CAA4C;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,wFAAwF;AACxF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,oFAAoF;AACpF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vortex-os/ontos — core types (Phase 1, text-first).
|
|
3
|
+
*
|
|
4
|
+
* Six object types, eight relation types. Object identity ("same-as") is handled
|
|
5
|
+
* by the alias layer (see the sqlite store), NOT as a graph relation — keeping
|
|
6
|
+
* dedup separate from `recurs-as` (pattern recurrence). See the maintainer's
|
|
7
|
+
* instance Phase 1 build spec for the full rationale.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vortex-os/ontos",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "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; no formal OWL/RDF reasoner.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "vortex-os-project",
|
|
7
|
+
"homepage": "https://github.com/vortex-os-project/vortex#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/vortex-os-project/vortex.git",
|
|
11
|
+
"directory": "modules/ontos"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./sqlite": {
|
|
22
|
+
"types": "./dist/sqlite/index.d.ts",
|
|
23
|
+
"default": "./dist/sqlite/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"scripts/rebuild-ontos.mjs",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"bin": {
|
|
32
|
+
"rebuild-ontos": "scripts/rebuild-ontos.mjs"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.json",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
37
|
+
"verify": "node scripts/verify.mjs",
|
|
38
|
+
"prepublishOnly": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"yaml": "^2.6.0"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@vortex-os/base": ">=0.3.0 <1.0.0",
|
|
45
|
+
"better-sqlite3": "^12.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"@vortex-os/base": {
|
|
49
|
+
"optional": false
|
|
50
|
+
},
|
|
51
|
+
"better-sqlite3": {
|
|
52
|
+
"optional": false
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
57
|
+
"better-sqlite3": "^12.10.0"
|
|
58
|
+
},
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Rebuild the ontos sqlite index from a VortEX instance's records (Phase 1,
|
|
3
|
+
// deterministic — no LLM, read-only on your files). Walks the record dirs,
|
|
4
|
+
// parses frontmatter, runs deterministic extraction, and upserts objects +
|
|
5
|
+
// typed relations. Untyped wikilink candidates are counted but not stored
|
|
6
|
+
// (they need human typing/confirmation — that lands with the confirm UX).
|
|
7
|
+
//
|
|
8
|
+
// Usage: rebuild-ontos [--data-dir <dir>] [--db <path>]
|
|
9
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
10
|
+
import { join, resolve } from "node:path";
|
|
11
|
+
import { OntosStore } from "../dist/sqlite/index.js";
|
|
12
|
+
import { extractDeterministic } from "../dist/extract/index.js";
|
|
13
|
+
import { parseFrontmatter } from "../dist/internal/frontmatter.js";
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
let dataDir = resolve(process.cwd(), "data");
|
|
17
|
+
let dbPath = null;
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
if (args[i] === "--data-dir" && args[i + 1]) dataDir = resolve(args[++i]);
|
|
20
|
+
else if (args[i] === "--db" && args[i + 1]) dbPath = resolve(args[++i]);
|
|
21
|
+
}
|
|
22
|
+
if (!dbPath) dbPath = join(dataDir, "_indexes", "ontos.sqlite");
|
|
23
|
+
|
|
24
|
+
const SOURCES = [
|
|
25
|
+
{ dir: join(dataDir, "_failures"), category: "failure" },
|
|
26
|
+
{ dir: join(dataDir, "decision-log"), category: "decision" },
|
|
27
|
+
{ dir: join(dataDir, "_memory"), category: "memory" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// FLAT listing (non-recursive): the extracted categories (_failures, decision-log,
|
|
31
|
+
// _memory) are flat dirs, so file basenames are unique within each — which keeps
|
|
32
|
+
// occurrence ids unique AND portable across machines (a full-path id would differ
|
|
33
|
+
// by OS path separator). Subdirectories are intentionally not descended.
|
|
34
|
+
function listMd(dir) {
|
|
35
|
+
let entries;
|
|
36
|
+
try {
|
|
37
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
38
|
+
} catch {
|
|
39
|
+
return []; // dir absent — skip
|
|
40
|
+
}
|
|
41
|
+
// isFile() is false for symlinks, so a symlinked *.md is not followed out of data/.
|
|
42
|
+
return entries
|
|
43
|
+
.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_INDEX.md")
|
|
44
|
+
.map((e) => join(dir, e.name));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const store = new OntosStore(dbPath);
|
|
48
|
+
let files = 0;
|
|
49
|
+
let skipped = 0;
|
|
50
|
+
let objects = 0;
|
|
51
|
+
let relations = 0;
|
|
52
|
+
let links = 0;
|
|
53
|
+
try {
|
|
54
|
+
// One transaction = a TRUE rebuild: clear first (drop stale rows from deleted/
|
|
55
|
+
// renamed records), then repopulate from the full walk. Atomic + fast.
|
|
56
|
+
store.transaction(() => {
|
|
57
|
+
store.clear();
|
|
58
|
+
for (const { dir, category } of SOURCES) {
|
|
59
|
+
for (const path of listMd(dir)) {
|
|
60
|
+
files++;
|
|
61
|
+
const { frontmatter, body } = parseFrontmatter(readFileSync(path, "utf8"));
|
|
62
|
+
const res = extractDeterministic({ path, category, frontmatter, body });
|
|
63
|
+
if (res.objects.length === 0) skipped++; // no recognizable fields (e.g. malformed/empty frontmatter)
|
|
64
|
+
for (const o of res.objects) {
|
|
65
|
+
store.upsertObject(o);
|
|
66
|
+
objects++;
|
|
67
|
+
}
|
|
68
|
+
for (const r of res.relations) {
|
|
69
|
+
store.upsertRelation(r);
|
|
70
|
+
relations++;
|
|
71
|
+
}
|
|
72
|
+
links += res.links.length;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
console.log(
|
|
77
|
+
`[rebuild-ontos] files=${files} skipped=${skipped} objects=${objects} relations=${relations} link-candidates=${links}`,
|
|
78
|
+
);
|
|
79
|
+
console.log(`[rebuild-ontos] db: ${store.dbPath}`);
|
|
80
|
+
} finally {
|
|
81
|
+
store.close();
|
|
82
|
+
}
|