@equationalapplications/core-llm-wiki 7.1.2 → 7.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/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, validateManifest, generateId, BaseRepository, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-ZU7K6NAU.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, WikiDuplicateHashError, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-ZU7K6NAU.mjs';
1
+ import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, WikiSourceRefHashCollision, validateManifest, generateId, BaseRepository, WikiGraphNodeOwnershipConflict, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-TTECJ3S3.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, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-TTECJ3S3.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
@@ -591,6 +591,26 @@ var EntryRepository = class extends BaseRepository {
591
591
  * diagnostic that inflates runReembed's permanentlyFailed counter. A conflict
592
592
  * with no blob leaves marker state alone, matching the "absent means don't
593
593
  * touch" semantics used for embedding_blob itself.
594
+ *
595
+ * Graph node ownership (spec §2.4 + §3, REQ-OWN-01 + REQ-GRAPH-01): entry
596
+ * IDs share one database-wide namespace, so an ID stays owned by its
597
+ * existing entity for as long as the row exists — soft deletion included.
598
+ * The pre-write lookup already reads the existing row for OKF metadata; we
599
+ * project `entity_id` off that same SELECT and reject the write before
600
+ * any outbox payload construction when the stored owner differs from
601
+ * `fact.entity_id`. Soft-deleted foreign rows keep their IDs reserved;
602
+ * permanent reservation is NOT introduced, so a physically absent ID may
603
+ * still be inserted under the requested entity. The ON CONFLICT UPDATE SET
604
+ * clause carries a final `WHERE entries.entity_id = excluded.entity_id`
605
+ * guard so the database itself refuses a cross-owner row overwrite even if
606
+ * a future caller bypasses the pre-write check. When `runAsync` reports
607
+ * 0 changes (a legitimate conflict-suppressed write, OR an adapter that
608
+ * under-reports a permitted write) we re-read the stored owner; only a
609
+ * foreign owner throws — this is local defensive behavior, not permission
610
+ * for adapters to under-report. The thrown error is
611
+ * `WikiGraphNodeOwnershipConflict` (no owner, node ID, or stored content
612
+ * is disclosed). Upserts that pass both gates retain existing same-entity
613
+ * update + resurrection semantics.
594
614
  */
595
615
  async upsert(fact, tx) {
596
616
  const executor = this.getExecutor(tx);
@@ -598,12 +618,15 @@ var EntryRepository = class extends BaseRepository {
598
618
  const tagsJson = JSON.stringify(fact.tags);
599
619
  const embeddingBlob = this.normalizeEmbeddingBlob(fact.embedding_blob);
600
620
  const existingRow = await executor.getFirstAsync(
601
- `SELECT id, lifecycle_status, stale_after, generated_by,
621
+ `SELECT id, entity_id, lifecycle_status, stale_after, generated_by,
602
622
  okf_sources, okf_verified, okf_usage_window,
603
623
  last_verified_at, last_verified_by
604
624
  FROM ${this.prefix}entries WHERE id = ?`,
605
625
  [fact.id]
606
626
  );
627
+ if (existingRow && existingRow.entity_id !== fact.entity_id) {
628
+ throw new WikiGraphNodeOwnershipConflict();
629
+ }
607
630
  const operation = fact.deleted_at ? "DELETE" : existingRow ? "UPDATE" : "INSERT";
608
631
  const result = await executor.runAsync(
609
632
  `INSERT INTO ${this.prefix}entries (
@@ -614,7 +637,6 @@ var EntryRepository = class extends BaseRepository {
614
637
  okf_sources, okf_verified, okf_usage_window
615
638
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
616
639
  ON CONFLICT(id) DO UPDATE SET
617
- entity_id = excluded.entity_id,
618
640
  title = excluded.title,
619
641
  body = excluded.body,
620
642
  tags = excluded.tags,
@@ -631,7 +653,8 @@ var EntryRepository = class extends BaseRepository {
631
653
  embedding_failed_at = CASE WHEN excluded.embedding_blob IS NOT NULL THEN NULL ELSE embedding_failed_at END,
632
654
  embedding_failure_kind = CASE WHEN excluded.embedding_blob IS NOT NULL THEN NULL ELSE embedding_failure_kind END,
633
655
  embedding_attempts = CASE WHEN excluded.embedding_blob IS NOT NULL THEN 0 ELSE embedding_attempts END,
634
- okf_type = excluded.okf_type`,
656
+ okf_type = excluded.okf_type
657
+ WHERE ${this.prefix}entries.entity_id = excluded.entity_id`,
635
658
  [
636
659
  fact.id,
637
660
  fact.entity_id,
@@ -663,6 +686,18 @@ var EntryRepository = class extends BaseRepository {
663
686
  fact.okf_usage_window ? JSON.stringify(fact.okf_usage_window) : null
664
687
  ]
665
688
  );
689
+ if (result.changes === 0) {
690
+ const stored = await tx.getFirstAsync(
691
+ `SELECT entity_id FROM ${this.prefix}entries WHERE id = ?`,
692
+ [fact.id]
693
+ );
694
+ if (!stored) {
695
+ throw new Error("Entry upsert reported no change and stored no row; the SQLiteAdapter did not perform the write");
696
+ }
697
+ if (stored.entity_id !== fact.entity_id) {
698
+ throw new WikiGraphNodeOwnershipConflict();
699
+ }
700
+ }
666
701
  const persistedOkf = existingRow ? {
667
702
  lifecycle_status: existingRow.lifecycle_status ?? "stable",
668
703
  stale_after: existingRow.stale_after != null ? Number(existingRow.stale_after) : null,
@@ -3058,6 +3093,11 @@ var WikiMemory = class {
3058
3093
  return this.maintenanceService.forget(entityId, params, opts);
3059
3094
  }
3060
3095
  /**
3096
+ * Full and partial ingestion both reject foreign-owned generated fact IDs with
3097
+ * `WikiGraphNodeOwnershipConflict`. Such collisions are unexpected. Ingestion
3098
+ * may already have persisted ontology or other state before rejection; unlike
3099
+ * direct graph preflight, it relies on transaction rollback for atomicity.
3100
+ *
3061
3101
  * @param params.promptOverride - Overrides the system prompt for this ingest call only.
3062
3102
  * For persistent customization, set `options.config.prompts.ingestSystemPrompt` at
3063
3103
  * WikiMemory construction time.
@@ -3089,6 +3129,19 @@ var WikiMemory = class {
3089
3129
  * or edge `type` throws `WikiStrictOntologyViolation` (pre-flight,
3090
3130
  * all-or-nothing — NONE written on failure).
3091
3131
  *
3132
+ * Entry IDs share one database-wide namespace. Existing rows, including
3133
+ * soft-deleted rows, reserve their IDs for their entity; same-entity updates
3134
+ * and resurrection remain supported. Hosts authenticate callers and select
3135
+ * the authorized entity ID; entity-qualified IDs are optional, not required.
3136
+ * After the live-only C2 probe above, a foreign node ID throws the fixed,
3137
+ * privacy-preserving `WikiGraphNodeOwnershipConflict` before persistent writes
3138
+ * (including seed manifests). Deleted source mappings do not trigger C2.
3139
+ *
3140
+ * Propagate later failures to roll back the host transaction. In particular,
3141
+ * foreign edge-ID collisions remain late bare `Error`s: the foreign edge is
3142
+ * not overwritten, but catching and committing can retain earlier writes.
3143
+ * This method neither commits nor rolls back the caller's transaction.
3144
+ *
3092
3145
  * @returns Counts: nodesWritten (validated nodes persisted), edgesWritten
3093
3146
  * (manifest-valid edges persisted), superseded (prior facts soft-deleted
3094
3147
  * plus prior source-ref edges hard-deleted).