@equationalapplications/core-llm-wiki 7.1.2 → 7.1.3
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 +36 -3
- package/dist/{chunk-ZU7K6NAU.mjs → chunk-TTECJ3S3.mjs} +59 -3
- package/dist/chunk-TTECJ3S3.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +113 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +58 -5
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-CA7723qk.d.mts → testing-DYeMd9d1.d.mts} +110 -1
- package/dist/{testing-CA7723qk.d.ts → testing-DYeMd9d1.d.ts} +110 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +56 -0
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-ZU7K6NAU.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1016,6 +1016,33 @@ const { nodesWritten, edgesWritten, superseded } = await wikiMemory.upsertGraph(
|
|
|
1016
1016
|
|
|
1017
1017
|
`upsertGraph` is "the tail of `ingestDocument` with the middle (LLM extraction) step removed" — it accepts caller-supplied nodes (`{ id, type, title, body? }`) and edges (`{ type, sourceId, targetId, id? }`) and writes them under the same `(sourceRef, sourceHash)` semantics. If a *different* live `sourceRef` already holds the same `sourceHash`, it throws `WikiSourceRefHashCollision`; re-writing the identical `(sourceRef, sourceHash)` is a no-op returning zero counts. The adapter parameter is required so writes participate in the caller's transaction.
|
|
1018
1018
|
|
|
1019
|
+
Entry IDs occupy one globally shared namespace within a database. An ID belongs
|
|
1020
|
+
to its existing entity while its row exists, including after soft deletion.
|
|
1021
|
+
Same-entity updates and resurrection retain their existing behavior; physically
|
|
1022
|
+
absent IDs may be inserted. Hosts remain responsible for authenticating callers
|
|
1023
|
+
and selecting the authorized entity ID. Entity-qualified deterministic IDs can
|
|
1024
|
+
avoid accidental collisions, but are not required by the API.
|
|
1025
|
+
|
|
1026
|
+
A foreign node ID rejects the entire non-no-op graph write with
|
|
1027
|
+
`WikiGraphNodeOwnershipConflict` (code `WIKI_GRAPH_NODE_OWNERSHIP_CONFLICT`).
|
|
1028
|
+
The error does not identify the owner, conflicting node, or stored details.
|
|
1029
|
+
A collision visible to node preflight performs no persistent writes, including
|
|
1030
|
+
seed-manifest writes. The live source-hash probe still runs first: an identical
|
|
1031
|
+
mapping is a zero-count no-op; a different live reference retains its hash
|
|
1032
|
+
collision error. Deleted source-reference mappings do not trigger that probe.
|
|
1033
|
+
|
|
1034
|
+
Call `upsertGraph` inside the host transaction and pass that callback's `tx`.
|
|
1035
|
+
It neither starts nor ends a transaction. Propagate later failures so the host
|
|
1036
|
+
transaction rolls back partial writes; catching a late error and committing is
|
|
1037
|
+
not atomic. Foreign edge-ID collisions retain their existing late bare `Error`
|
|
1038
|
+
and require host rollback, even though the foreign edge is never overwritten.
|
|
1039
|
+
|
|
1040
|
+
`ingestDocument` shares ownership enforcement on both its full and partial paths
|
|
1041
|
+
and may also throw `WikiGraphNodeOwnershipConflict`. It normally generates new
|
|
1042
|
+
IDs, so collisions are unexpected. Unlike direct graph preflight, ingestion may
|
|
1043
|
+
already have persisted ontology or other state when rejection occurs; its
|
|
1044
|
+
transaction rollback, not a zero-write preflight guarantee, protects atomicity.
|
|
1045
|
+
|
|
1019
1046
|
## Duplicate Hash Detection
|
|
1020
1047
|
|
|
1021
1048
|
Control behavior when a different live `sourceRef` already holds the same `sourceHash`. The option is passed as a third argument to `ingestDocument`, not inside the params object:
|
|
@@ -1115,11 +1142,17 @@ export interface SQLiteAdapter {
|
|
|
1115
1142
|
runAsync(sql: string, params?: unknown[]): Promise<{ changes: number; lastInsertRowId: number }>;
|
|
1116
1143
|
getAllAsync<T>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
1117
1144
|
getFirstAsync<T>(sql: string, params?: unknown[]): Promise<T | null>;
|
|
1118
|
-
withTransactionAsync<T>(fn: () => Promise<T>): Promise<T>;
|
|
1145
|
+
withTransactionAsync<T>(fn: (tx: SQLiteAdapter) => Promise<T>): Promise<T>;
|
|
1119
1146
|
closeAsync(): Promise<void>;
|
|
1120
1147
|
}
|
|
1121
1148
|
```
|
|
1122
1149
|
|
|
1150
|
+
`runAsync().changes` must accurately count written rows: a conflict-suppressed
|
|
1151
|
+
write reports 0, while a permitted value-identical update still counts its row.
|
|
1152
|
+
Host adapters are responsible for this contract; see the full affected-row
|
|
1153
|
+
requirements on [`SQLiteAdapter.runAsync`](src/types.ts). Ownership checks,
|
|
1154
|
+
edge counts, metadata compare-and-set, and maintenance depend on accurate counts.
|
|
1155
|
+
|
|
1123
1156
|
`@equationalapplications/expo-llm-wiki` provides a pre-built adapter for Expo/React Native. For web and Node.js, implement the interface yourself — examples below.
|
|
1124
1157
|
|
|
1125
1158
|
**Browser ([sql.js](https://github.com/sql-js/sql.js)):**
|
|
@@ -1155,7 +1188,7 @@ const adapter: SQLiteAdapter = {
|
|
|
1155
1188
|
},
|
|
1156
1189
|
async withTransactionAsync(fn) {
|
|
1157
1190
|
sqlDb.run('BEGIN');
|
|
1158
|
-
try { const r = await fn(); sqlDb.run('COMMIT'); return r; }
|
|
1191
|
+
try { const r = await fn(this); sqlDb.run('COMMIT'); return r; }
|
|
1159
1192
|
catch (e) { sqlDb.run('ROLLBACK'); throw e; }
|
|
1160
1193
|
},
|
|
1161
1194
|
async closeAsync() { sqlDb.close(); },
|
|
@@ -1184,7 +1217,7 @@ const adapter: SQLiteAdapter = {
|
|
|
1184
1217
|
},
|
|
1185
1218
|
async withTransactionAsync(fn) {
|
|
1186
1219
|
db.exec('BEGIN');
|
|
1187
|
-
try { const r = await fn(); db.exec('COMMIT'); return r; }
|
|
1220
|
+
try { const r = await fn(this); db.exec('COMMIT'); return r; }
|
|
1188
1221
|
catch (e) { db.exec('ROLLBACK'); throw e; }
|
|
1189
1222
|
},
|
|
1190
1223
|
async closeAsync() { db.close(); },
|
|
@@ -105,6 +105,21 @@ var WikiSourceRefHashCollision = class extends Error {
|
|
|
105
105
|
this.name = "WikiSourceRefHashCollision";
|
|
106
106
|
}
|
|
107
107
|
};
|
|
108
|
+
var WikiGraphNodeOwnershipConflict = class extends Error {
|
|
109
|
+
/**
|
|
110
|
+
* Privacy-preserving constructor: accepts no arguments, so neither the foreign
|
|
111
|
+
* owner, the conflicting node ID, the stored content, nor any provenance can
|
|
112
|
+
* leak through this error. The fixed message and `code` are the entire signal.
|
|
113
|
+
*
|
|
114
|
+
* @returns a contextless `WikiGraphNodeOwnershipConflict` instance. See the
|
|
115
|
+
* class-level JSDoc for the privacy rationale.
|
|
116
|
+
*/
|
|
117
|
+
constructor() {
|
|
118
|
+
super("Graph write rejected because a node ID is unavailable for this entity.");
|
|
119
|
+
this.code = "WIKI_GRAPH_NODE_OWNERSHIP_CONFLICT";
|
|
120
|
+
this.name = "WikiGraphNodeOwnershipConflict";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
108
123
|
var WikiTransactionError = class extends Error {
|
|
109
124
|
constructor(message, options) {
|
|
110
125
|
super(message, options);
|
|
@@ -1942,6 +1957,36 @@ var IngestionService = class {
|
|
|
1942
1957
|
releaseIngestLocks();
|
|
1943
1958
|
}
|
|
1944
1959
|
}
|
|
1960
|
+
/**
|
|
1961
|
+
* Reject the whole write when any incoming node ID is already owned by a
|
|
1962
|
+
* different entity. Runs entirely inside the supplied `tx`, through the
|
|
1963
|
+
* repository abstraction, and — being the first thing `upsertGraphCore`
|
|
1964
|
+
* does — before ontology resolution, which can itself persist a configured
|
|
1965
|
+
* seed manifest.
|
|
1966
|
+
*
|
|
1967
|
+
* The lookup has no deleted-row filter, so a soft-deleted foreign row keeps
|
|
1968
|
+
* its ID reserved. Only the lookup IDs are deduplicated; `params.nodes` and
|
|
1969
|
+
* the later write loops keep their ordering and counts.
|
|
1970
|
+
*
|
|
1971
|
+
* The error is deliberately contextless — see WikiGraphNodeOwnershipConflict.
|
|
1972
|
+
*
|
|
1973
|
+
* @param entityId - the entity the caller is writing on behalf of. Foreign
|
|
1974
|
+
* rows under a different entity trigger the rejection.
|
|
1975
|
+
* @param nodes - incoming node set; only the IDs are inspected, so callers
|
|
1976
|
+
* may pass the public `params.nodes` shape directly without slicing.
|
|
1977
|
+
* @param tx - the open transaction. Reuses the same handle every later
|
|
1978
|
+
* statement in `upsertGraphCore` runs against, so atomicity is enforced
|
|
1979
|
+
* by the host transaction's commit/rollback, not by this method.
|
|
1980
|
+
*/
|
|
1981
|
+
async assertGraphNodeOwnership(entityId, nodes, tx) {
|
|
1982
|
+
const ids = [...new Set(nodes.map((node) => node.id))];
|
|
1983
|
+
const existing = await this.entryRepo.findExistingMetadataByIds(ids, tx);
|
|
1984
|
+
for (const row of existing) {
|
|
1985
|
+
if (row.entity_id !== entityId) {
|
|
1986
|
+
throw new WikiGraphNodeOwnershipConflict();
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1945
1990
|
/**
|
|
1946
1991
|
* Implements spec §2 data flow steps a–j with the host-facing
|
|
1947
1992
|
* `{sourceRef, sourceHash, nodes, edges}` parameter shape. Shared between
|
|
@@ -1960,8 +2005,19 @@ var IngestionService = class {
|
|
|
1960
2005
|
* @returns Counts: nodesWritten (validated nodes persisted), edgesWritten
|
|
1961
2006
|
* (manifest-valid edges persisted), superseded (prior facts soft-deleted
|
|
1962
2007
|
* plus prior source-ref edges hard-deleted).
|
|
2008
|
+
*
|
|
2009
|
+
* Node-ownership pre-flight (spec §3, REQ-GRAPH-01): the first statement
|
|
2010
|
+
* is `assertGraphNodeOwnership(entityId, params.nodes, tx)`, which runs
|
|
2011
|
+
* before ontology resolution, seed-manifest persistence, and every other
|
|
2012
|
+
* write. A foreign-owned ID throws `WikiGraphNodeOwnershipConflict` with
|
|
2013
|
+
* no disclosed owner, node ID, or content; the caller's transaction
|
|
2014
|
+
* remains open and must be rolled back by the host. Soft-deleted foreign
|
|
2015
|
+
* rows keep their IDs reserved. Duplicate incoming IDs are deduplicated
|
|
2016
|
+
* only for the lookup; `params.nodes` and the write loops keep their
|
|
2017
|
+
* ordering and counts so success counts match the pre-change contract.
|
|
1963
2018
|
*/
|
|
1964
2019
|
async upsertGraphCore(entityId, params, tx, opts) {
|
|
2020
|
+
await this.assertGraphNodeOwnership(entityId, params.nodes, tx);
|
|
1965
2021
|
const now = Date.now();
|
|
1966
2022
|
const ontologyState = await this.ontologyService?.getEffectiveState(entityId, tx) ?? { mode: "off", manifest: { node_types: [], edge_types: [] } };
|
|
1967
2023
|
let { mode, manifest } = ontologyState;
|
|
@@ -4913,6 +4969,6 @@ var WriteService = class {
|
|
|
4913
4969
|
}
|
|
4914
4970
|
};
|
|
4915
4971
|
|
|
4916
|
-
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, 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, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, typeSatisfies, validateInlineEdges, validateManifest };
|
|
4917
|
-
//# sourceMappingURL=chunk-
|
|
4918
|
-
//# sourceMappingURL=chunk-
|
|
4972
|
+
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, 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, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, typeSatisfies, validateInlineEdges, validateManifest };
|
|
4973
|
+
//# sourceMappingURL=chunk-TTECJ3S3.mjs.map
|
|
4974
|
+
//# sourceMappingURL=chunk-TTECJ3S3.mjs.map
|