@equationalapplications/core-llm-wiki 4.13.1 → 4.14.1

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.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-CDIDE4Jd.mjs';
2
- export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEvent, p as WikiFact, q as WikiMemoryTestAccess, r as WikiOutboxEvent, s as WikiTask } from './testing-CDIDE4Jd.mjs';
1
+ import { M as MemoryBundle, F as FormatContextOptions, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-DW1qufP0.mjs';
2
+ export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEvent, p as WikiFact, q as WikiMemoryTestAccess, r as WikiOutboxEvent, s as WikiTask } from './testing-DW1qufP0.mjs';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MemoryBundle, F as FormatContextOptions, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-CDIDE4Jd.js';
2
- export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEvent, p as WikiFact, q as WikiMemoryTestAccess, r as WikiOutboxEvent, s as WikiTask } from './testing-CDIDE4Jd.js';
1
+ import { M as MemoryBundle, F as FormatContextOptions, a as MemoryDump, b as FormattedMemoryDump, R as ReadOptions, S as SQLiteAdapter, W as WikiOptions, c as WikiMemory } from './testing-DW1qufP0.js';
2
+ export { E as EntityStatus, d as ExtractedFact, e as ExtractedTask, H as HOOK_TIMEOUT_MARKER, L as LLMProvider, P as PromptOverrides, f as PromptService, g as PrunePartialFailureError, V as VectorRanker, h as VectorRankerFallback, i as VectorRankerRankArgs, j as VectorRankerSemanticResult, k as WikiBusyError, l as WikiBusyOperation, m as WikiCheckpoint, n as WikiConfig, o as WikiEvent, p as WikiFact, q as WikiMemoryTestAccess, r as WikiOutboxEvent, s as WikiTask } from './testing-DW1qufP0.js';
3
3
  import { OkfFile } from '@equationalapplications/core-okf';
4
4
  import 'minisearch';
5
5
 
package/dist/index.js CHANGED
@@ -926,7 +926,9 @@ function generateId(prefix = "") {
926
926
  crypto.getRandomValues(bytes);
927
927
  return prefix + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").substring(0, 24);
928
928
  }
929
- return prefix + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
929
+ throw new Error(
930
+ "generateId: no cryptographically secure random source available (crypto.randomUUID and crypto.getRandomValues are both missing)."
931
+ );
930
932
  }
931
933
 
932
934
  // src/repositories/OutboxRepository.ts
@@ -1711,6 +1713,20 @@ var JobManager = class {
1711
1713
  this.activeMaintenanceJobs = /* @__PURE__ */ new Set();
1712
1714
  this.activeIngestJobs = /* @__PURE__ */ new Map();
1713
1715
  this.statusSubscribers = /* @__PURE__ */ new Map();
1716
+ /**
1717
+ * Lookup table for acquireLock/releaseLock's dynamic-dispatch branch.
1718
+ * Excludes 'ingest' | 'global_reembed' | 'global_import', which those
1719
+ * methods already handle via explicit if/else branches before reaching
1720
+ * this table.
1721
+ */
1722
+ this.lockKeyFns = {
1723
+ prune: (id) => this._pruneKey(id),
1724
+ librarian: (id) => this._librarianKey(id),
1725
+ heal: (id) => this._healKey(id),
1726
+ reembed: (id) => this._reembedKey(id),
1727
+ import: (id) => this._importKey(id),
1728
+ forget: (id) => this._forgetKey(id)
1729
+ };
1714
1730
  }
1715
1731
  _pruneKey(entityId) {
1716
1732
  return `${this.prefix}:${entityId}:prune`;
@@ -1860,9 +1876,7 @@ var JobManager = class {
1860
1876
  } else if (operation === "global_import") {
1861
1877
  this.activeMaintenanceJobs.add(this._globalImportKey());
1862
1878
  } else {
1863
- const keyFnName = `_${operation}Key`;
1864
- const keyFn = this[keyFnName];
1865
- this.activeMaintenanceJobs.add(keyFn.call(this, entityId));
1879
+ this.activeMaintenanceJobs.add(this.lockKeyFns[operation](entityId));
1866
1880
  }
1867
1881
  this._notifyStatusSubscribers(entityId);
1868
1882
  }
@@ -1874,9 +1888,7 @@ var JobManager = class {
1874
1888
  } else if (operation === "global_import") {
1875
1889
  this.activeMaintenanceJobs.delete(this._globalImportKey());
1876
1890
  } else {
1877
- const keyFnName = `_${operation}Key`;
1878
- const keyFn = this[keyFnName];
1879
- this.activeMaintenanceJobs.delete(keyFn.call(this, entityId));
1891
+ this.activeMaintenanceJobs.delete(this.lockKeyFns[operation](entityId));
1880
1892
  }
1881
1893
  this._notifyStatusSubscribers(entityId);
1882
1894
  }
@@ -2826,6 +2838,9 @@ var MaintenanceService = class {
2826
2838
  };
2827
2839
 
2828
2840
  // src/services/ImportExportService.ts
2841
+ var MAX_EMBEDDING_BLOB_BYTES = 32 * 1024;
2842
+ var IMPORT_TITLE_MAX = 500;
2843
+ var IMPORT_BODY_MAX = 8e3;
2829
2844
  var ImportExportService = class {
2830
2845
  constructor(db, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService) {
2831
2846
  this.db = db;
@@ -2907,6 +2922,7 @@ var ImportExportService = class {
2907
2922
  const factsWithPreservedBlob = /* @__PURE__ */ new Map();
2908
2923
  const preservedBlobDims = /* @__PURE__ */ new Set();
2909
2924
  const softDeletedFactIds = [];
2925
+ const clippedTextByFactId = /* @__PURE__ */ new Map();
2910
2926
  await this.db.withTransactionAsync(async (tx) => {
2911
2927
  if (!merge) {
2912
2928
  const deletedLiveFactIds = await this.entryRepo.findIdsBySource(
@@ -2943,21 +2959,32 @@ var ImportExportService = class {
2943
2959
  const rawBlobRaw = fact.embedding_blob;
2944
2960
  let rawBlob = null;
2945
2961
  if (rawBlobRaw instanceof Uint8Array) {
2946
- rawBlob = rawBlobRaw;
2962
+ if (rawBlobRaw.byteLength <= MAX_EMBEDDING_BLOB_BYTES) {
2963
+ rawBlob = rawBlobRaw;
2964
+ }
2947
2965
  } else if (rawBlobRaw !== null && rawBlobRaw !== void 0 && typeof rawBlobRaw === "object") {
2948
2966
  const obj = rawBlobRaw;
2949
2967
  if (obj["type"] === "Buffer" && Array.isArray(obj["data"])) {
2950
- rawBlob = new Uint8Array(obj["data"]);
2968
+ const data = obj["data"];
2969
+ if (data.length <= MAX_EMBEDDING_BLOB_BYTES) {
2970
+ rawBlob = new Uint8Array(data);
2971
+ }
2951
2972
  } else if (!Array.isArray(rawBlobRaw)) {
2952
2973
  const entries = Object.keys(obj);
2953
2974
  if (entries.length > 0 && entries.every((k) => /^\d+$/.test(k))) {
2954
2975
  const len = entries.length;
2955
- rawBlob = new Uint8Array(len);
2956
- for (let i = 0; i < len; i++)
2957
- rawBlob[i] = obj[String(i)] ?? 0;
2976
+ if (len <= MAX_EMBEDDING_BLOB_BYTES) {
2977
+ rawBlob = new Uint8Array(len);
2978
+ for (let i = 0; i < len; i++) {
2979
+ rawBlob[i] = obj[String(i)] ?? 0;
2980
+ }
2981
+ }
2958
2982
  }
2959
2983
  }
2960
2984
  }
2985
+ if (rawBlob !== null && rawBlob.byteLength > MAX_EMBEDDING_BLOB_BYTES) {
2986
+ rawBlob = null;
2987
+ }
2961
2988
  let blobData = null;
2962
2989
  if (rawBlob !== null && rawBlob.byteLength > 0 && rawBlob.byteLength % 4 === 0) {
2963
2990
  const copy = new ArrayBuffer(rawBlob.byteLength);
@@ -2987,11 +3014,14 @@ var ImportExportService = class {
2987
3014
  }
2988
3015
  if (merge && safeUpdatedAt <= existing.updated_at) continue;
2989
3016
  }
3017
+ const safeTitle = clip(String(fact.title ?? ""), IMPORT_TITLE_MAX);
3018
+ const safeBody = clip(String(fact.body ?? ""), IMPORT_BODY_MAX);
3019
+ clippedTextByFactId.set(fact.id, { title: safeTitle, body: safeBody });
2990
3020
  const factObj = {
2991
3021
  id: fact.id,
2992
3022
  entity_id: entityId,
2993
- title: fact.title,
2994
- body: fact.body,
3023
+ title: safeTitle,
3024
+ body: safeBody,
2995
3025
  tags: Array.isArray(fact.tags) ? fact.tags : [],
2996
3026
  confidence: fact.confidence,
2997
3027
  source_type: sourceType,
@@ -3079,11 +3109,12 @@ var ImportExportService = class {
3079
3109
  await this.searchService.sync(entityId);
3080
3110
  for (const fact of bundle.facts) {
3081
3111
  if (!fact.deleted_at && upsertedFactIds.has(fact.id) && !factsWithPreservedBlob.has(fact.id)) {
3112
+ const clipped = clippedTextByFactId.get(fact.id);
3082
3113
  const embedded = await this.embeddingService.embedFact({
3083
3114
  id: fact.id,
3084
3115
  entity_id: entityId,
3085
- title: fact.title,
3086
- body: fact.body,
3116
+ title: clipped?.title ?? fact.title,
3117
+ body: clipped?.body ?? fact.body,
3087
3118
  tags: Array.isArray(fact.tags) || typeof fact.tags === "string" ? fact.tags : []
3088
3119
  });
3089
3120
  if (!embedded) {
@@ -3183,7 +3214,7 @@ var ImportExportService = class {
3183
3214
  }
3184
3215
  _warnCrossEntityCollision(type, id, existingEntityId, targetEntityId) {
3185
3216
  console.warn(
3186
- `[WikiMemory] importDump: ${type} id "${id}" already belongs to entity "${existingEntityId}"; skipping for entity "${targetEntityId}"`
3217
+ `[WikiMemory] importDump: ${type} id ${JSON.stringify(id)} already belongs to entity ${JSON.stringify(existingEntityId)}; skipping for entity ${JSON.stringify(targetEntityId)}`
3187
3218
  );
3188
3219
  }
3189
3220
  _normalizeImportedSourceType(raw, ctx) {
@@ -3262,7 +3293,7 @@ var EmbeddingService = class {
3262
3293
  tagsStr = fact.tags;
3263
3294
  }
3264
3295
  }
3265
- const text = `${fact.title} ${fact.body} ${tagsStr}`.trim();
3296
+ const text = clip(`${fact.title} ${fact.body} ${tagsStr}`.trim(), 16e3);
3266
3297
  try {
3267
3298
  const vector = await embedFn(text);
3268
3299
  if (vector.length === 0 || !vector.every((v) => typeof v === "number" && isFinite(v))) {
@@ -3876,15 +3907,38 @@ var RetrievalService = class {
3876
3907
 
3877
3908
  // src/services/WriteService.ts
3878
3909
  var WriteService = class {
3879
- constructor(db, options, eventRepo, metadataRepo, jobManager, maintenanceService) {
3910
+ constructor(db, options, entryRepo, eventRepo, metadataRepo, jobManager, maintenanceService) {
3880
3911
  this.db = db;
3881
3912
  this.options = options;
3913
+ this.entryRepo = entryRepo;
3882
3914
  this.eventRepo = eventRepo;
3883
3915
  this.metadataRepo = metadataRepo;
3884
3916
  this.jobManager = jobManager;
3885
3917
  this.maintenanceService = maintenanceService;
3886
3918
  }
3887
3919
  async write(entityId, event) {
3920
+ if (typeof entityId !== "string" || entityId.length === 0 || entityId.length > 200 || entityId.includes("\0")) {
3921
+ throw new TypeError(
3922
+ `Invalid entityId: must be a non-empty string at most 200 chars with no null bytes; got ${JSON.stringify(entityId)}.`
3923
+ );
3924
+ }
3925
+ if (event === null || typeof event !== "object" || Array.isArray(event)) {
3926
+ throw new TypeError("Invalid event: must be a non-null object.");
3927
+ }
3928
+ if (typeof event.summary !== "string") {
3929
+ throw new TypeError("Invalid event.summary: must be a string.");
3930
+ }
3931
+ const summary = clip(event.summary, 4e3);
3932
+ let relatedEntryId = null;
3933
+ const rawRelatedEntryId = event.related_entry_id;
3934
+ if (rawRelatedEntryId != null && rawRelatedEntryId !== "") {
3935
+ if (typeof rawRelatedEntryId !== "string" || rawRelatedEntryId.length > 200 || rawRelatedEntryId.includes("\0")) {
3936
+ relatedEntryId = null;
3937
+ } else {
3938
+ const existing = await this.entryRepo.findByIds([rawRelatedEntryId], [entityId]);
3939
+ relatedEntryId = existing.length > 0 ? rawRelatedEntryId : null;
3940
+ }
3941
+ }
3888
3942
  const id = generateId("evt_");
3889
3943
  const now = Date.now();
3890
3944
  let eventType = event.event_type;
@@ -3895,8 +3949,8 @@ var WriteService = class {
3895
3949
  id,
3896
3950
  entity_id: entityId,
3897
3951
  event_type: eventType,
3898
- summary: event.summary,
3899
- related_entry_id: event.related_entry_id || null,
3952
+ summary,
3953
+ related_entry_id: relatedEntryId,
3900
3954
  created_at: now
3901
3955
  };
3902
3956
  let shouldRunLibrarian = false;
@@ -3957,6 +4011,7 @@ var WriteService = class {
3957
4011
  };
3958
4012
 
3959
4013
  // src/WikiMemory.ts
4014
+ var TABLE_PREFIX_PATTERN = /^[A-Za-z][A-Za-z0-9_]{0,30}_$/;
3960
4015
  var _testAccessNonTestEnvWarned;
3961
4016
  var WikiMemory = class {
3962
4017
  constructor(db, options) {
@@ -3964,7 +4019,12 @@ var WikiMemory = class {
3964
4019
  __privateAdd(this, _testAccessNonTestEnvWarned, false);
3965
4020
  this.db = db;
3966
4021
  this.options = options;
3967
- this.prefix = options.config?.tablePrefix || "llm_wiki_";
4022
+ this.prefix = options.config?.tablePrefix ?? "llm_wiki_";
4023
+ if (!TABLE_PREFIX_PATTERN.test(this.prefix)) {
4024
+ throw new Error(
4025
+ `Invalid tablePrefix: ${JSON.stringify(this.prefix)}. Must match ${TABLE_PREFIX_PATTERN} (letter, then alphanumeric/underscore, ending in "_", max 32 chars total).`
4026
+ );
4027
+ }
3968
4028
  this.outboxRepo = new OutboxRepository(db, this.prefix, !!options.config?.enableOutbox);
3969
4029
  this.entryRepo = new EntryRepository(db, this.prefix, this.outboxRepo);
3970
4030
  this.taskRepo = new TaskRepository(db, this.prefix, this.outboxRepo);
@@ -4018,6 +4078,7 @@ var WikiMemory = class {
4018
4078
  this.writeService = new WriteService(
4019
4079
  this.db,
4020
4080
  this.options,
4081
+ this.entryRepo,
4021
4082
  this.eventRepo,
4022
4083
  this.metadataRepo,
4023
4084
  this.jobManager,
@@ -4096,7 +4157,7 @@ var WikiMemory = class {
4096
4157
  async hasChanged(entityId, sourceRef, sourceHash) {
4097
4158
  const normalizedRef = normalizeSourceRef(sourceRef);
4098
4159
  if (!normalizedRef) {
4099
- throw new Error(`Invalid sourceRef: "${sourceRef}"`);
4160
+ throw new Error(`Invalid sourceRef: ${JSON.stringify(sourceRef)}`);
4100
4161
  }
4101
4162
  const normalizedHash = normalizeSourceHash(sourceHash);
4102
4163
  if (!normalizedHash) {