@equationalapplications/core-llm-wiki 6.0.1 → 6.2.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 +81 -0
- package/dist/{chunk-HREQ6F6Z.mjs → chunk-YEESP6J2.mjs} +59 -6
- package/dist/chunk-YEESP6J2.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +61 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -7
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-DgqVB29I.d.mts → testing-D0m2V9-E.d.mts} +3 -0
- package/dist/{testing-DgqVB29I.d.ts → testing-D0m2V9-E.d.ts} +3 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +12 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-HREQ6F6Z.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -582,6 +582,86 @@ In **Strict** and **Emergent** modes, librarian and ingest JSON may include type
|
|
|
582
582
|
|
|
583
583
|
See the design spec: [`docs/superpowers/specs/2026-06-23-per-entity-seeded-ontology-design.md`](https://github.com/equationalapplications/expo-llm-wiki/blob/main/docs/superpowers/specs/2026-06-23-per-entity-seeded-ontology-design.md).
|
|
584
584
|
|
|
585
|
+
### Ontology type inheritance
|
|
586
|
+
|
|
587
|
+
`OntologyNodeType` accepts an optional `parent_type` field for **one-level type inheritance** — the building block for polymorphic queries like "all CreativeWorks" without deep, multi-level hierarchies.
|
|
588
|
+
|
|
589
|
+
```ts
|
|
590
|
+
// packages/core/src/types.ts
|
|
591
|
+
export interface OntologyNodeType {
|
|
592
|
+
type: string;
|
|
593
|
+
description: string;
|
|
594
|
+
/** Optional parent type slug. One level only — the parent must exist in the
|
|
595
|
+
* same manifest and must not itself declare a `parent_type`. */
|
|
596
|
+
parent_type?: string;
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
A concrete type declares `parent_type: '<parent-slug>'`; the parent must be a top-level node in the same manifest. A `design_spec` node with `parent_type: 'creativework'` is treated as both itself *and* a `creativework` during edge matching.
|
|
601
|
+
|
|
602
|
+
#### Inheritance rules (strictly enforced)
|
|
603
|
+
|
|
604
|
+
`validateManifest` enforces the **one-level invariant** at every persist and read (`entity_manifests.manifest_json` is `JSON.parse`d on every read, so validation runs on the read path too). Violations throw:
|
|
605
|
+
|
|
606
|
+
| Condition | Error |
|
|
607
|
+
|-----------|-------|
|
|
608
|
+
| `parent_type` references an unknown slug | `Parent type not found: <slug>` |
|
|
609
|
+
| `parent_type` equals the node's own slug | `Self-parent: <type>` |
|
|
610
|
+
| The referenced parent also declares a `parent_type` | `Parent chain too deep: <a> → <b> → <grandparent>` |
|
|
611
|
+
| `parent_type` is present but blank (`''` / whitespace) or non-string (`number`, `null`, `object`) | `Ontology parent_type must be a non-empty string when present: <type>` |
|
|
612
|
+
|
|
613
|
+
Two consequences worth knowing:
|
|
614
|
+
|
|
615
|
+
- The depth check rejects **2-cycles** (`a → b → a`) which a self-parent check alone would miss.
|
|
616
|
+
- A *present* `parent_type` key is required to be a usable string; an *absent* key (`undefined`) is fine and means "no parent". This is the same rule core applies to blank slugs elsewhere (`Ontology node type slug must be non-empty`).
|
|
617
|
+
|
|
618
|
+
Parent types are **instantiable** — a fact may be classified as bare `creativework`, and `resolveNodeType('creativework')` returns `'creativework'` normally. There is no `abstract` flag; if you want a parent to stay abstract, write the description to steer classification toward the concrete children.
|
|
619
|
+
|
|
620
|
+
#### Edge matching (symmetric, exact-first)
|
|
621
|
+
|
|
622
|
+
Edge matching is parent-aware on **both** sides and routes through a single primitive, `typeSatisfies(declaredType, concreteType, manifest)`:
|
|
623
|
+
|
|
624
|
+
- `declaredType === concreteType` (case-insensitive) → **exact match**, short-circuits before the node lookup. Manifests with no `parent_type` behave bit-for-bit as before.
|
|
625
|
+
- Otherwise: looks up `concreteType`'s definition in the manifest and returns `true` if its `parent_type` equals `declaredType`. One hop only — `typeSatisfies` never recurses.
|
|
626
|
+
|
|
627
|
+
`typeSatisfies` is used at four gates — `validateInlineEdges` (source), `OntologyService.resolveEdges` (source and target, via two-pass), `IngestionService.upsertGraph` (source) — so a parent-satisfied edge that validates will also persist.
|
|
628
|
+
|
|
629
|
+
**Targets resolve exact-first.** When resolving an edge against a target fact, `OntologyService.resolveEdges` runs two passes over candidate defs:
|
|
630
|
+
|
|
631
|
+
```ts
|
|
632
|
+
// Packages/core/src/services/OntologyService.ts (abridged)
|
|
633
|
+
const targetType = (target.okf_type ?? '').trim().toLowerCase();
|
|
634
|
+
const def = candidates.find(d => d.target_type.trim().toLowerCase() === targetType)
|
|
635
|
+
?? candidates.find(d => typeSatisfies(d.target_type, targetType, manifest));
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
The exact pass runs first, so a `design_spec` target with both `about creativework → creativework` and `about creativework → design_spec` declared resolves to the **narrower** row — array order never decides which pass wins. Ties within a single pass are immaterial: only `def.type` is read off the winning def, and `validateManifest` already rejects a manifest whose triples spell one edge name with different casing, so every candidate within a pass yields a byte-identical edge row.
|
|
639
|
+
|
|
640
|
+
> **Accepted cost:** declaring `→ creativework` now admits every child of `creativework`, and there is no way to say "the parent type only." A type that needs an exact-only target must not be given children.
|
|
641
|
+
|
|
642
|
+
#### Emergent prompt schema
|
|
643
|
+
|
|
644
|
+
In `emergent` mode the LLM may propose new types via `ontology_updates`. The prompt schema (`EMERGENT_EXTRA` in `packages/core/src/prompts/ontology.ts`) advertises the optional `parent_type` so emergent proposals can suggest children of an existing type:
|
|
645
|
+
|
|
646
|
+
```json
|
|
647
|
+
"ontology_updates": {
|
|
648
|
+
"node_types": [{ "type": "slug", "description": "...", "parent_type": "optional existing slug" }],
|
|
649
|
+
"edge_types": [{ "type": "slug", "source_type": "...", "target_type": "...", "description": "..." }]
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
The rest of the manifest reaches the LLM unchanged — `buildPromptContext` does `JSON.stringify(manifest, null, 2)`, so an established manifest's `parent_type` fields appear verbatim alongside `type` and `description`.
|
|
654
|
+
|
|
655
|
+
Emergent proposals are **untrusted input**: `mergeOntologyUpdates` drops any `parent_type` (rather than throwing) when it is a non-string, blank, unresolvable, self-referential, or whose referenced parent already declares its own parent. The lenient merge contract keeps malformed LLM proposals from aborting an ingest transaction. Changing an established type's parent is a `setOntologyManifest` operation.
|
|
656
|
+
|
|
657
|
+
#### Backwards compatibility
|
|
658
|
+
|
|
659
|
+
`parent_type` is optional and manifests persist as a whole JSON blob in `entity_manifests.manifest_json`. Existing manifests without the field validate identically; no SQLite migration is needed. `setOntologyManifest` rejects a two-level chain at the public API boundary, so callers cannot accidentally introduce an unenforced chain through a typo.
|
|
660
|
+
|
|
661
|
+
`typeSatisfies` is intentionally **not** re-exported from the package's public surface (`packages/core/src/index.ts`). No host needs the primitive to author or validate a manifest; publishing it would freeze an internal matching rule into the package's public surface.
|
|
662
|
+
|
|
663
|
+
See the design spec: [`docs/superpowers/specs/2026-08-28-ontology-parent-field-spec.md`](https://github.com/equationalapplications/expo-llm-wiki/blob/main/docs/superpowers/specs/2026-08-28-ontology-parent-field-spec.md).
|
|
664
|
+
|
|
585
665
|
### Ontology backfill
|
|
586
666
|
|
|
587
667
|
Facts that enter the store without passing through the librarian (synced-down
|
|
@@ -1131,6 +1211,7 @@ The flowchart shows:
|
|
|
1131
1211
|
| [@equationalapplications/core-llm-tools](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core-llm-tools/README.md) | Gemini tool schemas and capability injector |
|
|
1132
1212
|
| [@equationalapplications/core-okf](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/okf/README.md) | Zero-dependency Open Knowledge Format (OKF) v0.1 + v0.2 primitives — parse and produce interoperable knowledge bundles. |
|
|
1133
1213
|
| [@equationalapplications/schema-org-llm-wiki](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/schema-org/README.md) | Curated schema.org warm-agent ontology manifest |
|
|
1214
|
+
| [@equationalapplications/schema-software-org](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/schema-software-org/README.md) | Software-organization executive ontology manifest — 17 node types, 40 edges, warm-agent superset, data-only |
|
|
1134
1215
|
|
|
1135
1216
|
## OKF v0.2 conformance (llm-wiki/2)
|
|
1136
1217
|
|
|
@@ -190,6 +190,17 @@ function resolveEdgeDefinitions(rawEdgeType, manifest) {
|
|
|
190
190
|
function edgeTripleKey(type, sourceType, targetType) {
|
|
191
191
|
return `${type.trim().toLowerCase()}|${sourceType.trim().toLowerCase()}|${targetType.trim().toLowerCase()}`;
|
|
192
192
|
}
|
|
193
|
+
function typeSatisfies(declaredType, concreteType, manifest) {
|
|
194
|
+
const concrete = concreteType.trim().toLowerCase();
|
|
195
|
+
const declared = declaredType.trim().toLowerCase();
|
|
196
|
+
if (!concrete || !declared) return false;
|
|
197
|
+
if (declared === concrete) return true;
|
|
198
|
+
const def = (manifest.node_types ?? []).find(
|
|
199
|
+
(n) => typeof n?.type === "string" && n.type.trim().toLowerCase() === concrete
|
|
200
|
+
);
|
|
201
|
+
const parent = typeof def?.parent_type === "string" ? def.parent_type.trim().toLowerCase() : "";
|
|
202
|
+
return parent !== "" && parent === declared;
|
|
203
|
+
}
|
|
193
204
|
function validateManifest(manifest) {
|
|
194
205
|
const nodeSlugs = /* @__PURE__ */ new Set();
|
|
195
206
|
for (const node of manifest.node_types ?? []) {
|
|
@@ -199,6 +210,28 @@ function validateManifest(manifest) {
|
|
|
199
210
|
if (nodeSlugs.has(key)) throw new Error(`Duplicate node type: ${type}`);
|
|
200
211
|
nodeSlugs.add(key);
|
|
201
212
|
}
|
|
213
|
+
const parentOf = /* @__PURE__ */ new Map();
|
|
214
|
+
for (const node of manifest.node_types ?? []) {
|
|
215
|
+
const parent = typeof node.parent_type === "string" ? node.parent_type.trim().toLowerCase() : void 0;
|
|
216
|
+
parentOf.set(node.type.trim().toLowerCase(), parent);
|
|
217
|
+
}
|
|
218
|
+
for (const node of manifest.node_types ?? []) {
|
|
219
|
+
if (node.parent_type === void 0) continue;
|
|
220
|
+
if (typeof node.parent_type !== "string" || !node.parent_type.trim()) {
|
|
221
|
+
throw new Error(`Ontology parent_type must be a non-empty string when present: ${node.type}`);
|
|
222
|
+
}
|
|
223
|
+
const parentSlug = node.parent_type.trim().toLowerCase();
|
|
224
|
+
if (parentSlug === node.type.trim().toLowerCase()) {
|
|
225
|
+
throw new Error(`Self-parent: ${node.type}`);
|
|
226
|
+
}
|
|
227
|
+
if (!nodeSlugs.has(parentSlug)) {
|
|
228
|
+
throw new Error(`Parent type not found: ${node.parent_type}`);
|
|
229
|
+
}
|
|
230
|
+
const grandparent = parentOf.get(parentSlug);
|
|
231
|
+
if (grandparent) {
|
|
232
|
+
throw new Error(`Parent chain too deep: ${node.type} \u2192 ${node.parent_type} \u2192 ${grandparent}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
202
235
|
const edgeKeys = /* @__PURE__ */ new Set();
|
|
203
236
|
const edgeNames = /* @__PURE__ */ new Map();
|
|
204
237
|
for (const edge of manifest.edge_types ?? []) {
|
|
@@ -222,18 +255,38 @@ function validateManifest(manifest) {
|
|
|
222
255
|
}
|
|
223
256
|
}
|
|
224
257
|
}
|
|
258
|
+
function buildDeclaresParentIndex(nodes) {
|
|
259
|
+
const declaresParent = /* @__PURE__ */ new Map();
|
|
260
|
+
for (const n of nodes) {
|
|
261
|
+
const slug = typeof n?.type === "string" ? n.type.trim().toLowerCase() : "";
|
|
262
|
+
if (!slug || declaresParent.has(slug)) continue;
|
|
263
|
+
declaresParent.set(slug, n?.parent_type !== void 0);
|
|
264
|
+
}
|
|
265
|
+
return declaresParent;
|
|
266
|
+
}
|
|
225
267
|
function mergeOntologyUpdates(current, updates) {
|
|
226
268
|
const node_types = [...current.node_types];
|
|
227
269
|
const edge_types = [...current.edge_types];
|
|
228
270
|
const nodeSlugs = new Set(node_types.map((n) => n.type.trim().toLowerCase()));
|
|
229
271
|
const edgeKeys = new Set(edge_types.map((e) => edgeTripleKey(e.type, e.source_type, e.target_type)));
|
|
230
272
|
const edgeNames = new Map(edge_types.map((e) => [e.type.trim().toLowerCase(), e.type.trim()]));
|
|
273
|
+
const declaresParent = buildDeclaresParentIndex([
|
|
274
|
+
...current.node_types,
|
|
275
|
+
...updates.node_types ?? []
|
|
276
|
+
]);
|
|
231
277
|
for (const node of updates.node_types ?? []) {
|
|
232
278
|
const type = node?.type?.trim();
|
|
233
279
|
if (!type) continue;
|
|
234
280
|
const key = type.toLowerCase();
|
|
235
281
|
if (nodeSlugs.has(key)) continue;
|
|
236
|
-
|
|
282
|
+
const rawParent = typeof node?.parent_type === "string" ? node.parent_type.trim() : "";
|
|
283
|
+
const parentSlug = rawParent.toLowerCase();
|
|
284
|
+
const keepParent = parentSlug !== "" && parentSlug !== key && declaresParent.get(parentSlug) === false;
|
|
285
|
+
node_types.push({
|
|
286
|
+
type,
|
|
287
|
+
description: String(node.description ?? ""),
|
|
288
|
+
...keepParent ? { parent_type: rawParent } : {}
|
|
289
|
+
});
|
|
237
290
|
nodeSlugs.add(key);
|
|
238
291
|
}
|
|
239
292
|
for (const edge of updates.edge_types ?? []) {
|
|
@@ -270,7 +323,7 @@ function validateInlineEdges(sourceType, _targetType, edges, manifest, opts) {
|
|
|
270
323
|
continue;
|
|
271
324
|
}
|
|
272
325
|
const defs = resolveEdgeDefinitions(edge.edge_type, manifest);
|
|
273
|
-
const match = defs.find((d) => d.source_type
|
|
326
|
+
const match = defs.find((d) => typeSatisfies(d.source_type, sourceType, manifest));
|
|
274
327
|
if (!match) {
|
|
275
328
|
if (strict) throw new WikiStrictOntologyViolation(entityId, "edge", edge.edge_type);
|
|
276
329
|
continue;
|
|
@@ -1946,7 +1999,7 @@ var IngestionService = class {
|
|
|
1946
1999
|
}
|
|
1947
2000
|
const sourceType = sourceIdToType.get(edge.sourceId);
|
|
1948
2001
|
const candidates = (manifest.edge_types ?? []).filter(
|
|
1949
|
-
(d) => d.type.toLowerCase() === edge.type.toLowerCase() && d.source_type
|
|
2002
|
+
(d) => d.type.toLowerCase() === edge.type.toLowerCase() && typeSatisfies(d.source_type, sourceType ?? "", manifest)
|
|
1950
2003
|
);
|
|
1951
2004
|
const match = candidates[0];
|
|
1952
2005
|
if (!match) {
|
|
@@ -4566,6 +4619,6 @@ var WriteService = class {
|
|
|
4566
4619
|
}
|
|
4567
4620
|
};
|
|
4568
4621
|
|
|
4569
|
-
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, EmbeddingService, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ImportExportService, IngestionService, JobManager, MaintenanceService, MetadataRepository, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, RetrievalService, SearchService, WikiBusyError, WikiDuplicateHashError, WikiIngestEmptyError, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, validateInlineEdges, validateManifest };
|
|
4570
|
-
//# sourceMappingURL=chunk-
|
|
4571
|
-
//# sourceMappingURL=chunk-
|
|
4622
|
+
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, EmbeddingService, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ImportExportService, IngestionService, JobManager, MaintenanceService, MetadataRepository, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, RetrievalService, SearchService, WikiBusyError, WikiDuplicateHashError, WikiIngestEmptyError, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, typeSatisfies, validateInlineEdges, validateManifest };
|
|
4623
|
+
//# sourceMappingURL=chunk-YEESP6J2.mjs.map
|
|
4624
|
+
//# sourceMappingURL=chunk-YEESP6J2.mjs.map
|