@equationalapplications/core-llm-wiki 7.5.0 → 7.7.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 +93 -0
- package/dist/{chunk-3P7FAKJA.mjs → chunk-VOSMYISQ.mjs} +243 -41
- package/dist/chunk-VOSMYISQ.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +434 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +196 -5
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-ClpOUiLI.d.mts → testing-A4Sofxyo.d.mts} +136 -4
- package/dist/{testing-ClpOUiLI.d.ts → testing-A4Sofxyo.d.ts} +136 -4
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +236 -38
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-3P7FAKJA.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, WikiSourceRefHashCollision, DiagnosticBuffer, validateManifest, WikiDraftNotFound, generateId, BaseRepository, WikiGraphNodeOwnershipConflict, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-
|
|
2
|
-
export { DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiDraftNotFound, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-
|
|
1
|
+
import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, resolveGrounding, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, WikiSourceRefHashCollision, DiagnosticBuffer, validateManifest, WikiDraftNotFound, generateId, BaseRepository, WikiGraphNodeOwnershipConflict, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, edgeTripleAllowed, WikiTransactionError, extractSqliteCode } from './chunk-VOSMYISQ.mjs';
|
|
2
|
+
export { DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiDraftNotFound, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-VOSMYISQ.mjs';
|
|
3
3
|
import { appendRelatedSection, buildConceptDocument, buildLogMd, buildEntityIndexMd, buildRootIndexMd, isAllowedOkfPath, parseRootIndexMd, parseEntityIndexMd, parseConcept, splitRelatedSection, extractMarkdownLinks, parseLogMd, parseEventIdComment, latestVerified, formatSourcesJson, appendEventIdComment, parseVerifiedFlexible, parseCitationsList, deriveTrustTier, isStaleAfter } from '@equationalapplications/core-okf';
|
|
4
4
|
|
|
5
5
|
// src/db/schema.ts
|
|
@@ -948,15 +948,16 @@ var EntryRepository = class extends BaseRepository {
|
|
|
948
948
|
* fact, not only immutable_document rows, so the source-type restriction has
|
|
949
949
|
* to be applied here, after retrieval.
|
|
950
950
|
*/
|
|
951
|
-
async findAnchorRowsByIds(entityId, ids, tx) {
|
|
951
|
+
async findAnchorRowsByIds(entityId, ids, tx, opts) {
|
|
952
952
|
if (ids.length === 0) return [];
|
|
953
953
|
const executor = this.getExecutor(tx);
|
|
954
|
+
const columns = opts?.withBody ? "id, title, source_ref, body, lifecycle_status" : "id, title, source_ref";
|
|
954
955
|
const rows = [];
|
|
955
956
|
for (let i = 0; i < ids.length; i += this.chunkSize) {
|
|
956
957
|
const chunk = ids.slice(i, i + this.chunkSize);
|
|
957
958
|
const placeholders = chunk.map(() => "?").join(", ");
|
|
958
959
|
const chunkRows = await executor.getAllAsync(
|
|
959
|
-
`SELECT
|
|
960
|
+
`SELECT ${columns} FROM ${this.prefix}entries
|
|
960
961
|
WHERE entity_id = ? AND deleted_at IS NULL
|
|
961
962
|
AND source_type = 'immutable_document'
|
|
962
963
|
AND id IN (${placeholders})`,
|
|
@@ -1395,6 +1396,35 @@ var EntryRepository = class extends BaseRepository {
|
|
|
1395
1396
|
}
|
|
1396
1397
|
return out;
|
|
1397
1398
|
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Per source ref, the hash of the latest live row (same ordering as
|
|
1401
|
+
* {@link findLatestSourceHashes}) and whether ANY live row carries a hash.
|
|
1402
|
+
* Refs with no live row are absent. Chunked under the bind-variable limit.
|
|
1403
|
+
*/
|
|
1404
|
+
async findSourceStates(entityId, sourceRefs, tx) {
|
|
1405
|
+
const out = /* @__PURE__ */ new Map();
|
|
1406
|
+
const deduped = Array.from(new Set(sourceRefs));
|
|
1407
|
+
if (deduped.length === 0) return out;
|
|
1408
|
+
const executor = this.getExecutor(tx);
|
|
1409
|
+
const chunkLimit = Math.max(1, this.chunkSize - 1);
|
|
1410
|
+
for (let i = 0; i < deduped.length; i += chunkLimit) {
|
|
1411
|
+
const chunk = deduped.slice(i, i + chunkLimit);
|
|
1412
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
1413
|
+
const rows = await executor.getAllAsync(
|
|
1414
|
+
`WITH ranked AS (
|
|
1415
|
+
SELECT source_ref, source_hash,
|
|
1416
|
+
ROW_NUMBER() OVER (PARTITION BY source_ref ORDER BY updated_at DESC, id ASC) AS rn,
|
|
1417
|
+
MAX(CASE WHEN source_hash IS NOT NULL THEN 1 ELSE 0 END) OVER (PARTITION BY source_ref) AS any_hashed
|
|
1418
|
+
FROM ${this.prefix}entries
|
|
1419
|
+
WHERE entity_id = ? AND source_ref IN (${placeholders}) AND deleted_at IS NULL
|
|
1420
|
+
)
|
|
1421
|
+
SELECT source_ref, source_hash, any_hashed FROM ranked WHERE rn = 1`,
|
|
1422
|
+
[entityId, ...chunk]
|
|
1423
|
+
);
|
|
1424
|
+
for (const r of rows) out.set(r.source_ref, { latestHash: r.source_hash, anyHashed: Number(r.any_hashed) === 1 });
|
|
1425
|
+
}
|
|
1426
|
+
return out;
|
|
1427
|
+
}
|
|
1398
1428
|
/**
|
|
1399
1429
|
* Return the live source_refs for an entity that hold the given source_hash.
|
|
1400
1430
|
* Used by the ingestDocument duplicate-hash guard and by hosts auditing
|
|
@@ -2654,6 +2684,105 @@ var GraphTraversalService = class {
|
|
|
2654
2684
|
return { nodes, edges: filteredEdges };
|
|
2655
2685
|
}
|
|
2656
2686
|
};
|
|
2687
|
+
|
|
2688
|
+
// src/repositories/LintRepository.ts
|
|
2689
|
+
var LintRepository = class extends BaseRepository {
|
|
2690
|
+
async countFactHealth(entityId) {
|
|
2691
|
+
const row = await this.db.getFirstAsync(
|
|
2692
|
+
`SELECT
|
|
2693
|
+
SUM(CASE WHEN okf_type IS NULL THEN 1 ELSE 0 END) AS untyped,
|
|
2694
|
+
SUM(CASE WHEN lifecycle_status = 'draft' THEN 1 ELSE 0 END) AS drafts,
|
|
2695
|
+
SUM(CASE WHEN source_type = 'librarian_inferred'
|
|
2696
|
+
AND COALESCE(json_array_length(CASE WHEN json_valid(okf_verified) THEN okf_verified END), 0) = 0
|
|
2697
|
+
THEN 1 ELSE 0 END) AS unverified
|
|
2698
|
+
FROM ${this.prefix}entries
|
|
2699
|
+
WHERE entity_id = ? AND deleted_at IS NULL`,
|
|
2700
|
+
[entityId]
|
|
2701
|
+
);
|
|
2702
|
+
return {
|
|
2703
|
+
untypedFacts: Number(row?.untyped ?? 0),
|
|
2704
|
+
drafts: Number(row?.drafts ?? 0),
|
|
2705
|
+
unverifiedInferred: Number(row?.unverified ?? 0)
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
danglingWhere() {
|
|
2709
|
+
const live = (col) => `EXISTS (SELECT 1 FROM ${this.prefix}entries n WHERE n.id = e.${col} AND n.entity_id = e.entity_id AND n.deleted_at IS NULL)`;
|
|
2710
|
+
return `e.entity_id = ? AND (NOT ${live("source_id")} OR NOT ${live("target_id")})`;
|
|
2711
|
+
}
|
|
2712
|
+
async countDanglingEdges(entityId) {
|
|
2713
|
+
const row = await this.db.getFirstAsync(
|
|
2714
|
+
`SELECT COUNT(*) AS n FROM ${this.prefix}edges e WHERE ${this.danglingWhere()}`,
|
|
2715
|
+
[entityId]
|
|
2716
|
+
);
|
|
2717
|
+
return Number(row?.n ?? 0);
|
|
2718
|
+
}
|
|
2719
|
+
async sampleDanglingEdgeIds(entityId, limit) {
|
|
2720
|
+
const rows = await this.db.getAllAsync(
|
|
2721
|
+
`SELECT e.id FROM ${this.prefix}edges e WHERE ${this.danglingWhere()} ORDER BY e.id ASC LIMIT ?`,
|
|
2722
|
+
[entityId, limit]
|
|
2723
|
+
);
|
|
2724
|
+
return rows.map((r) => r.id);
|
|
2725
|
+
}
|
|
2726
|
+
/** Keyset page of edges whose endpoints are both live in this entity, with endpoint types. */
|
|
2727
|
+
async pageLiveEdges(entityId, afterId, limit) {
|
|
2728
|
+
return this.db.getAllAsync(
|
|
2729
|
+
`SELECT e.id, e.edge_type, s.okf_type AS source_type, t.okf_type AS target_type
|
|
2730
|
+
FROM ${this.prefix}edges e
|
|
2731
|
+
JOIN ${this.prefix}entries s ON s.id = e.source_id AND s.entity_id = e.entity_id AND s.deleted_at IS NULL
|
|
2732
|
+
JOIN ${this.prefix}entries t ON t.id = e.target_id AND t.entity_id = e.entity_id AND t.deleted_at IS NULL
|
|
2733
|
+
WHERE e.entity_id = ? AND e.id > ?
|
|
2734
|
+
ORDER BY e.id ASC
|
|
2735
|
+
LIMIT ?`,
|
|
2736
|
+
[entityId, afterId, limit]
|
|
2737
|
+
);
|
|
2738
|
+
}
|
|
2739
|
+
};
|
|
2740
|
+
|
|
2741
|
+
// src/services/LintService.ts
|
|
2742
|
+
var LINT_SAMPLE_SIZE = 20;
|
|
2743
|
+
var LINT_PAGE_SIZE = 500;
|
|
2744
|
+
var LintService = class {
|
|
2745
|
+
constructor(lintRepo, ontologyService) {
|
|
2746
|
+
this.lintRepo = lintRepo;
|
|
2747
|
+
this.ontologyService = ontologyService;
|
|
2748
|
+
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Read-only (spec §8.1). Manifest violations stream the entity's live-endpoint
|
|
2751
|
+
* edges in keyset pages and check each triple in JS against the effective
|
|
2752
|
+
* manifest. `getEffectiveState` is called without a transaction, so a seed
|
|
2753
|
+
* manifest is cached, never persisted. Counts come from separate statements,
|
|
2754
|
+
* not one snapshot.
|
|
2755
|
+
*/
|
|
2756
|
+
async lint(entityId) {
|
|
2757
|
+
const health = await this.lintRepo.countFactHealth(entityId);
|
|
2758
|
+
const danglingEdges = await this.lintRepo.countDanglingEdges(entityId);
|
|
2759
|
+
const danglingEdgeIds = danglingEdges > 0 ? await this.lintRepo.sampleDanglingEdgeIds(entityId, LINT_SAMPLE_SIZE) : [];
|
|
2760
|
+
const { mode, manifest } = await this.ontologyService.getEffectiveState(entityId);
|
|
2761
|
+
const constrained = mode !== "off" && ((manifest.node_types?.length ?? 0) > 0 || (manifest.edge_types?.length ?? 0) > 0);
|
|
2762
|
+
let manifestViolations = 0;
|
|
2763
|
+
const manifestViolationEdgeIds = [];
|
|
2764
|
+
if (constrained) {
|
|
2765
|
+
let after = "";
|
|
2766
|
+
for (; ; ) {
|
|
2767
|
+
const page = await this.lintRepo.pageLiveEdges(entityId, after, LINT_PAGE_SIZE);
|
|
2768
|
+
for (const e of page) {
|
|
2769
|
+
if (!edgeTripleAllowed(manifest, e.edge_type, e.source_type ?? "", e.target_type ?? "")) {
|
|
2770
|
+
manifestViolations++;
|
|
2771
|
+
if (manifestViolationEdgeIds.length < LINT_SAMPLE_SIZE) manifestViolationEdgeIds.push(e.id);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
if (page.length < LINT_PAGE_SIZE) break;
|
|
2775
|
+
after = page[page.length - 1].id;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
return {
|
|
2779
|
+
danglingEdges,
|
|
2780
|
+
manifestViolations,
|
|
2781
|
+
...health,
|
|
2782
|
+
sample: { danglingEdgeIds, manifestViolationEdgeIds }
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
};
|
|
2657
2786
|
var OkfTrustWritesRepository = class {
|
|
2658
2787
|
constructor(db, prefix) {
|
|
2659
2788
|
this.db = db;
|
|
@@ -2844,15 +2973,17 @@ var WikiMemory = class {
|
|
|
2844
2973
|
this.eventRepo = new EventRepository(this.db, this.prefix);
|
|
2845
2974
|
this.edgeRepo = new EdgeRepository(this.db, this.prefix);
|
|
2846
2975
|
this.metadataRepo = new MetadataRepository(this.db, this.prefix);
|
|
2976
|
+
this.lintRepo = new LintRepository(this.db, this.prefix);
|
|
2847
2977
|
this.ontologyService = new OntologyService(
|
|
2848
2978
|
this.metadataRepo,
|
|
2849
2979
|
this.edgeRepo,
|
|
2850
2980
|
options.config?.ontology
|
|
2851
2981
|
);
|
|
2982
|
+
this.lintService = new LintService(this.lintRepo, this.ontologyService);
|
|
2852
2983
|
this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
|
|
2853
2984
|
this.searchService = new SearchService(this.entryRepo);
|
|
2854
2985
|
this.jobManager = new JobManager(this.prefix);
|
|
2855
|
-
this.promptService = new PromptService(options.config?.prompts);
|
|
2986
|
+
this.promptService = new PromptService(options.config?.prompts, resolveGrounding(options.config?.grounding));
|
|
2856
2987
|
this.ingestionService = new IngestionService(
|
|
2857
2988
|
this.db,
|
|
2858
2989
|
this.prefix,
|
|
@@ -3031,6 +3162,61 @@ var WikiMemory = class {
|
|
|
3031
3162
|
return { sourceRef: e.rawSourceRef, changed, duplicateOf: canonical };
|
|
3032
3163
|
});
|
|
3033
3164
|
}
|
|
3165
|
+
/**
|
|
3166
|
+
* Batch pending state per source (spec §8.2). `current` exactly when
|
|
3167
|
+
* `hasChanged` is false; `partial` when live rows exist but none carries a
|
|
3168
|
+
* hash (a first ingest with a failed chunk, or imported rows without a
|
|
3169
|
+
* hash). A failed re-ingest of an already-hashed ref reports `changed`. Validation and raw-ref echo match
|
|
3170
|
+
* batched `hasChanged`. Input order and duplicates are preserved. A
|
|
3171
|
+
* non-array `sources`, or an entry that is not an object (including a hole
|
|
3172
|
+
* in a sparse array), throws a `TypeError` before any SQL runs.
|
|
3173
|
+
*/
|
|
3174
|
+
async pendingSources(entityId, sources) {
|
|
3175
|
+
assertEntityId(entityId);
|
|
3176
|
+
if (!Array.isArray(sources)) {
|
|
3177
|
+
throw new TypeError("Invalid sources: must be an array of { sourceRef, sourceHash } objects.");
|
|
3178
|
+
}
|
|
3179
|
+
if (sources.length === 0) return [];
|
|
3180
|
+
const normalized = Array.from(sources, (s, i) => {
|
|
3181
|
+
if (s === null || typeof s !== "object") {
|
|
3182
|
+
throw new TypeError(`Invalid sources[${i}]: must be a { sourceRef, sourceHash } object.`);
|
|
3183
|
+
}
|
|
3184
|
+
const { sourceRef: rawSourceRef, sourceHash: rawSourceHash } = s;
|
|
3185
|
+
const sourceRef = normalizeSourceRef(rawSourceRef);
|
|
3186
|
+
if (!sourceRef) {
|
|
3187
|
+
const shown = typeof rawSourceRef === "string" ? JSON.stringify(rawSourceRef) : `<${typeof rawSourceRef}>`;
|
|
3188
|
+
throw new Error(`Invalid sourceRef: ${shown}`);
|
|
3189
|
+
}
|
|
3190
|
+
const sourceHash = normalizeSourceHash(rawSourceHash);
|
|
3191
|
+
if (!sourceHash) throw new Error("Invalid sourceHash: must be a 64-character hex string (normalized to lowercase)");
|
|
3192
|
+
return { rawSourceRef, sourceRef, sourceHash };
|
|
3193
|
+
});
|
|
3194
|
+
const states = await this.entryRepo.findSourceStates(entityId, normalized.map((n) => n.sourceRef));
|
|
3195
|
+
return normalized.map((n) => {
|
|
3196
|
+
const state = states.get(n.sourceRef);
|
|
3197
|
+
let status;
|
|
3198
|
+
if (!state) status = "new";
|
|
3199
|
+
else if (!state.anyHashed) status = "partial";
|
|
3200
|
+
else if (state.latestHash !== null && normalizeSourceHash(state.latestHash) === n.sourceHash) status = "current";
|
|
3201
|
+
else status = "changed";
|
|
3202
|
+
return { sourceRef: n.rawSourceRef, status };
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
3205
|
+
/** Read-only maintenance report for one entity (spec §8.1). Reports, never repairs. */
|
|
3206
|
+
async lint(entityId) {
|
|
3207
|
+
assertEntityId(entityId);
|
|
3208
|
+
return this.lintService.lint(entityId);
|
|
3209
|
+
}
|
|
3210
|
+
/**
|
|
3211
|
+
* Effective system prompts for ingest, librarian, heal and ontology backfill
|
|
3212
|
+
* (spec §8.3). Backs the `wiki_get_instructions` tool. Templates only;
|
|
3213
|
+
* overrides are returned verbatim, so keep secrets out of `WikiConfig.prompts`.
|
|
3214
|
+
*/
|
|
3215
|
+
async getInstructions(entityId) {
|
|
3216
|
+
assertEntityId(entityId);
|
|
3217
|
+
const ontologyContext = await this.ontologyService.buildPromptContext(entityId);
|
|
3218
|
+
return this.promptService.buildInstructionTemplates(ontologyContext);
|
|
3219
|
+
}
|
|
3034
3220
|
/**
|
|
3035
3221
|
* Returns the live source_refs for an entity, one row per ref, with the most
|
|
3036
3222
|
* recently-updated live `source_hash` and a live fact count. Refs are sorted
|
|
@@ -3431,6 +3617,11 @@ var WikiMemory = class {
|
|
|
3431
3617
|
}
|
|
3432
3618
|
};
|
|
3433
3619
|
_testAccessNonTestEnvWarned = new WeakMap();
|
|
3620
|
+
function assertEntityId(entityId) {
|
|
3621
|
+
if (typeof entityId !== "string" || entityId.length === 0) {
|
|
3622
|
+
throw new TypeError("Invalid entityId: must be a non-empty string.");
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3434
3625
|
|
|
3435
3626
|
// src/utils/formatContext.ts
|
|
3436
3627
|
function validateMaxOption(value, name) {
|