@fortemi/core 2026.7.14 → 2026.8.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 +24 -1
- package/dist/aiwg-index-schema.d.ts +0 -1
- package/dist/aiwg-index.d.ts +29 -30
- package/dist/index.d.ts +756 -579
- package/dist/index.js +1083 -43
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -926,6 +926,85 @@ var migration0022 = {
|
|
|
926
926
|
`
|
|
927
927
|
};
|
|
928
928
|
|
|
929
|
+
// src/migrations/0023_source_metadata_purge.ts
|
|
930
|
+
var migration0023 = {
|
|
931
|
+
version: 23,
|
|
932
|
+
name: "0023_source_metadata_purge",
|
|
933
|
+
sql: `
|
|
934
|
+
CREATE TABLE IF NOT EXISTS source_identity (
|
|
935
|
+
id TEXT PRIMARY KEY,
|
|
936
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
937
|
+
archive_id TEXT,
|
|
938
|
+
namespace TEXT NOT NULL,
|
|
939
|
+
external_id TEXT NOT NULL,
|
|
940
|
+
external_id_hash TEXT NOT NULL,
|
|
941
|
+
source_schema_version TEXT NOT NULL,
|
|
942
|
+
content_digest TEXT NOT NULL,
|
|
943
|
+
import_run_id TEXT NOT NULL,
|
|
944
|
+
caller_stable_id TEXT,
|
|
945
|
+
note_id TEXT NOT NULL REFERENCES note(id) ON DELETE CASCADE,
|
|
946
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
947
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
948
|
+
UNIQUE (tenant_id, archive_id, namespace, external_id)
|
|
949
|
+
);
|
|
950
|
+
|
|
951
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_source_identity_scope_key
|
|
952
|
+
ON source_identity(tenant_id, COALESCE(archive_id, ''), namespace, external_id);
|
|
953
|
+
CREATE INDEX IF NOT EXISTS idx_source_identity_note ON source_identity(note_id);
|
|
954
|
+
CREATE INDEX IF NOT EXISTS idx_source_identity_import_run ON source_identity(import_run_id);
|
|
955
|
+
CREATE INDEX IF NOT EXISTS idx_source_identity_hash ON source_identity(external_id_hash);
|
|
956
|
+
|
|
957
|
+
CREATE TABLE IF NOT EXISTS source_import_run (
|
|
958
|
+
id TEXT PRIMARY KEY,
|
|
959
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
960
|
+
archive_id TEXT,
|
|
961
|
+
namespace TEXT NOT NULL,
|
|
962
|
+
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
963
|
+
completed_at TIMESTAMPTZ,
|
|
964
|
+
checkpoint JSONB NOT NULL DEFAULT '{}',
|
|
965
|
+
receipt JSONB NOT NULL DEFAULT '{}'
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
CREATE TABLE IF NOT EXISTS metadata_index_path (
|
|
969
|
+
path TEXT PRIMARY KEY,
|
|
970
|
+
value_type TEXT NOT NULL,
|
|
971
|
+
indexed_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
972
|
+
);
|
|
973
|
+
|
|
974
|
+
INSERT INTO metadata_index_path (path, value_type) VALUES
|
|
975
|
+
('provider', 'string'),
|
|
976
|
+
('model', 'string'),
|
|
977
|
+
('role', 'string'),
|
|
978
|
+
('event_kind', 'string'),
|
|
979
|
+
('sensitivity', 'string'),
|
|
980
|
+
('import_run_id', 'string')
|
|
981
|
+
ON CONFLICT (path) DO NOTHING;
|
|
982
|
+
|
|
983
|
+
CREATE INDEX IF NOT EXISTS idx_note_metadata_provider
|
|
984
|
+
ON note_revised_current ((ai_metadata ->> 'provider'));
|
|
985
|
+
CREATE INDEX IF NOT EXISTS idx_note_metadata_model
|
|
986
|
+
ON note_revised_current ((ai_metadata ->> 'model'));
|
|
987
|
+
CREATE INDEX IF NOT EXISTS idx_note_metadata_role
|
|
988
|
+
ON note_revised_current ((ai_metadata ->> 'role'));
|
|
989
|
+
CREATE INDEX IF NOT EXISTS idx_note_metadata_event_kind
|
|
990
|
+
ON note_revised_current ((ai_metadata ->> 'event_kind'));
|
|
991
|
+
CREATE INDEX IF NOT EXISTS idx_note_metadata_sensitivity
|
|
992
|
+
ON note_revised_current ((ai_metadata ->> 'sensitivity'));
|
|
993
|
+
|
|
994
|
+
CREATE TABLE IF NOT EXISTS deletion_receipt (
|
|
995
|
+
id TEXT PRIMARY KEY,
|
|
996
|
+
operation_key TEXT NOT NULL UNIQUE,
|
|
997
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
998
|
+
archive_id TEXT,
|
|
999
|
+
selector_hash TEXT NOT NULL,
|
|
1000
|
+
outcome TEXT NOT NULL,
|
|
1001
|
+
counts JSONB NOT NULL,
|
|
1002
|
+
completed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
1003
|
+
policy JSONB NOT NULL DEFAULT '{}'
|
|
1004
|
+
);
|
|
1005
|
+
`
|
|
1006
|
+
};
|
|
1007
|
+
|
|
929
1008
|
// src/migrations/index.ts
|
|
930
1009
|
var allMigrations = [
|
|
931
1010
|
migration0001,
|
|
@@ -949,7 +1028,8 @@ var allMigrations = [
|
|
|
949
1028
|
migration0019,
|
|
950
1029
|
migration0020,
|
|
951
1030
|
migration0021,
|
|
952
|
-
migration0022
|
|
1031
|
+
migration0022,
|
|
1032
|
+
migration0023
|
|
953
1033
|
];
|
|
954
1034
|
|
|
955
1035
|
// src/data-archive.ts
|
|
@@ -1732,7 +1812,7 @@ var EmbeddingSetsRepository = class {
|
|
|
1732
1812
|
const set = await this.get(setId);
|
|
1733
1813
|
if (set.kind !== "virtual") throw new Error(`Embedding set is not virtual: ${setId}`);
|
|
1734
1814
|
const definition = this.definitionFromRow(set);
|
|
1735
|
-
const
|
|
1815
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
1736
1816
|
const materialization = definition.materialization ? { ...definition.materialization, freshness: "stale" } : null;
|
|
1737
1817
|
await this.db.query(
|
|
1738
1818
|
`UPDATE embedding_set
|
|
@@ -1741,7 +1821,7 @@ var EmbeddingSetsRepository = class {
|
|
|
1741
1821
|
[
|
|
1742
1822
|
setId,
|
|
1743
1823
|
jsonParam(materialization),
|
|
1744
|
-
jsonParam({ status: "stale", sourceHash: definition.materialization?.inputHash, checkedAt:
|
|
1824
|
+
jsonParam({ status: "stale", sourceHash: definition.materialization?.inputHash, checkedAt: now2, reason })
|
|
1745
1825
|
]
|
|
1746
1826
|
);
|
|
1747
1827
|
}
|
|
@@ -2004,6 +2084,90 @@ var EmbeddingSetsRepository = class {
|
|
|
2004
2084
|
}
|
|
2005
2085
|
};
|
|
2006
2086
|
|
|
2087
|
+
// src/repositories/metadata-predicates.ts
|
|
2088
|
+
var REGISTERED_METADATA_PATHS = [
|
|
2089
|
+
"provider",
|
|
2090
|
+
"model",
|
|
2091
|
+
"role",
|
|
2092
|
+
"event_kind",
|
|
2093
|
+
"sensitivity",
|
|
2094
|
+
"import_run_id"
|
|
2095
|
+
];
|
|
2096
|
+
var REGISTERED_SET = new Set(REGISTERED_METADATA_PATHS);
|
|
2097
|
+
var MAX_PREDICATES = 8;
|
|
2098
|
+
var MAX_IN_VALUES = 32;
|
|
2099
|
+
var MAX_VALUE_LENGTH = 256;
|
|
2100
|
+
function assertRegisteredPath(path) {
|
|
2101
|
+
if (!REGISTERED_SET.has(path) || path.includes(".") || path.includes("/")) {
|
|
2102
|
+
throw new Error(`Unsupported metadata predicate path: ${path}`);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
function assertBoundedValue(value) {
|
|
2106
|
+
if (typeof value === "string" && value.length > MAX_VALUE_LENGTH) {
|
|
2107
|
+
throw new Error("Metadata predicate value exceeds the 256 character bound");
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
function jsonAccessor(path) {
|
|
2111
|
+
if (path === "import_run_id") return "si.import_run_id";
|
|
2112
|
+
return `c.ai_metadata ->> '${path}'`;
|
|
2113
|
+
}
|
|
2114
|
+
function buildMetadataPredicateConditions(options, startIdx) {
|
|
2115
|
+
const predicates = options.metadataPredicates ?? [];
|
|
2116
|
+
if (predicates.length > MAX_PREDICATES) {
|
|
2117
|
+
throw new Error(`Metadata predicate count exceeds the ${MAX_PREDICATES} predicate bound`);
|
|
2118
|
+
}
|
|
2119
|
+
const conditions = [];
|
|
2120
|
+
const params = [];
|
|
2121
|
+
const joins = [];
|
|
2122
|
+
let idx = startIdx;
|
|
2123
|
+
let needsSourceJoin = options.tenant_id !== void 0 || options.archive_id !== void 0;
|
|
2124
|
+
if (options.tenant_id !== void 0) {
|
|
2125
|
+
conditions.push(`COALESCE(si.tenant_id, 'default') = $${idx++}`);
|
|
2126
|
+
params.push(options.tenant_id);
|
|
2127
|
+
}
|
|
2128
|
+
if (options.archive_id !== void 0) {
|
|
2129
|
+
conditions.push(`si.archive_id IS NOT DISTINCT FROM $${idx++}`);
|
|
2130
|
+
params.push(options.archive_id);
|
|
2131
|
+
}
|
|
2132
|
+
for (const predicate of predicates) {
|
|
2133
|
+
assertRegisteredPath(predicate.path);
|
|
2134
|
+
const lhs = jsonAccessor(predicate.path);
|
|
2135
|
+
if (predicate.path === "import_run_id") needsSourceJoin = true;
|
|
2136
|
+
if (predicate.op === "eq") {
|
|
2137
|
+
assertBoundedValue(predicate.value);
|
|
2138
|
+
conditions.push(`${lhs} IS NOT DISTINCT FROM $${idx++}`);
|
|
2139
|
+
params.push(predicate.value == null ? null : String(predicate.value));
|
|
2140
|
+
} else if (predicate.op === "in") {
|
|
2141
|
+
if (predicate.value.length > MAX_IN_VALUES) {
|
|
2142
|
+
throw new Error(`Metadata predicate membership exceeds the ${MAX_IN_VALUES} value bound`);
|
|
2143
|
+
}
|
|
2144
|
+
for (const value of predicate.value) assertBoundedValue(value);
|
|
2145
|
+
conditions.push(`${lhs} = ANY($${idx++})`);
|
|
2146
|
+
params.push(predicate.value.map((value) => value == null ? null : String(value)));
|
|
2147
|
+
} else if (predicate.op === "range") {
|
|
2148
|
+
if (predicate.gte === void 0 && predicate.lte === void 0) {
|
|
2149
|
+
throw new Error("Metadata range predicate requires gte or lte");
|
|
2150
|
+
}
|
|
2151
|
+
if (predicate.gte !== void 0) {
|
|
2152
|
+
assertBoundedValue(predicate.gte);
|
|
2153
|
+
conditions.push(`${lhs} >= $${idx++}`);
|
|
2154
|
+
params.push(String(predicate.gte));
|
|
2155
|
+
}
|
|
2156
|
+
if (predicate.lte !== void 0) {
|
|
2157
|
+
assertBoundedValue(predicate.lte);
|
|
2158
|
+
conditions.push(`${lhs} <= $${idx++}`);
|
|
2159
|
+
params.push(String(predicate.lte));
|
|
2160
|
+
}
|
|
2161
|
+
} else {
|
|
2162
|
+
conditions.push(predicate.value === false ? `${lhs} IS NULL` : `${lhs} IS NOT NULL`);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
if (needsSourceJoin) {
|
|
2166
|
+
joins.push("LEFT JOIN source_identity si ON si.note_id = n.id");
|
|
2167
|
+
}
|
|
2168
|
+
return { conditions, joins, params, nextIdx: idx };
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2007
2171
|
// src/repositories/search-repository.ts
|
|
2008
2172
|
var ATTACHMENT_TEXT_JOIN2 = `
|
|
2009
2173
|
LEFT JOIN (
|
|
@@ -2072,6 +2236,41 @@ var SearchRepository = class {
|
|
|
2072
2236
|
attachEmbeddingStatus(results, embeddingSet) {
|
|
2073
2237
|
return results.map((r) => ({ ...r, has_embedding: embeddingSet.has(r.id) }));
|
|
2074
2238
|
}
|
|
2239
|
+
async fetchLocatorMap(noteIds, metadataPaths = []) {
|
|
2240
|
+
const locators = /* @__PURE__ */ new Map();
|
|
2241
|
+
if (noteIds.length === 0) return locators;
|
|
2242
|
+
const result = await this.db.query(
|
|
2243
|
+
`SELECT note_id, namespace, external_id_hash, import_run_id, source_schema_version
|
|
2244
|
+
FROM source_identity
|
|
2245
|
+
WHERE note_id = ANY($1)
|
|
2246
|
+
ORDER BY created_at ASC`,
|
|
2247
|
+
[noteIds]
|
|
2248
|
+
);
|
|
2249
|
+
for (const row of result.rows) {
|
|
2250
|
+
const existing = locators.get(row.note_id) ?? [];
|
|
2251
|
+
existing.push({
|
|
2252
|
+
note_id: row.note_id,
|
|
2253
|
+
chunk: { kind: "current", index: 0 },
|
|
2254
|
+
source: {
|
|
2255
|
+
namespace: row.namespace,
|
|
2256
|
+
external_id_hash: row.external_id_hash,
|
|
2257
|
+
import_run_id: row.import_run_id,
|
|
2258
|
+
schema_version: row.source_schema_version
|
|
2259
|
+
},
|
|
2260
|
+
metadata_paths: [...metadataPaths]
|
|
2261
|
+
});
|
|
2262
|
+
locators.set(row.note_id, existing);
|
|
2263
|
+
}
|
|
2264
|
+
for (const noteId of noteIds) {
|
|
2265
|
+
if (!locators.has(noteId)) {
|
|
2266
|
+
locators.set(noteId, [{ note_id: noteId, chunk: { kind: "current", index: 0 }, metadata_paths: [...metadataPaths] }]);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
return locators;
|
|
2270
|
+
}
|
|
2271
|
+
metadataPaths(options) {
|
|
2272
|
+
return [...new Set((options.metadataPredicates ?? []).map((predicate) => predicate.path))];
|
|
2273
|
+
}
|
|
2075
2274
|
async search(query, options = {}, queryEmbedding) {
|
|
2076
2275
|
const { limit = 20, offset = 0 } = options;
|
|
2077
2276
|
const mode = options.mode ?? "auto";
|
|
@@ -2098,17 +2297,21 @@ var SearchRepository = class {
|
|
|
2098
2297
|
const resolvedEmbeddingSet = await this.resolveEmbeddingSet(options);
|
|
2099
2298
|
const tsqFn = this.tsqueryFn(query);
|
|
2100
2299
|
const { conditions, params, nextIdx } = buildNoteConditions(options, 2);
|
|
2300
|
+
const metadata = buildMetadataPredicateConditions(options, nextIdx);
|
|
2301
|
+
conditions.push(...metadata.conditions);
|
|
2302
|
+
params.push(...metadata.params);
|
|
2101
2303
|
conditions.unshift(
|
|
2102
2304
|
`(n.tsv @@ ${tsqFn}('english', $1) OR
|
|
2103
2305
|
${COMBINED_TEXT_VECTOR_SQL2} @@ ${tsqFn}('english', $1))`
|
|
2104
2306
|
);
|
|
2105
|
-
let paramIdx = this.scopeToResolvedEmbeddingSet(conditions, params, nextIdx, resolvedEmbeddingSet);
|
|
2307
|
+
let paramIdx = this.scopeToResolvedEmbeddingSet(conditions, params, metadata.nextIdx, resolvedEmbeddingSet);
|
|
2106
2308
|
const allParams = [query, ...params];
|
|
2107
2309
|
const where = conditions.join(" AND ");
|
|
2108
2310
|
const countResult = await this.db.query(
|
|
2109
2311
|
`SELECT COUNT(*) as count
|
|
2110
2312
|
FROM note n
|
|
2111
2313
|
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2314
|
+
${metadata.joins.join("\n")}
|
|
2112
2315
|
${ATTACHMENT_TEXT_JOIN2}
|
|
2113
2316
|
WHERE ${where}`,
|
|
2114
2317
|
allParams
|
|
@@ -2129,6 +2332,7 @@ var SearchRepository = class {
|
|
|
2129
2332
|
) as snippet
|
|
2130
2333
|
FROM note n
|
|
2131
2334
|
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2335
|
+
${metadata.joins.join("\n")}
|
|
2132
2336
|
${ATTACHMENT_TEXT_JOIN2}
|
|
2133
2337
|
WHERE ${where}
|
|
2134
2338
|
ORDER BY rank DESC, n.created_at DESC
|
|
@@ -2145,12 +2349,14 @@ var SearchRepository = class {
|
|
|
2145
2349
|
const idsResult = await this.db.query(
|
|
2146
2350
|
`SELECT n.id FROM note n
|
|
2147
2351
|
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2352
|
+
${metadata.joins.join("\n")}
|
|
2148
2353
|
${ATTACHMENT_TEXT_JOIN2}
|
|
2149
2354
|
WHERE ${where}`,
|
|
2150
2355
|
allParams
|
|
2151
2356
|
);
|
|
2152
2357
|
facets = await this.fetchFacets(idsResult.rows.map((r) => r.id));
|
|
2153
2358
|
}
|
|
2359
|
+
const locatorMap = await this.fetchLocatorMap(resultIds, this.metadataPaths(options));
|
|
2154
2360
|
const baseResults = result.rows.map((r) => ({
|
|
2155
2361
|
id: r.id,
|
|
2156
2362
|
title: r.title,
|
|
@@ -2158,7 +2364,8 @@ var SearchRepository = class {
|
|
|
2158
2364
|
rank: r.rank,
|
|
2159
2365
|
created_at: r.created_at,
|
|
2160
2366
|
updated_at: r.updated_at,
|
|
2161
|
-
tags: tagMap.get(r.id) ?? []
|
|
2367
|
+
tags: tagMap.get(r.id) ?? [],
|
|
2368
|
+
locators: locatorMap.get(r.id) ?? []
|
|
2162
2369
|
}));
|
|
2163
2370
|
return {
|
|
2164
2371
|
results: this.attachEmbeddingStatus(baseResults, embeddingSet),
|
|
@@ -2176,12 +2383,17 @@ var SearchRepository = class {
|
|
|
2176
2383
|
const vectorStr = `[${queryEmbedding.join(",")}]`;
|
|
2177
2384
|
const resolvedEmbeddingSet = await this.resolveEmbeddingSet(options);
|
|
2178
2385
|
const { conditions, params, nextIdx } = buildNoteConditions(options, 1);
|
|
2179
|
-
|
|
2386
|
+
const metadata = buildMetadataPredicateConditions(options, nextIdx);
|
|
2387
|
+
conditions.push(...metadata.conditions);
|
|
2388
|
+
params.push(...metadata.params);
|
|
2389
|
+
let paramIdx = this.scopeToResolvedEmbeddingRows(conditions, params, metadata.nextIdx, resolvedEmbeddingSet);
|
|
2180
2390
|
const where = conditions.join(" AND ");
|
|
2181
2391
|
const countResult = await this.db.query(
|
|
2182
2392
|
`SELECT COUNT(*) as count
|
|
2183
2393
|
FROM embedding e
|
|
2184
2394
|
JOIN note n ON n.id = e.note_id
|
|
2395
|
+
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2396
|
+
${metadata.joins.join("\n")}
|
|
2185
2397
|
WHERE ${where}`,
|
|
2186
2398
|
params
|
|
2187
2399
|
);
|
|
@@ -2196,6 +2408,7 @@ var SearchRepository = class {
|
|
|
2196
2408
|
FROM embedding e
|
|
2197
2409
|
JOIN note n ON n.id = e.note_id
|
|
2198
2410
|
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2411
|
+
${metadata.joins.join("\n")}
|
|
2199
2412
|
${ATTACHMENT_TEXT_JOIN2}
|
|
2200
2413
|
WHERE ${where}
|
|
2201
2414
|
ORDER BY e.vector <=> $${vecIdx}::vector ASC
|
|
@@ -2204,9 +2417,15 @@ var SearchRepository = class {
|
|
|
2204
2417
|
);
|
|
2205
2418
|
const tagMap = await this.fetchTagMap(result.rows.map((r) => r.id));
|
|
2206
2419
|
const facets = options.include_facets ? await this.fetchFacets((await this.db.query(
|
|
2207
|
-
`SELECT n.id
|
|
2420
|
+
`SELECT n.id
|
|
2421
|
+
FROM embedding e
|
|
2422
|
+
JOIN note n ON n.id = e.note_id
|
|
2423
|
+
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2424
|
+
${metadata.joins.join("\n")}
|
|
2425
|
+
WHERE ${where}`,
|
|
2208
2426
|
params
|
|
2209
2427
|
)).rows.map((r) => r.id)) : void 0;
|
|
2428
|
+
const locatorMap = await this.fetchLocatorMap(result.rows.map((r) => r.id), this.metadataPaths(options));
|
|
2210
2429
|
return {
|
|
2211
2430
|
results: result.rows.map((r) => ({
|
|
2212
2431
|
id: r.id,
|
|
@@ -2216,7 +2435,8 @@ var SearchRepository = class {
|
|
|
2216
2435
|
created_at: r.created_at,
|
|
2217
2436
|
updated_at: r.updated_at,
|
|
2218
2437
|
tags: tagMap.get(r.id) ?? [],
|
|
2219
|
-
has_embedding: true
|
|
2438
|
+
has_embedding: true,
|
|
2439
|
+
locators: locatorMap.get(r.id) ?? []
|
|
2220
2440
|
})),
|
|
2221
2441
|
total,
|
|
2222
2442
|
query: "",
|
|
@@ -2234,12 +2454,15 @@ var SearchRepository = class {
|
|
|
2234
2454
|
const tsqFn = this.tsqueryFn(query);
|
|
2235
2455
|
const resolvedEmbeddingSet = await this.resolveEmbeddingSet(options);
|
|
2236
2456
|
const textCond = buildNoteConditions(options, 2);
|
|
2457
|
+
const textMeta = buildMetadataPredicateConditions(options, textCond.nextIdx);
|
|
2237
2458
|
const textConditions = [
|
|
2238
2459
|
...textCond.conditions,
|
|
2460
|
+
...textMeta.conditions,
|
|
2239
2461
|
`(n.tsv @@ ${tsqFn}('english', $1) OR
|
|
2240
2462
|
${COMBINED_TEXT_VECTOR_SQL2} @@ ${tsqFn}('english', $1))`
|
|
2241
2463
|
];
|
|
2242
|
-
|
|
2464
|
+
textCond.params.push(...textMeta.params);
|
|
2465
|
+
this.scopeToResolvedEmbeddingSet(textConditions, textCond.params, textMeta.nextIdx, resolvedEmbeddingSet);
|
|
2243
2466
|
const textWhere = textConditions.join(" AND ");
|
|
2244
2467
|
const textParams = [query, ...textCond.params];
|
|
2245
2468
|
const textResult = await this.db.query(
|
|
@@ -2250,6 +2473,7 @@ var SearchRepository = class {
|
|
|
2250
2473
|
) as rank
|
|
2251
2474
|
FROM note n
|
|
2252
2475
|
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2476
|
+
${textMeta.joins.join("\n")}
|
|
2253
2477
|
${ATTACHMENT_TEXT_JOIN2}
|
|
2254
2478
|
WHERE ${textWhere}
|
|
2255
2479
|
ORDER BY rank DESC
|
|
@@ -2257,13 +2481,18 @@ var SearchRepository = class {
|
|
|
2257
2481
|
textParams
|
|
2258
2482
|
);
|
|
2259
2483
|
const vecCond = buildNoteConditions(options, 1);
|
|
2260
|
-
|
|
2484
|
+
const vecMeta = buildMetadataPredicateConditions(options, vecCond.nextIdx);
|
|
2485
|
+
vecCond.conditions.push(...vecMeta.conditions);
|
|
2486
|
+
vecCond.params.push(...vecMeta.params);
|
|
2487
|
+
vecCond.nextIdx = this.scopeToResolvedEmbeddingRows(vecCond.conditions, vecCond.params, vecMeta.nextIdx, resolvedEmbeddingSet);
|
|
2261
2488
|
const vecWhere = vecCond.conditions.join(" AND ");
|
|
2262
2489
|
const vecVecIdx = vecCond.nextIdx;
|
|
2263
2490
|
const vectorResult = await this.db.query(
|
|
2264
2491
|
`SELECT n.id, (e.vector <=> $${vecVecIdx}::vector) as distance
|
|
2265
2492
|
FROM embedding e
|
|
2266
2493
|
JOIN note n ON n.id = e.note_id
|
|
2494
|
+
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2495
|
+
${vecMeta.joins.join("\n")}
|
|
2267
2496
|
WHERE ${vecWhere}
|
|
2268
2497
|
ORDER BY e.vector <=> $${vecVecIdx}::vector ASC
|
|
2269
2498
|
LIMIT 100`,
|
|
@@ -2296,6 +2525,7 @@ var SearchRepository = class {
|
|
|
2296
2525
|
this.fetchTagMap(pageIds),
|
|
2297
2526
|
this.fetchEmbeddingStatus(pageIds, resolvedEmbeddingSet, options.embeddingSetId)
|
|
2298
2527
|
]);
|
|
2528
|
+
const locatorMap = await this.fetchLocatorMap(pageIds, this.metadataPaths(options));
|
|
2299
2529
|
const facets = options.include_facets ? await this.fetchFacets(sortedIds) : void 0;
|
|
2300
2530
|
return {
|
|
2301
2531
|
results: pageIds.map((id) => {
|
|
@@ -2309,7 +2539,8 @@ var SearchRepository = class {
|
|
|
2309
2539
|
created_at: r.created_at,
|
|
2310
2540
|
updated_at: r.updated_at,
|
|
2311
2541
|
tags: tagMap.get(id) ?? [],
|
|
2312
|
-
has_embedding: embeddingSet.has(id)
|
|
2542
|
+
has_embedding: embeddingSet.has(id),
|
|
2543
|
+
locators: locatorMap.get(id) ?? []
|
|
2313
2544
|
};
|
|
2314
2545
|
}).filter((r) => r !== null),
|
|
2315
2546
|
total,
|
|
@@ -2325,10 +2556,17 @@ var SearchRepository = class {
|
|
|
2325
2556
|
const { limit = 20, offset = 0 } = options;
|
|
2326
2557
|
const resolvedEmbeddingSet = await this.resolveEmbeddingSet(options);
|
|
2327
2558
|
const { conditions, params, nextIdx } = buildNoteConditions(options, 1);
|
|
2328
|
-
|
|
2559
|
+
const metadata = buildMetadataPredicateConditions(options, nextIdx);
|
|
2560
|
+
conditions.push(...metadata.conditions);
|
|
2561
|
+
params.push(...metadata.params);
|
|
2562
|
+
let paramIdx = this.scopeToResolvedEmbeddingSet(conditions, params, metadata.nextIdx, resolvedEmbeddingSet);
|
|
2329
2563
|
const where = conditions.join(" AND ");
|
|
2330
2564
|
const countResult = await this.db.query(
|
|
2331
|
-
`SELECT COUNT(*) as count
|
|
2565
|
+
`SELECT COUNT(*) as count
|
|
2566
|
+
FROM note n
|
|
2567
|
+
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2568
|
+
${metadata.joins.join("\n")}
|
|
2569
|
+
WHERE ${where}`,
|
|
2332
2570
|
params
|
|
2333
2571
|
);
|
|
2334
2572
|
const total = parseInt(countResult.rows[0].count, 10);
|
|
@@ -2338,6 +2576,7 @@ var SearchRepository = class {
|
|
|
2338
2576
|
LEFT(${COMBINED_TEXT_SQL}, 200) as snippet
|
|
2339
2577
|
FROM note n
|
|
2340
2578
|
LEFT JOIN note_revised_current c ON c.note_id = n.id
|
|
2579
|
+
${metadata.joins.join("\n")}
|
|
2341
2580
|
${ATTACHMENT_TEXT_JOIN2}
|
|
2342
2581
|
WHERE ${where}
|
|
2343
2582
|
ORDER BY n.created_at DESC
|
|
@@ -2346,6 +2585,7 @@ var SearchRepository = class {
|
|
|
2346
2585
|
);
|
|
2347
2586
|
const resultIds = result.rows.map((r) => r.id);
|
|
2348
2587
|
const embeddingSet = await this.fetchEmbeddingStatus(resultIds, resolvedEmbeddingSet, options.embeddingSetId);
|
|
2588
|
+
const locatorMap = await this.fetchLocatorMap(resultIds, this.metadataPaths(options));
|
|
2349
2589
|
return {
|
|
2350
2590
|
results: result.rows.map((r) => ({
|
|
2351
2591
|
id: r.id,
|
|
@@ -2355,7 +2595,8 @@ var SearchRepository = class {
|
|
|
2355
2595
|
created_at: r.created_at,
|
|
2356
2596
|
updated_at: r.updated_at,
|
|
2357
2597
|
tags: [],
|
|
2358
|
-
has_embedding: embeddingSet.has(r.id)
|
|
2598
|
+
has_embedding: embeddingSet.has(r.id),
|
|
2599
|
+
locators: locatorMap.get(r.id) ?? []
|
|
2359
2600
|
})),
|
|
2360
2601
|
total,
|
|
2361
2602
|
query: "",
|
|
@@ -3493,6 +3734,7 @@ function createFortemi(config) {
|
|
|
3493
3734
|
|
|
3494
3735
|
// src/server-compatibility.ts
|
|
3495
3736
|
var FORTEMI_COMPATIBILITY_PATH = "/api/v1/system/compatibility";
|
|
3737
|
+
var FORTEMI_SERVER_COMPATIBILITY_REVISION = "2026-07-06";
|
|
3496
3738
|
var FORTEMI_COMPATIBILITY_STATES = [
|
|
3497
3739
|
"available",
|
|
3498
3740
|
"degraded",
|
|
@@ -3944,8 +4186,8 @@ async function migrateLegacyBlobStore(archiveName, target, indexedDbFactory) {
|
|
|
3944
4186
|
|
|
3945
4187
|
// src/blob-store.ts
|
|
3946
4188
|
var MemoryBlobStore = class {
|
|
3947
|
-
constructor(
|
|
3948
|
-
this.now =
|
|
4189
|
+
constructor(now2 = Date.now) {
|
|
4190
|
+
this.now = now2;
|
|
3949
4191
|
}
|
|
3950
4192
|
entries = /* @__PURE__ */ new Map();
|
|
3951
4193
|
async put(bytes) {
|
|
@@ -4018,12 +4260,12 @@ function toChecksum(hex) {
|
|
|
4018
4260
|
return CHECKSUM_PREFIX + hex;
|
|
4019
4261
|
}
|
|
4020
4262
|
var BytecaskBlobStore = class {
|
|
4021
|
-
constructor(facade, adapter, index, probe,
|
|
4263
|
+
constructor(facade, adapter, index, probe, now2 = Date.now) {
|
|
4022
4264
|
this.facade = facade;
|
|
4023
4265
|
this.adapter = adapter;
|
|
4024
4266
|
this.index = index;
|
|
4025
4267
|
this.probe = probe;
|
|
4026
|
-
this.now =
|
|
4268
|
+
this.now = now2;
|
|
4027
4269
|
}
|
|
4028
4270
|
async put(bytes) {
|
|
4029
4271
|
return toChecksum(await this.facade.put(bytes));
|
|
@@ -4158,6 +4400,482 @@ function createLazyBlobStore(archiveName, options) {
|
|
|
4158
4400
|
};
|
|
4159
4401
|
}
|
|
4160
4402
|
|
|
4403
|
+
// src/repositories/source-upsert-repository.ts
|
|
4404
|
+
var DEFAULT_MAX_ITEMS = 500;
|
|
4405
|
+
function assertSource(input) {
|
|
4406
|
+
if (!input.namespace || input.namespace.length > 128) throw new Error("Source namespace is required and must be <= 128 characters");
|
|
4407
|
+
if (!input.external_id || input.external_id.length > 1024) throw new Error("Source external_id is required and must be <= 1024 characters");
|
|
4408
|
+
if (!input.source_schema_version || input.source_schema_version.length > 64) {
|
|
4409
|
+
throw new Error("Source schema version is required and must be <= 64 characters");
|
|
4410
|
+
}
|
|
4411
|
+
if (!input.import_run_id || input.import_run_id.length > 128) throw new Error("Source import_run_id is required and must be <= 128 characters");
|
|
4412
|
+
}
|
|
4413
|
+
function sourceHash(source) {
|
|
4414
|
+
return computeHash(new TextEncoder().encode([
|
|
4415
|
+
source.tenant_id ?? "default",
|
|
4416
|
+
source.archive_id ?? "",
|
|
4417
|
+
source.namespace,
|
|
4418
|
+
source.external_id
|
|
4419
|
+
].join("\0")));
|
|
4420
|
+
}
|
|
4421
|
+
function contentDigest(content) {
|
|
4422
|
+
return computeHash(new TextEncoder().encode(content));
|
|
4423
|
+
}
|
|
4424
|
+
async function insertNote(tx, input, noteId, digest) {
|
|
4425
|
+
const originalId = generateId();
|
|
4426
|
+
if (input.source.archive_id) {
|
|
4427
|
+
await tx.query(
|
|
4428
|
+
`INSERT INTO archive (id, name)
|
|
4429
|
+
VALUES ($1, $2)
|
|
4430
|
+
ON CONFLICT (id) DO NOTHING`,
|
|
4431
|
+
[input.source.archive_id, input.source.archive_id]
|
|
4432
|
+
);
|
|
4433
|
+
}
|
|
4434
|
+
await tx.query(
|
|
4435
|
+
`INSERT INTO note (id, archive_id, title, format, source, visibility)
|
|
4436
|
+
VALUES ($1, $2, $3, $4, $5, $6)`,
|
|
4437
|
+
[
|
|
4438
|
+
noteId,
|
|
4439
|
+
input.source.archive_id ?? null,
|
|
4440
|
+
input.title ?? null,
|
|
4441
|
+
input.format ?? "markdown",
|
|
4442
|
+
`source:${input.source.namespace}`,
|
|
4443
|
+
input.visibility ?? "private"
|
|
4444
|
+
]
|
|
4445
|
+
);
|
|
4446
|
+
await tx.query(
|
|
4447
|
+
`INSERT INTO note_original (id, note_id, content, content_hash)
|
|
4448
|
+
VALUES ($1, $2, $3, $4)`,
|
|
4449
|
+
[originalId, noteId, input.content, digest]
|
|
4450
|
+
);
|
|
4451
|
+
await tx.query(
|
|
4452
|
+
`INSERT INTO note_revised_current (note_id, content, ai_metadata)
|
|
4453
|
+
VALUES ($1, $2, $3::jsonb)`,
|
|
4454
|
+
[noteId, input.content, JSON.stringify(input.metadata ?? null)]
|
|
4455
|
+
);
|
|
4456
|
+
}
|
|
4457
|
+
async function updateNote(tx, input, noteId, outcome) {
|
|
4458
|
+
if (input.source.archive_id) {
|
|
4459
|
+
await tx.query(
|
|
4460
|
+
`INSERT INTO archive (id, name)
|
|
4461
|
+
VALUES ($1, $2)
|
|
4462
|
+
ON CONFLICT (id) DO NOTHING`,
|
|
4463
|
+
[input.source.archive_id, input.source.archive_id]
|
|
4464
|
+
);
|
|
4465
|
+
}
|
|
4466
|
+
if (outcome === "versioned") {
|
|
4467
|
+
const count2 = await tx.query(
|
|
4468
|
+
`SELECT COUNT(*) AS count FROM note_revision WHERE note_id = $1`,
|
|
4469
|
+
[noteId]
|
|
4470
|
+
);
|
|
4471
|
+
const current = await tx.query(
|
|
4472
|
+
`SELECT content, ai_metadata FROM note_revised_current WHERE note_id = $1`,
|
|
4473
|
+
[noteId]
|
|
4474
|
+
);
|
|
4475
|
+
const nextRevision = Number.parseInt(count2.rows[0]?.count ?? "0", 10) + 1;
|
|
4476
|
+
if (current.rows[0]) {
|
|
4477
|
+
await tx.query(
|
|
4478
|
+
`INSERT INTO note_revision (id, note_id, revision_number, type, content, ai_metadata)
|
|
4479
|
+
VALUES ($1, $2, $3, 'source-import', $4, $5::jsonb)`,
|
|
4480
|
+
[
|
|
4481
|
+
generateId(),
|
|
4482
|
+
noteId,
|
|
4483
|
+
nextRevision,
|
|
4484
|
+
current.rows[0].content,
|
|
4485
|
+
JSON.stringify(current.rows[0].ai_metadata ?? null)
|
|
4486
|
+
]
|
|
4487
|
+
);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
await tx.query(
|
|
4491
|
+
`UPDATE note
|
|
4492
|
+
SET title = $1, format = $2, visibility = $3, archive_id = $4, updated_at = now(), deleted_at = NULL
|
|
4493
|
+
WHERE id = $5`,
|
|
4494
|
+
[
|
|
4495
|
+
input.title ?? null,
|
|
4496
|
+
input.format ?? "markdown",
|
|
4497
|
+
input.visibility ?? "private",
|
|
4498
|
+
input.source.archive_id ?? null,
|
|
4499
|
+
noteId
|
|
4500
|
+
]
|
|
4501
|
+
);
|
|
4502
|
+
await tx.query(
|
|
4503
|
+
`UPDATE note_revised_current
|
|
4504
|
+
SET content = $1, ai_metadata = $2::jsonb, is_user_edited = false, updated_at = now()
|
|
4505
|
+
WHERE note_id = $3`,
|
|
4506
|
+
[input.content, JSON.stringify(input.metadata ?? null), noteId]
|
|
4507
|
+
);
|
|
4508
|
+
}
|
|
4509
|
+
var SourceUpsertRepository = class {
|
|
4510
|
+
constructor(db, events) {
|
|
4511
|
+
this.db = db;
|
|
4512
|
+
this.events = events;
|
|
4513
|
+
}
|
|
4514
|
+
async upsertBatch(items, options = {}) {
|
|
4515
|
+
const maxItems = options.maxItems ?? DEFAULT_MAX_ITEMS;
|
|
4516
|
+
if (items.length > maxItems) throw new Error(`Source upsert batch exceeds the ${maxItems} item bound`);
|
|
4517
|
+
if (items.length === 0) {
|
|
4518
|
+
return {
|
|
4519
|
+
import_run_id: "",
|
|
4520
|
+
dry_run: options.dryRun === true,
|
|
4521
|
+
outcomes: [],
|
|
4522
|
+
counts: { inserted: 0, unchanged: 0, versioned: 0, replaced: 0, conflict: 0, rejected: 0 }
|
|
4523
|
+
};
|
|
4524
|
+
}
|
|
4525
|
+
const outcomes = [];
|
|
4526
|
+
for (const [index, item] of items.entries()) {
|
|
4527
|
+
try {
|
|
4528
|
+
assertSource(item.source);
|
|
4529
|
+
outcomes.push({
|
|
4530
|
+
index,
|
|
4531
|
+
outcome: "rejected",
|
|
4532
|
+
external_id_hash: sourceHash(item.source),
|
|
4533
|
+
content_digest: contentDigest(item.content)
|
|
4534
|
+
});
|
|
4535
|
+
} catch (error) {
|
|
4536
|
+
outcomes.push({
|
|
4537
|
+
index,
|
|
4538
|
+
outcome: "rejected",
|
|
4539
|
+
external_id_hash: item.source ? sourceHash({ ...item.source, external_id: item.source.external_id ?? "" }) : "",
|
|
4540
|
+
content_digest: contentDigest(item.content ?? ""),
|
|
4541
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
4542
|
+
});
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
if (outcomes.some((outcome) => outcome.reason)) {
|
|
4546
|
+
return this.finish(items[0].source?.import_run_id ?? "", options.dryRun === true, outcomes);
|
|
4547
|
+
}
|
|
4548
|
+
if (options.dryRun) {
|
|
4549
|
+
const preview = [];
|
|
4550
|
+
for (const [index, item] of items.entries()) {
|
|
4551
|
+
const externalIdHash = sourceHash(item.source);
|
|
4552
|
+
const digest = contentDigest(item.content);
|
|
4553
|
+
const existing = await this.db.query(
|
|
4554
|
+
`SELECT note_id, content_digest
|
|
4555
|
+
FROM source_identity
|
|
4556
|
+
WHERE tenant_id = $1
|
|
4557
|
+
AND archive_id IS NOT DISTINCT FROM $2
|
|
4558
|
+
AND namespace = $3
|
|
4559
|
+
AND external_id = $4
|
|
4560
|
+
LIMIT 1`,
|
|
4561
|
+
[item.source.tenant_id ?? "default", item.source.archive_id ?? null, item.source.namespace, item.source.external_id]
|
|
4562
|
+
);
|
|
4563
|
+
if (existing.rows.length === 0) {
|
|
4564
|
+
preview.push({ index, outcome: "inserted", external_id_hash: externalIdHash, content_digest: digest });
|
|
4565
|
+
} else if (existing.rows[0].content_digest === digest) {
|
|
4566
|
+
preview.push({ index, outcome: "unchanged", note_id: existing.rows[0].note_id, external_id_hash: externalIdHash, content_digest: digest });
|
|
4567
|
+
} else if ((item.policy ?? "version") === "conflict") {
|
|
4568
|
+
preview.push({ index, outcome: "conflict", note_id: existing.rows[0].note_id, external_id_hash: externalIdHash, content_digest: digest });
|
|
4569
|
+
} else {
|
|
4570
|
+
preview.push({
|
|
4571
|
+
index,
|
|
4572
|
+
outcome: item.policy === "replace" ? "replaced" : "versioned",
|
|
4573
|
+
note_id: existing.rows[0].note_id,
|
|
4574
|
+
external_id_hash: externalIdHash,
|
|
4575
|
+
content_digest: digest
|
|
4576
|
+
});
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
return this.finish(items[0].source.import_run_id, true, preview);
|
|
4580
|
+
}
|
|
4581
|
+
await this.db.transaction(async (tx) => {
|
|
4582
|
+
for (const [index, item] of items.entries()) {
|
|
4583
|
+
const externalIdHash = sourceHash(item.source);
|
|
4584
|
+
const digest = contentDigest(item.content);
|
|
4585
|
+
const existing = await tx.query(
|
|
4586
|
+
`SELECT note_id, content_digest
|
|
4587
|
+
FROM source_identity
|
|
4588
|
+
WHERE tenant_id = $1
|
|
4589
|
+
AND archive_id IS NOT DISTINCT FROM $2
|
|
4590
|
+
AND namespace = $3
|
|
4591
|
+
AND external_id = $4
|
|
4592
|
+
LIMIT 1`,
|
|
4593
|
+
[item.source.tenant_id ?? "default", item.source.archive_id ?? null, item.source.namespace, item.source.external_id]
|
|
4594
|
+
);
|
|
4595
|
+
if (existing.rows.length === 0) {
|
|
4596
|
+
const noteId = item.source.caller_stable_id ?? generateId();
|
|
4597
|
+
await insertNote(tx, item, noteId, digest);
|
|
4598
|
+
await tx.query(
|
|
4599
|
+
`INSERT INTO source_identity
|
|
4600
|
+
(id, tenant_id, archive_id, namespace, external_id, external_id_hash,
|
|
4601
|
+
source_schema_version, content_digest, import_run_id, caller_stable_id, note_id)
|
|
4602
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
|
|
4603
|
+
[
|
|
4604
|
+
generateId(),
|
|
4605
|
+
item.source.tenant_id ?? "default",
|
|
4606
|
+
item.source.archive_id ?? null,
|
|
4607
|
+
item.source.namespace,
|
|
4608
|
+
item.source.external_id,
|
|
4609
|
+
externalIdHash,
|
|
4610
|
+
item.source.source_schema_version,
|
|
4611
|
+
digest,
|
|
4612
|
+
item.source.import_run_id,
|
|
4613
|
+
item.source.caller_stable_id ?? null,
|
|
4614
|
+
noteId
|
|
4615
|
+
]
|
|
4616
|
+
);
|
|
4617
|
+
outcomes[index] = { index, outcome: "inserted", note_id: noteId, external_id_hash: externalIdHash, content_digest: digest };
|
|
4618
|
+
continue;
|
|
4619
|
+
}
|
|
4620
|
+
const row = existing.rows[0];
|
|
4621
|
+
if (row.content_digest === digest) {
|
|
4622
|
+
outcomes[index] = { index, outcome: "unchanged", note_id: row.note_id, external_id_hash: externalIdHash, content_digest: digest };
|
|
4623
|
+
continue;
|
|
4624
|
+
}
|
|
4625
|
+
const policy = item.policy ?? "version";
|
|
4626
|
+
if (policy === "conflict") {
|
|
4627
|
+
outcomes[index] = { index, outcome: "conflict", note_id: row.note_id, external_id_hash: externalIdHash, content_digest: digest };
|
|
4628
|
+
continue;
|
|
4629
|
+
}
|
|
4630
|
+
const outcome = policy === "replace" ? "replaced" : "versioned";
|
|
4631
|
+
await updateNote(tx, item, row.note_id, outcome);
|
|
4632
|
+
await tx.query(
|
|
4633
|
+
`UPDATE source_identity
|
|
4634
|
+
SET source_schema_version = $1, content_digest = $2, import_run_id = $3, updated_at = now()
|
|
4635
|
+
WHERE note_id = $4
|
|
4636
|
+
AND tenant_id = $5
|
|
4637
|
+
AND archive_id IS NOT DISTINCT FROM $6
|
|
4638
|
+
AND namespace = $7
|
|
4639
|
+
AND external_id = $8`,
|
|
4640
|
+
[
|
|
4641
|
+
item.source.source_schema_version,
|
|
4642
|
+
digest,
|
|
4643
|
+
item.source.import_run_id,
|
|
4644
|
+
row.note_id,
|
|
4645
|
+
item.source.tenant_id ?? "default",
|
|
4646
|
+
item.source.archive_id ?? null,
|
|
4647
|
+
item.source.namespace,
|
|
4648
|
+
item.source.external_id
|
|
4649
|
+
]
|
|
4650
|
+
);
|
|
4651
|
+
outcomes[index] = { index, outcome, note_id: row.note_id, external_id_hash: externalIdHash, content_digest: digest };
|
|
4652
|
+
}
|
|
4653
|
+
if (hasMaterialChange(outcomes)) {
|
|
4654
|
+
await tx.query(
|
|
4655
|
+
`INSERT INTO source_import_run (id, tenant_id, archive_id, namespace, completed_at, checkpoint, receipt)
|
|
4656
|
+
VALUES ($1, $2, $3, $4, now(), $5::jsonb, $6::jsonb)
|
|
4657
|
+
ON CONFLICT (id) DO UPDATE
|
|
4658
|
+
SET completed_at = EXCLUDED.completed_at,
|
|
4659
|
+
checkpoint = EXCLUDED.checkpoint,
|
|
4660
|
+
receipt = EXCLUDED.receipt`,
|
|
4661
|
+
[
|
|
4662
|
+
items[0].source.import_run_id,
|
|
4663
|
+
items[0].source.tenant_id ?? "default",
|
|
4664
|
+
items[0].source.archive_id ?? null,
|
|
4665
|
+
items[0].source.namespace,
|
|
4666
|
+
JSON.stringify({ item_count: items.length }),
|
|
4667
|
+
JSON.stringify({ counts: countOutcomes(outcomes) })
|
|
4668
|
+
]
|
|
4669
|
+
);
|
|
4670
|
+
}
|
|
4671
|
+
});
|
|
4672
|
+
if (hasMaterialChange(outcomes)) {
|
|
4673
|
+
this.events?.emit("source.upserted", { importRunId: items[0].source.import_run_id, counts: countOutcomes(outcomes) });
|
|
4674
|
+
}
|
|
4675
|
+
return this.finish(items[0].source.import_run_id, false, outcomes);
|
|
4676
|
+
}
|
|
4677
|
+
finish(importRunId, dryRun, outcomes) {
|
|
4678
|
+
return { import_run_id: importRunId, dry_run: dryRun, outcomes, counts: countOutcomes(outcomes) };
|
|
4679
|
+
}
|
|
4680
|
+
};
|
|
4681
|
+
function hasMaterialChange(outcomes) {
|
|
4682
|
+
return outcomes.some((outcome) => outcome.outcome === "inserted" || outcome.outcome === "versioned" || outcome.outcome === "replaced");
|
|
4683
|
+
}
|
|
4684
|
+
function countOutcomes(outcomes) {
|
|
4685
|
+
return {
|
|
4686
|
+
inserted: outcomes.filter((outcome) => outcome.outcome === "inserted").length,
|
|
4687
|
+
unchanged: outcomes.filter((outcome) => outcome.outcome === "unchanged").length,
|
|
4688
|
+
versioned: outcomes.filter((outcome) => outcome.outcome === "versioned").length,
|
|
4689
|
+
replaced: outcomes.filter((outcome) => outcome.outcome === "replaced").length,
|
|
4690
|
+
conflict: outcomes.filter((outcome) => outcome.outcome === "conflict").length,
|
|
4691
|
+
rejected: outcomes.filter((outcome) => outcome.outcome === "rejected").length
|
|
4692
|
+
};
|
|
4693
|
+
}
|
|
4694
|
+
|
|
4695
|
+
// src/repositories/lifecycle-purge-repository.ts
|
|
4696
|
+
function zeroCounts() {
|
|
4697
|
+
return {
|
|
4698
|
+
notes: 0,
|
|
4699
|
+
revisions: 0,
|
|
4700
|
+
links: 0,
|
|
4701
|
+
tags: 0,
|
|
4702
|
+
embeddings: 0,
|
|
4703
|
+
attachments: 0,
|
|
4704
|
+
blobs: 0,
|
|
4705
|
+
graph_edges: 0,
|
|
4706
|
+
provenance_edges: 0,
|
|
4707
|
+
source_identities: 0
|
|
4708
|
+
};
|
|
4709
|
+
}
|
|
4710
|
+
function selectorHash(selector) {
|
|
4711
|
+
return computeHash(new TextEncoder().encode(JSON.stringify({
|
|
4712
|
+
tenant_id: selector.tenant_id ?? "default",
|
|
4713
|
+
archive_id: selector.archive_id ?? null,
|
|
4714
|
+
note_ids: [...selector.note_ids ?? []].sort(),
|
|
4715
|
+
source: selector.source ? {
|
|
4716
|
+
namespace: selector.source.namespace,
|
|
4717
|
+
external_id_hash: selector.source.external_id ? computeHash(new TextEncoder().encode(selector.source.external_id)) : null
|
|
4718
|
+
} : null
|
|
4719
|
+
})));
|
|
4720
|
+
}
|
|
4721
|
+
function buildSelectorWhere(selector, startIdx) {
|
|
4722
|
+
const clauses = [];
|
|
4723
|
+
const params = [];
|
|
4724
|
+
let idx = startIdx;
|
|
4725
|
+
if (selector.note_ids?.length) {
|
|
4726
|
+
clauses.push(`n.id = ANY($${idx++})`);
|
|
4727
|
+
params.push([...selector.note_ids]);
|
|
4728
|
+
}
|
|
4729
|
+
if (selector.tenant_id !== void 0 || selector.archive_id !== void 0 || selector.source) {
|
|
4730
|
+
clauses.push(`EXISTS (
|
|
4731
|
+
SELECT 1 FROM source_identity si
|
|
4732
|
+
WHERE si.note_id = n.id
|
|
4733
|
+
AND si.tenant_id = $${idx++}
|
|
4734
|
+
AND si.archive_id IS NOT DISTINCT FROM $${idx++}
|
|
4735
|
+
${selector.source ? `AND si.namespace = $${idx++}` : ""}
|
|
4736
|
+
${selector.source?.external_id ? `AND si.external_id = $${idx++}` : ""}
|
|
4737
|
+
)`);
|
|
4738
|
+
params.push(selector.tenant_id ?? "default", selector.archive_id ?? null);
|
|
4739
|
+
if (selector.source) params.push(selector.source.namespace);
|
|
4740
|
+
if (selector.source?.external_id) params.push(selector.source.external_id);
|
|
4741
|
+
}
|
|
4742
|
+
if (clauses.length === 0) throw new Error("Purge selector must target note_ids or source identity");
|
|
4743
|
+
return { sql: clauses.join(" AND "), params };
|
|
4744
|
+
}
|
|
4745
|
+
async function selectedNoteIds(db, selector) {
|
|
4746
|
+
const where = buildSelectorWhere(selector, 1);
|
|
4747
|
+
const result = await db.query(
|
|
4748
|
+
`SELECT n.id FROM note n WHERE ${where.sql} ORDER BY n.id`,
|
|
4749
|
+
where.params
|
|
4750
|
+
);
|
|
4751
|
+
return result.rows.map((row) => row.id);
|
|
4752
|
+
}
|
|
4753
|
+
var LifecyclePurgeRepository = class {
|
|
4754
|
+
constructor(db, events) {
|
|
4755
|
+
this.db = db;
|
|
4756
|
+
this.events = events;
|
|
4757
|
+
}
|
|
4758
|
+
async preview(selector) {
|
|
4759
|
+
const noteIds = await selectedNoteIds(this.db, selector);
|
|
4760
|
+
return { selector_hash: selectorHash(selector), counts: await this.count(noteIds) };
|
|
4761
|
+
}
|
|
4762
|
+
async purge(selector, operationKey) {
|
|
4763
|
+
const existing = await this.db.query(
|
|
4764
|
+
`SELECT id, operation_key, tenant_id, archive_id, selector_hash, outcome, counts, completed_at, policy
|
|
4765
|
+
FROM deletion_receipt
|
|
4766
|
+
WHERE operation_key = $1`,
|
|
4767
|
+
[operationKey]
|
|
4768
|
+
);
|
|
4769
|
+
if (existing.rows[0]) return existing.rows[0];
|
|
4770
|
+
const hash = selectorHash(selector);
|
|
4771
|
+
let receipt;
|
|
4772
|
+
await this.db.transaction(async (tx) => {
|
|
4773
|
+
const noteIds = await selectedNoteIds(tx, selector);
|
|
4774
|
+
const counts = await this.count(noteIds, tx);
|
|
4775
|
+
await this.deleteSelected(tx, noteIds);
|
|
4776
|
+
receipt = {
|
|
4777
|
+
id: generateId(),
|
|
4778
|
+
operation_key: operationKey,
|
|
4779
|
+
tenant_id: selector.tenant_id ?? "default",
|
|
4780
|
+
archive_id: selector.archive_id ?? null,
|
|
4781
|
+
selector_hash: hash,
|
|
4782
|
+
outcome: "completed",
|
|
4783
|
+
counts,
|
|
4784
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4785
|
+
policy: {
|
|
4786
|
+
authority: "fortemi#1092",
|
|
4787
|
+
mode: "terminal-purge",
|
|
4788
|
+
receipt_contains_content: false
|
|
4789
|
+
}
|
|
4790
|
+
};
|
|
4791
|
+
await tx.query(
|
|
4792
|
+
`INSERT INTO deletion_receipt
|
|
4793
|
+
(id, operation_key, tenant_id, archive_id, selector_hash, outcome, counts, completed_at, policy)
|
|
4794
|
+
VALUES ($1, $2, $3, $4, $5, 'completed', $6::jsonb, $7, $8::jsonb)`,
|
|
4795
|
+
[
|
|
4796
|
+
receipt.id,
|
|
4797
|
+
receipt.operation_key,
|
|
4798
|
+
receipt.tenant_id,
|
|
4799
|
+
receipt.archive_id,
|
|
4800
|
+
receipt.selector_hash,
|
|
4801
|
+
JSON.stringify(receipt.counts),
|
|
4802
|
+
receipt.completed_at,
|
|
4803
|
+
JSON.stringify(receipt.policy)
|
|
4804
|
+
]
|
|
4805
|
+
);
|
|
4806
|
+
});
|
|
4807
|
+
const completedReceipt = receipt;
|
|
4808
|
+
if (!completedReceipt) throw new Error("Purge transaction did not produce a receipt");
|
|
4809
|
+
this.events?.emit("purge.completed", { receiptId: completedReceipt.id, counts: completedReceipt.counts });
|
|
4810
|
+
return completedReceipt;
|
|
4811
|
+
}
|
|
4812
|
+
async count(noteIds, db = this.db) {
|
|
4813
|
+
const counts = zeroCounts();
|
|
4814
|
+
if (noteIds.length === 0) return counts;
|
|
4815
|
+
const params = [noteIds];
|
|
4816
|
+
const rows = await Promise.all([
|
|
4817
|
+
db.query("SELECT COUNT(*) AS count FROM note WHERE id = ANY($1)", params),
|
|
4818
|
+
db.query("SELECT COUNT(*) AS count FROM note_revision WHERE note_id = ANY($1)", params),
|
|
4819
|
+
db.query("SELECT COUNT(*) AS count FROM link WHERE source_note_id = ANY($1) OR target_note_id = ANY($1)", params),
|
|
4820
|
+
db.query("SELECT COUNT(*) AS count FROM note_tag WHERE note_id = ANY($1)", params),
|
|
4821
|
+
db.query("SELECT COUNT(*) AS count FROM embedding WHERE note_id = ANY($1)", params),
|
|
4822
|
+
db.query("SELECT COUNT(*) AS count FROM attachment WHERE note_id = ANY($1)", params),
|
|
4823
|
+
db.query(
|
|
4824
|
+
`SELECT COUNT(*) AS count FROM attachment_blob ab
|
|
4825
|
+
WHERE EXISTS (SELECT 1 FROM attachment a WHERE a.blob_id = ab.id AND a.note_id = ANY($1))`,
|
|
4826
|
+
params
|
|
4827
|
+
),
|
|
4828
|
+
db.query("SELECT COUNT(*) AS count FROM graph_edge_artifact WHERE from_note_id = ANY($1) OR to_note_id = ANY($1)", params),
|
|
4829
|
+
db.query(
|
|
4830
|
+
`SELECT COUNT(*) AS count FROM provenance_edge
|
|
4831
|
+
WHERE (entity_type = 'note' AND entity_id = ANY($1))
|
|
4832
|
+
OR (attributes ->> 'note_id') = ANY($1)`,
|
|
4833
|
+
params
|
|
4834
|
+
),
|
|
4835
|
+
db.query("SELECT COUNT(*) AS count FROM source_identity WHERE note_id = ANY($1)", params)
|
|
4836
|
+
]);
|
|
4837
|
+
const values = rows.map((row) => Number.parseInt(row.rows[0]?.count ?? "0", 10));
|
|
4838
|
+
[
|
|
4839
|
+
counts.notes,
|
|
4840
|
+
counts.revisions,
|
|
4841
|
+
counts.links,
|
|
4842
|
+
counts.tags,
|
|
4843
|
+
counts.embeddings,
|
|
4844
|
+
counts.attachments,
|
|
4845
|
+
counts.blobs,
|
|
4846
|
+
counts.graph_edges,
|
|
4847
|
+
counts.provenance_edges,
|
|
4848
|
+
counts.source_identities
|
|
4849
|
+
] = values;
|
|
4850
|
+
return counts;
|
|
4851
|
+
}
|
|
4852
|
+
async deleteSelected(tx, noteIds) {
|
|
4853
|
+
if (noteIds.length === 0) return;
|
|
4854
|
+
const params = [noteIds];
|
|
4855
|
+
await tx.query("DELETE FROM community_assignment WHERE note_id = ANY($1)", params);
|
|
4856
|
+
await tx.query("DELETE FROM graph_edge_artifact WHERE from_note_id = ANY($1) OR to_note_id = ANY($1)", params);
|
|
4857
|
+
await tx.query("DELETE FROM embedding_set_member WHERE note_id = ANY($1)", params);
|
|
4858
|
+
await tx.query("DELETE FROM embedding WHERE note_id = ANY($1)", params);
|
|
4859
|
+
await tx.query("DELETE FROM attachment_embedding WHERE attachment_id IN (SELECT id FROM attachment WHERE note_id = ANY($1))", params);
|
|
4860
|
+
await tx.query("DELETE FROM attachment WHERE note_id = ANY($1)", params);
|
|
4861
|
+
await tx.query(
|
|
4862
|
+
`DELETE FROM attachment_blob ab
|
|
4863
|
+
WHERE NOT EXISTS (SELECT 1 FROM attachment a WHERE a.blob_id = ab.id)`
|
|
4864
|
+
);
|
|
4865
|
+
await tx.query("DELETE FROM source_identity WHERE note_id = ANY($1)", params);
|
|
4866
|
+
await tx.query("DELETE FROM provenance_edge WHERE (entity_type = $2 AND entity_id = ANY($1)) OR (attributes ->> $3) = ANY($1)", [noteIds, "note", "note_id"]);
|
|
4867
|
+
await tx.query("DELETE FROM job_queue WHERE note_id = ANY($1)", params);
|
|
4868
|
+
await tx.query("DELETE FROM collection_note WHERE note_id = ANY($1)", params);
|
|
4869
|
+
await tx.query("DELETE FROM note_tag WHERE note_id = ANY($1)", params);
|
|
4870
|
+
await tx.query("DELETE FROM link WHERE source_note_id = ANY($1) OR target_note_id = ANY($1)", params);
|
|
4871
|
+
await tx.query("DELETE FROM note_revision WHERE note_id = ANY($1)", params);
|
|
4872
|
+
await tx.query("DELETE FROM note_revised_current WHERE note_id = ANY($1)", params);
|
|
4873
|
+
await tx.query("DELETE FROM note_original WHERE note_id = ANY($1)", params);
|
|
4874
|
+
await tx.query("DELETE FROM shard_field_presence WHERE component = $2 AND record_id = ANY($1)", [noteIds, "notes"]);
|
|
4875
|
+
await tx.query("DELETE FROM note WHERE id = ANY($1)", params);
|
|
4876
|
+
}
|
|
4877
|
+
};
|
|
4878
|
+
|
|
4161
4879
|
// src/repositories/graph-repository.ts
|
|
4162
4880
|
var SIMILARITY_GRAPH_ALGORITHM = "knn-batched-v1";
|
|
4163
4881
|
var DEFAULT_GRAPH_BATCH_SIZE = 64;
|
|
@@ -6957,11 +7675,11 @@ var FallbackRouter = class {
|
|
|
6957
7675
|
// -------------------------------------------------------------------------
|
|
6958
7676
|
/** Get providers not currently in cooldown */
|
|
6959
7677
|
getAvailableProviders() {
|
|
6960
|
-
const
|
|
7678
|
+
const now2 = Date.now();
|
|
6961
7679
|
return this.providers.filter((p) => {
|
|
6962
7680
|
const cd = this.cooldownMap.get(p.id);
|
|
6963
7681
|
if (!cd) return true;
|
|
6964
|
-
if (
|
|
7682
|
+
if (now2 >= cd.expiresAt) {
|
|
6965
7683
|
this.cooldownMap.delete(p.id);
|
|
6966
7684
|
return true;
|
|
6967
7685
|
}
|
|
@@ -6970,10 +7688,10 @@ var FallbackRouter = class {
|
|
|
6970
7688
|
}
|
|
6971
7689
|
/** Get providers in cooldown with their expiry info */
|
|
6972
7690
|
getCoolingDown() {
|
|
6973
|
-
const
|
|
7691
|
+
const now2 = Date.now();
|
|
6974
7692
|
const result = [];
|
|
6975
7693
|
for (const [id, entry] of this.cooldownMap) {
|
|
6976
|
-
if (
|
|
7694
|
+
if (now2 < entry.expiresAt) {
|
|
6977
7695
|
result.push({ providerId: id, category: entry.category, expiresAt: entry.expiresAt });
|
|
6978
7696
|
}
|
|
6979
7697
|
}
|
|
@@ -27958,13 +28676,13 @@ async function liveRepresentationLosses(db) {
|
|
|
27958
28676
|
const result = await db.query(
|
|
27959
28677
|
`SELECT COUNT(*) AS count FROM ${table} WHERE deleted_at IS NOT NULL`
|
|
27960
28678
|
);
|
|
27961
|
-
const
|
|
27962
|
-
if (
|
|
28679
|
+
const count2 = Number(result.rows[0]?.count ?? 0);
|
|
28680
|
+
if (count2 > 0) {
|
|
27963
28681
|
losses.push({
|
|
27964
28682
|
code: "unrepresentable-live-tombstone",
|
|
27965
28683
|
component,
|
|
27966
|
-
count,
|
|
27967
|
-
message: `${
|
|
28684
|
+
count: count2,
|
|
28685
|
+
message: `${count2} ${table} tombstone(s) have no full-v1 wire field`,
|
|
27968
28686
|
action: "reject",
|
|
27969
28687
|
reason: "full-v1-live-production"
|
|
27970
28688
|
});
|
|
@@ -27996,15 +28714,15 @@ async function liveRepresentationLosses(db) {
|
|
|
27996
28714
|
for (const row of vectorDimensions.rows) {
|
|
27997
28715
|
const dimension = Number(row.dimension);
|
|
27998
28716
|
if (dimension === 768) continue;
|
|
27999
|
-
const
|
|
28717
|
+
const count2 = Number(row.count);
|
|
28000
28718
|
losses.push({
|
|
28001
28719
|
code: "unrepresentable-live-embedding-dimension",
|
|
28002
28720
|
component: "embeddings",
|
|
28003
|
-
count,
|
|
28721
|
+
count: count2,
|
|
28004
28722
|
field_path: "/vector",
|
|
28005
28723
|
source_state: "value",
|
|
28006
28724
|
destination_capability: "full-v1 requires exactly 768 vector dimensions",
|
|
28007
|
-
message: `${
|
|
28725
|
+
message: `${count2} embedding vector(s) have ${dimension} dimensions`,
|
|
28008
28726
|
action: "reject",
|
|
28009
28727
|
reason: "full-v1-live-production"
|
|
28010
28728
|
});
|
|
@@ -28564,16 +29282,27 @@ async function collectCoreV1Losses(db, options) {
|
|
|
28564
29282
|
{ component: "community_assignments", table: "community_assignment" }
|
|
28565
29283
|
];
|
|
28566
29284
|
for (const { component, table } of componentCounts) {
|
|
28567
|
-
const
|
|
28568
|
-
if (
|
|
29285
|
+
const count2 = await rowCount(db, `SELECT COUNT(*) AS count FROM ${table}`);
|
|
29286
|
+
if (count2 > 0) {
|
|
28569
29287
|
losses.push({
|
|
28570
29288
|
code: "component-outside-profile",
|
|
28571
29289
|
component,
|
|
28572
|
-
count,
|
|
28573
|
-
message: `${
|
|
29290
|
+
count: count2,
|
|
29291
|
+
message: `${count2} ${component} record(s) are outside core-v1 and were omitted.`
|
|
28574
29292
|
});
|
|
28575
29293
|
}
|
|
28576
29294
|
}
|
|
29295
|
+
const sourceIdentities = await rowCount(db, "SELECT COUNT(*) AS count FROM source_identity");
|
|
29296
|
+
if (sourceIdentities > 0) {
|
|
29297
|
+
losses.push({
|
|
29298
|
+
code: "source-identity-outside-profile",
|
|
29299
|
+
count: sourceIdentities,
|
|
29300
|
+
field_path: "source_identity",
|
|
29301
|
+
action: "omit",
|
|
29302
|
+
destination_capability: "core-v1 does not declare source-addressed identity mappings",
|
|
29303
|
+
message: `${sourceIdentities} source identity mapping(s) are outside core-v1 and were omitted.`
|
|
29304
|
+
});
|
|
29305
|
+
}
|
|
28577
29306
|
const nullRevisions = await rowCount(
|
|
28578
29307
|
db,
|
|
28579
29308
|
`SELECT COUNT(*) AS count
|
|
@@ -30717,13 +31446,13 @@ function noteSearchText(note) {
|
|
|
30717
31446
|
}
|
|
30718
31447
|
function countOccurrences(haystack, needle) {
|
|
30719
31448
|
if (!needle) return 0;
|
|
30720
|
-
let
|
|
31449
|
+
let count2 = 0;
|
|
30721
31450
|
let index = haystack.indexOf(needle);
|
|
30722
31451
|
while (index !== -1) {
|
|
30723
|
-
|
|
31452
|
+
count2 += 1;
|
|
30724
31453
|
index = haystack.indexOf(needle, index + needle.length);
|
|
30725
31454
|
}
|
|
30726
|
-
return
|
|
31455
|
+
return count2;
|
|
30727
31456
|
}
|
|
30728
31457
|
function noteMatchesTokens(note, tokens) {
|
|
30729
31458
|
if (tokens.length === 0) return true;
|
|
@@ -31201,7 +31930,7 @@ function hasPositiveInteger(value) {
|
|
|
31201
31930
|
}
|
|
31202
31931
|
function isFacetCounts(value) {
|
|
31203
31932
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
31204
|
-
return Object.values(value).every((counts) => !!counts && typeof counts === "object" && !Array.isArray(counts) && Object.values(counts).every((
|
|
31933
|
+
return Object.values(value).every((counts) => !!counts && typeof counts === "object" && !Array.isArray(counts) && Object.values(counts).every((count2) => hasNonNegativeInteger(count2)));
|
|
31205
31934
|
}
|
|
31206
31935
|
function isPlainRecord(value) {
|
|
31207
31936
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
@@ -34796,13 +35525,20 @@ var RECORD_COLLECTIONS = [
|
|
|
34796
35525
|
"collection_note",
|
|
34797
35526
|
"attachment",
|
|
34798
35527
|
"attachment_blob",
|
|
34799
|
-
"shard_manifest"
|
|
35528
|
+
"shard_manifest",
|
|
35529
|
+
"source_identity",
|
|
35530
|
+
"source_import_run",
|
|
35531
|
+
"deletion_receipt"
|
|
34800
35532
|
];
|
|
34801
35533
|
var RECORD_STORE_CAPABILITIES = {
|
|
34802
35534
|
crud: true,
|
|
34803
35535
|
journal: true,
|
|
34804
35536
|
atomicBatch: true,
|
|
34805
35537
|
boundedTextScan: true,
|
|
35538
|
+
sourceAddressedUpsert: true,
|
|
35539
|
+
deletionReceipts: true,
|
|
35540
|
+
typedMetadataPredicates: false,
|
|
35541
|
+
evidenceLocators: true,
|
|
34806
35542
|
fullTextSearch: false,
|
|
34807
35543
|
vectorSearch: false,
|
|
34808
35544
|
sqlJoins: false
|
|
@@ -35991,7 +36727,7 @@ async function buildRecordShardArchive(store, options, profile) {
|
|
|
35991
36727
|
(attachment) => exportedNoteIds.has(attachment.note_id)
|
|
35992
36728
|
);
|
|
35993
36729
|
const projectedAttachmentCount = browserNotes.reduce(
|
|
35994
|
-
(
|
|
36730
|
+
(count2, note) => count2 + (note.attachments?.length ?? 0),
|
|
35995
36731
|
0
|
|
35996
36732
|
);
|
|
35997
36733
|
const sourceNoteById = new Map(notes.map((note) => [note.id, note]));
|
|
@@ -36145,6 +36881,17 @@ async function buildRecordShardArchive(store, options, profile) {
|
|
|
36145
36881
|
message: `${collectionStateLosses} collection lifecycle state(s) are outside record-v1 and were omitted.`
|
|
36146
36882
|
});
|
|
36147
36883
|
}
|
|
36884
|
+
const sourceIdentities = await store.list("source_identity");
|
|
36885
|
+
if (sourceIdentities.length > 0) {
|
|
36886
|
+
losses.push({
|
|
36887
|
+
code: "source-identity-outside-profile",
|
|
36888
|
+
count: sourceIdentities.length,
|
|
36889
|
+
field_path: "source_identity",
|
|
36890
|
+
action: "omit",
|
|
36891
|
+
destination_capability: "record-v1 does not declare source-addressed identity mappings",
|
|
36892
|
+
message: `${sourceIdentities.length} source identity mapping(s) are outside record-v1 and were omitted.`
|
|
36893
|
+
});
|
|
36894
|
+
}
|
|
36148
36895
|
}
|
|
36149
36896
|
let manifest = isRecordV1 ? {
|
|
36150
36897
|
version: recordSchemaVersion2,
|
|
@@ -36427,9 +37174,9 @@ async function importShardToRecords(store, data, options) {
|
|
|
36427
37174
|
}
|
|
36428
37175
|
for (const component of manifest.components ?? []) {
|
|
36429
37176
|
if (UNSUPPORTED_COMPONENTS.includes(component)) {
|
|
36430
|
-
const
|
|
37177
|
+
const count2 = manifest.counts?.[component];
|
|
36431
37178
|
const key = component === "communities" ? "communities" : component;
|
|
36432
|
-
skipped[key] = (skipped[key] ?? 0) + (typeof
|
|
37179
|
+
skipped[key] = (skipped[key] ?? 0) + (typeof count2 === "number" ? count2 : 0);
|
|
36433
37180
|
warnings.push(
|
|
36434
37181
|
`Shard component '${component}' is not supported by the canonical record tier and was skipped. Import into a PGlite-backed store to preserve it.`
|
|
36435
37182
|
);
|
|
@@ -36829,9 +37576,302 @@ async function importShardToRecords(store, data, options) {
|
|
|
36829
37576
|
};
|
|
36830
37577
|
}
|
|
36831
37578
|
|
|
37579
|
+
// src/records/source-upsert.ts
|
|
37580
|
+
function now() {
|
|
37581
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
37582
|
+
}
|
|
37583
|
+
function contentDigest2(content) {
|
|
37584
|
+
return computeHash(new TextEncoder().encode(content));
|
|
37585
|
+
}
|
|
37586
|
+
function sourceHash2(source) {
|
|
37587
|
+
return computeHash(new TextEncoder().encode([
|
|
37588
|
+
source.tenant_id ?? "default",
|
|
37589
|
+
source.archive_id ?? "",
|
|
37590
|
+
source.namespace,
|
|
37591
|
+
source.external_id
|
|
37592
|
+
].join("\0")));
|
|
37593
|
+
}
|
|
37594
|
+
function countOutcomes2(outcomes) {
|
|
37595
|
+
return {
|
|
37596
|
+
inserted: outcomes.filter((outcome) => outcome.outcome === "inserted").length,
|
|
37597
|
+
unchanged: outcomes.filter((outcome) => outcome.outcome === "unchanged").length,
|
|
37598
|
+
versioned: outcomes.filter((outcome) => outcome.outcome === "versioned").length,
|
|
37599
|
+
replaced: outcomes.filter((outcome) => outcome.outcome === "replaced").length,
|
|
37600
|
+
conflict: outcomes.filter((outcome) => outcome.outcome === "conflict").length,
|
|
37601
|
+
rejected: outcomes.filter((outcome) => outcome.outcome === "rejected").length
|
|
37602
|
+
};
|
|
37603
|
+
}
|
|
37604
|
+
async function findSource(store, source) {
|
|
37605
|
+
const identities = await store.list("source_identity");
|
|
37606
|
+
return identities.find((identity) => identity.tenant_id === (source.tenant_id ?? "default") && identity.archive_id === (source.archive_id ?? null) && identity.namespace === source.namespace && identity.external_id === source.external_id) ?? null;
|
|
37607
|
+
}
|
|
37608
|
+
async function upsertRecordStoreSources(store, items, options = {}) {
|
|
37609
|
+
const maxItems = options.maxItems ?? 500;
|
|
37610
|
+
if (items.length > maxItems) throw new Error(`Source upsert batch exceeds the ${maxItems} item bound`);
|
|
37611
|
+
if (!store.applyBatch) throw new Error("RecordStore source upsert requires atomic applyBatch() support");
|
|
37612
|
+
if (items.length === 0) {
|
|
37613
|
+
return {
|
|
37614
|
+
import_run_id: "",
|
|
37615
|
+
dry_run: options.dryRun === true,
|
|
37616
|
+
outcomes: [],
|
|
37617
|
+
counts: { inserted: 0, unchanged: 0, versioned: 0, replaced: 0, conflict: 0, rejected: 0 }
|
|
37618
|
+
};
|
|
37619
|
+
}
|
|
37620
|
+
const outcomes = [];
|
|
37621
|
+
const mutations = [];
|
|
37622
|
+
const stamp = now();
|
|
37623
|
+
for (const [index, item] of items.entries()) {
|
|
37624
|
+
const external_id_hash = sourceHash2(item.source);
|
|
37625
|
+
const content_digest = contentDigest2(item.content);
|
|
37626
|
+
const existing = await findSource(store, item.source);
|
|
37627
|
+
if (!existing) {
|
|
37628
|
+
const noteId = item.source.caller_stable_id ?? generateId();
|
|
37629
|
+
outcomes.push({ index, outcome: "inserted", note_id: noteId, external_id_hash, content_digest });
|
|
37630
|
+
if (!options.dryRun) {
|
|
37631
|
+
const note2 = {
|
|
37632
|
+
id: noteId,
|
|
37633
|
+
archive_id: item.source.archive_id ?? null,
|
|
37634
|
+
title: item.title ?? null,
|
|
37635
|
+
format: item.format ?? "markdown",
|
|
37636
|
+
source: `source:${item.source.namespace}`,
|
|
37637
|
+
visibility: item.visibility ?? "private",
|
|
37638
|
+
revision_mode: "standard",
|
|
37639
|
+
is_starred: false,
|
|
37640
|
+
is_pinned: false,
|
|
37641
|
+
is_archived: false,
|
|
37642
|
+
created_at: stamp,
|
|
37643
|
+
updated_at: stamp,
|
|
37644
|
+
deleted_at: null
|
|
37645
|
+
};
|
|
37646
|
+
const original = {
|
|
37647
|
+
id: generateId(),
|
|
37648
|
+
note_id: noteId,
|
|
37649
|
+
content: item.content,
|
|
37650
|
+
content_hash: content_digest,
|
|
37651
|
+
created_at: stamp
|
|
37652
|
+
};
|
|
37653
|
+
const current2 = {
|
|
37654
|
+
id: noteId,
|
|
37655
|
+
content: item.content,
|
|
37656
|
+
ai_metadata: item.metadata ?? null,
|
|
37657
|
+
generation_count: 0,
|
|
37658
|
+
model: null,
|
|
37659
|
+
is_user_edited: false,
|
|
37660
|
+
updated_at: stamp
|
|
37661
|
+
};
|
|
37662
|
+
const identity = {
|
|
37663
|
+
id: generateId(),
|
|
37664
|
+
tenant_id: item.source.tenant_id ?? "default",
|
|
37665
|
+
archive_id: item.source.archive_id ?? null,
|
|
37666
|
+
namespace: item.source.namespace,
|
|
37667
|
+
external_id: item.source.external_id,
|
|
37668
|
+
external_id_hash,
|
|
37669
|
+
source_schema_version: item.source.source_schema_version,
|
|
37670
|
+
content_digest,
|
|
37671
|
+
import_run_id: item.source.import_run_id,
|
|
37672
|
+
caller_stable_id: item.source.caller_stable_id ?? null,
|
|
37673
|
+
note_id: noteId,
|
|
37674
|
+
created_at: stamp,
|
|
37675
|
+
updated_at: stamp
|
|
37676
|
+
};
|
|
37677
|
+
mutations.push(
|
|
37678
|
+
{ op: "put", collection: "note", record: note2 },
|
|
37679
|
+
{ op: "put", collection: "note_original", record: original },
|
|
37680
|
+
{ op: "put", collection: "note_revised_current", record: current2 },
|
|
37681
|
+
{ op: "put", collection: "source_identity", record: identity }
|
|
37682
|
+
);
|
|
37683
|
+
}
|
|
37684
|
+
continue;
|
|
37685
|
+
}
|
|
37686
|
+
if (existing.content_digest === content_digest) {
|
|
37687
|
+
outcomes.push({ index, outcome: "unchanged", note_id: existing.note_id, external_id_hash, content_digest });
|
|
37688
|
+
continue;
|
|
37689
|
+
}
|
|
37690
|
+
const policy = item.policy ?? "version";
|
|
37691
|
+
if (policy === "conflict") {
|
|
37692
|
+
outcomes.push({ index, outcome: "conflict", note_id: existing.note_id, external_id_hash, content_digest });
|
|
37693
|
+
continue;
|
|
37694
|
+
}
|
|
37695
|
+
const note = await store.get("note", existing.note_id);
|
|
37696
|
+
const current = await store.get("note_revised_current", existing.note_id);
|
|
37697
|
+
if (!note || !current) {
|
|
37698
|
+
outcomes.push({ index, outcome: "rejected", note_id: existing.note_id, external_id_hash, content_digest, reason: "source identity points to a missing note" });
|
|
37699
|
+
continue;
|
|
37700
|
+
}
|
|
37701
|
+
const outcome = policy === "replace" ? "replaced" : "versioned";
|
|
37702
|
+
outcomes.push({ index, outcome, note_id: existing.note_id, external_id_hash, content_digest });
|
|
37703
|
+
if (!options.dryRun) {
|
|
37704
|
+
mutations.push(
|
|
37705
|
+
{
|
|
37706
|
+
op: "put",
|
|
37707
|
+
collection: "note",
|
|
37708
|
+
record: {
|
|
37709
|
+
...note,
|
|
37710
|
+
title: item.title ?? null,
|
|
37711
|
+
archive_id: item.source.archive_id ?? null,
|
|
37712
|
+
format: item.format ?? "markdown",
|
|
37713
|
+
visibility: item.visibility ?? "private",
|
|
37714
|
+
deleted_at: null,
|
|
37715
|
+
updated_at: stamp
|
|
37716
|
+
}
|
|
37717
|
+
},
|
|
37718
|
+
{
|
|
37719
|
+
op: "put",
|
|
37720
|
+
collection: "note_revised_current",
|
|
37721
|
+
record: { ...current, content: item.content, ai_metadata: item.metadata ?? null, is_user_edited: false, updated_at: stamp }
|
|
37722
|
+
},
|
|
37723
|
+
{
|
|
37724
|
+
op: "put",
|
|
37725
|
+
collection: "source_identity",
|
|
37726
|
+
record: { ...existing, source_schema_version: item.source.source_schema_version, content_digest, import_run_id: item.source.import_run_id, updated_at: stamp }
|
|
37727
|
+
}
|
|
37728
|
+
);
|
|
37729
|
+
}
|
|
37730
|
+
}
|
|
37731
|
+
if (!options.dryRun && hasMaterialChange2(outcomes)) {
|
|
37732
|
+
const run = {
|
|
37733
|
+
id: items[0].source.import_run_id,
|
|
37734
|
+
tenant_id: items[0].source.tenant_id ?? "default",
|
|
37735
|
+
archive_id: items[0].source.archive_id ?? null,
|
|
37736
|
+
namespace: items[0].source.namespace,
|
|
37737
|
+
started_at: stamp,
|
|
37738
|
+
completed_at: stamp,
|
|
37739
|
+
checkpoint: { item_count: items.length },
|
|
37740
|
+
receipt: { counts: countOutcomes2(outcomes) }
|
|
37741
|
+
};
|
|
37742
|
+
mutations.push({ op: "put", collection: "source_import_run", record: run });
|
|
37743
|
+
await store.applyBatch(mutations);
|
|
37744
|
+
}
|
|
37745
|
+
return {
|
|
37746
|
+
import_run_id: items[0].source.import_run_id,
|
|
37747
|
+
dry_run: options.dryRun === true,
|
|
37748
|
+
outcomes,
|
|
37749
|
+
counts: countOutcomes2(outcomes)
|
|
37750
|
+
};
|
|
37751
|
+
}
|
|
37752
|
+
function hasMaterialChange2(outcomes) {
|
|
37753
|
+
return outcomes.some((outcome) => outcome.outcome === "inserted" || outcome.outcome === "versioned" || outcome.outcome === "replaced");
|
|
37754
|
+
}
|
|
37755
|
+
|
|
37756
|
+
// src/records/lifecycle-purge.ts
|
|
37757
|
+
function hashSelector(selector) {
|
|
37758
|
+
return computeHash(new TextEncoder().encode(JSON.stringify({
|
|
37759
|
+
tenant_id: selector.tenant_id ?? "default",
|
|
37760
|
+
archive_id: selector.archive_id ?? null,
|
|
37761
|
+
note_ids: [...selector.note_ids ?? []].sort(),
|
|
37762
|
+
source: selector.source ? {
|
|
37763
|
+
namespace: selector.source.namespace,
|
|
37764
|
+
external_id_hash: selector.source.external_id ? computeHash(new TextEncoder().encode(selector.source.external_id)) : null
|
|
37765
|
+
} : null
|
|
37766
|
+
})));
|
|
37767
|
+
}
|
|
37768
|
+
function zeroCounts2() {
|
|
37769
|
+
return {
|
|
37770
|
+
notes: 0,
|
|
37771
|
+
revisions: 0,
|
|
37772
|
+
links: 0,
|
|
37773
|
+
tags: 0,
|
|
37774
|
+
embeddings: 0,
|
|
37775
|
+
attachments: 0,
|
|
37776
|
+
blobs: 0,
|
|
37777
|
+
graph_edges: 0,
|
|
37778
|
+
provenance_edges: 0,
|
|
37779
|
+
source_identities: 0
|
|
37780
|
+
};
|
|
37781
|
+
}
|
|
37782
|
+
async function selectedNoteIds2(store, selector) {
|
|
37783
|
+
const notes = await store.list("note");
|
|
37784
|
+
if (selector.note_ids?.length) return notes.filter((note) => selector.note_ids.includes(note.id)).map((note) => note.id);
|
|
37785
|
+
if (!selector.source) throw new Error("Purge selector must target note_ids or source identity");
|
|
37786
|
+
const identities = await store.list("source_identity");
|
|
37787
|
+
return identities.filter((identity) => identity.tenant_id === (selector.tenant_id ?? "default") && identity.archive_id === (selector.archive_id ?? null) && identity.namespace === selector.source.namespace && (selector.source.external_id === void 0 || identity.external_id === selector.source.external_id)).map((identity) => identity.note_id);
|
|
37788
|
+
}
|
|
37789
|
+
async function count(store, noteIds) {
|
|
37790
|
+
const counts = zeroCounts2();
|
|
37791
|
+
if (noteIds.length === 0) return counts;
|
|
37792
|
+
const noteSet = new Set(noteIds);
|
|
37793
|
+
counts.notes = (await store.list("note")).filter((record) => noteSet.has(record.id)).length;
|
|
37794
|
+
counts.revisions = 0;
|
|
37795
|
+
counts.links = (await store.list("link")).filter((record) => noteSet.has(record.source_note_id) || noteSet.has(record.target_note_id)).length;
|
|
37796
|
+
counts.tags = (await store.list("note_tag")).filter((record) => noteSet.has(record.note_id)).length;
|
|
37797
|
+
counts.attachments = (await store.list("attachment")).filter((record) => noteSet.has(record.note_id)).length;
|
|
37798
|
+
const purgedBlobIds = new Set((await store.list("attachment")).filter((record) => noteSet.has(record.note_id)).map((record) => record.blob_id));
|
|
37799
|
+
counts.blobs = (await store.list("attachment_blob")).filter((record) => purgedBlobIds.has(record.id)).length;
|
|
37800
|
+
counts.source_identities = (await store.list("source_identity")).filter((record) => noteSet.has(record.note_id)).length;
|
|
37801
|
+
return counts;
|
|
37802
|
+
}
|
|
37803
|
+
async function previewRecordStorePurge(store, selector) {
|
|
37804
|
+
return { selector_hash: hashSelector(selector), counts: await count(store, await selectedNoteIds2(store, selector)) };
|
|
37805
|
+
}
|
|
37806
|
+
async function purgeRecordStoreGraph(store, selector, operationKey) {
|
|
37807
|
+
if (!store.applyBatch) throw new Error("RecordStore purge requires atomic applyBatch() support");
|
|
37808
|
+
const prior = (await store.list("deletion_receipt")).find((receipt2) => receipt2.operation_key === operationKey);
|
|
37809
|
+
if (prior) return {
|
|
37810
|
+
id: prior.id,
|
|
37811
|
+
operation_key: prior.operation_key,
|
|
37812
|
+
tenant_id: prior.tenant_id,
|
|
37813
|
+
archive_id: prior.archive_id,
|
|
37814
|
+
selector_hash: prior.selector_hash,
|
|
37815
|
+
outcome: "completed",
|
|
37816
|
+
counts: prior.counts,
|
|
37817
|
+
completed_at: prior.completed_at,
|
|
37818
|
+
policy: prior.policy
|
|
37819
|
+
};
|
|
37820
|
+
const noteIds = await selectedNoteIds2(store, selector);
|
|
37821
|
+
const noteSet = new Set(noteIds);
|
|
37822
|
+
const counts = await count(store, noteIds);
|
|
37823
|
+
const mutations = [];
|
|
37824
|
+
for (const collection of ["note_revised_current", "note_original", "note_tag", "collection_note", "attachment", "source_identity"]) {
|
|
37825
|
+
for (const record of await store.list(collection)) {
|
|
37826
|
+
const noteId = collection === "note_revised_current" ? record.id : "note_id" in record && typeof record.note_id === "string" ? record.note_id : null;
|
|
37827
|
+
if (noteId && noteSet.has(noteId)) {
|
|
37828
|
+
mutations.push({ op: "delete", collection, id: record.id });
|
|
37829
|
+
}
|
|
37830
|
+
}
|
|
37831
|
+
}
|
|
37832
|
+
for (const record of await store.list("link")) {
|
|
37833
|
+
if (noteSet.has(record.source_note_id) || noteSet.has(record.target_note_id)) mutations.push({ op: "delete", collection: "link", id: record.id });
|
|
37834
|
+
}
|
|
37835
|
+
for (const record of await store.list("note")) {
|
|
37836
|
+
if (noteSet.has(record.id)) mutations.push({ op: "delete", collection: "note", id: record.id });
|
|
37837
|
+
}
|
|
37838
|
+
const liveBlobIds = new Set((await store.list("attachment")).filter((record) => !noteSet.has(record.note_id)).map((record) => record.blob_id));
|
|
37839
|
+
for (const record of await store.list("attachment_blob")) {
|
|
37840
|
+
if (!liveBlobIds.has(record.id)) mutations.push({ op: "delete", collection: "attachment_blob", id: record.id });
|
|
37841
|
+
}
|
|
37842
|
+
const receipt = {
|
|
37843
|
+
id: generateId(),
|
|
37844
|
+
operation_key: operationKey,
|
|
37845
|
+
tenant_id: selector.tenant_id ?? "default",
|
|
37846
|
+
archive_id: selector.archive_id ?? null,
|
|
37847
|
+
selector_hash: hashSelector(selector),
|
|
37848
|
+
outcome: "completed",
|
|
37849
|
+
counts,
|
|
37850
|
+
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37851
|
+
policy: {
|
|
37852
|
+
authority: "fortemi#1092",
|
|
37853
|
+
mode: "terminal-purge",
|
|
37854
|
+
receipt_contains_content: false
|
|
37855
|
+
}
|
|
37856
|
+
};
|
|
37857
|
+
mutations.push({ op: "put", collection: "deletion_receipt", record: receipt });
|
|
37858
|
+
await store.applyBatch(mutations);
|
|
37859
|
+
return {
|
|
37860
|
+
id: receipt.id,
|
|
37861
|
+
operation_key: receipt.operation_key,
|
|
37862
|
+
tenant_id: receipt.tenant_id,
|
|
37863
|
+
archive_id: receipt.archive_id,
|
|
37864
|
+
selector_hash: receipt.selector_hash,
|
|
37865
|
+
outcome: "completed",
|
|
37866
|
+
counts,
|
|
37867
|
+
completed_at: receipt.completed_at,
|
|
37868
|
+
policy: receipt.policy
|
|
37869
|
+
};
|
|
37870
|
+
}
|
|
37871
|
+
|
|
36832
37872
|
// src/index.ts
|
|
36833
|
-
var VERSION = "2026.
|
|
37873
|
+
var VERSION = "2026.8.0";
|
|
36834
37874
|
|
|
36835
|
-
export { AIWG_SCAN_REQUIRED_FIELDS, AllowlistTrustStore, ArchiveManager, AttachmentsRepository, CORE_V1_COMPONENTS, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CanonicalAttachmentsRepository, CanonicalNotesRepository, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DB_SNAPSHOT_SCHEMA_VERSION, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FORTEMI_COMPATIBILITY_PATH, FORTEMI_COMPATIBILITY_STATES, FORTEMI_REQUIRED_COMPATIBILITY_CAPABILITIES, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, IdbRecordStore, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LOCAL_ENDPOINTS, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MemoryRecordStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, RECORD_COLLECTIONS, RECORD_SCHEMA_VERSION, RECORD_STORE_CAPABILITIES, SHARD_FORMAT, SIGNATURE_ENTRY, SIGNING_ENVELOPE_VERSION, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexFromKnowledgeShard, aiwgFortemiIndexToCommunityGraph, aiwgFortemiIndexToKnowledgeShard, aiwgFortemiIndexToKnowledgeShardWithReport, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, assertShardComponentRecord, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, computeBlobHash, computeHash, computeSri, conceptTaggingHandler, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLazyBlobStore, createLegacyProvider, createPGliteBackend, createPGliteInstance, createRecordBackend, createRecordStore, createRemoteBackend, createRoutes, createShardBackend, createShardCapabilityReport, createWorkerEmbedFunction, defaultStorageBackendFactory, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, discoverLocalProviders, dropAttachmentProjection, dropNoteProjection, dumpDbSnapshot, embeddingConfigToShard, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, exportShard, exportShardFromRecords, exportShardFromRecordsWithReport, exportShardWithReport, fetchAndValidateFortemiCompatibility, fetchPluginScript, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, formatFortemiCompatibilitySummary, fortemiCompatibilityUrl, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getAiwgFortemiIndexExportSchema, getEmbedFunction, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getKnowledgeShardContractReceipt, getKnowledgeShardProfileRegistry, getKnowledgeShardSchema, getLlmFunction, getNote, getPrefetchedSha256, handleEmbedRequests, hasFortemiSecureSecrets, importShard, importShardToRecords, isPluginScriptAllowed, isShardPrefetched, isShardSigningSupported, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, migrateLegacyBlobStore, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, prefetchShard, profileSupportError, projectAttachments, projectNotes, projectRecords, provenanceEdgeToShard, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, restoreDbSnapshot, searchTool, selectBackend, selectLlmModel, setEmbedFunction, setLlmFunction, sha256Hex, sidecarBlobDigests, signShard, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsToShard, templateToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, urlLinkToShard, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgFortemiIndexExportSchema, validateAiwgFortemiProjectedRecordSchema, validateAiwgStaticEmbeddingSet, validateChecksums, validateCoreV1ShardArchive, validateFortemiCompatibilityResponse, validateFullV1ShardArchive, validateRecordV1ShardArchive, validateShardArchive, validateShardComponentRecord, validateShardManifest, verifyDbSnapshotMeta, verifyShardSignature, verifySri };
|
|
37875
|
+
export { AIWG_SCAN_REQUIRED_FIELDS, AllowlistTrustStore, ArchiveManager, AttachmentsRepository, CORE_V1_COMPONENTS, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CanonicalAttachmentsRepository, CanonicalNotesRepository, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DB_SNAPSHOT_SCHEMA_VERSION, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FORTEMI_COMPATIBILITY_PATH, FORTEMI_COMPATIBILITY_STATES, FORTEMI_REQUIRED_COMPATIBILITY_CAPABILITIES, FORTEMI_SERVER_COMPATIBILITY_REVISION, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, IdbRecordStore, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LOCAL_ENDPOINTS, LifecyclePurgeRepository, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MemoryRecordStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, RECORD_COLLECTIONS, RECORD_SCHEMA_VERSION, RECORD_STORE_CAPABILITIES, REGISTERED_METADATA_PATHS, SHARD_FORMAT, SIGNATURE_ENTRY, SIGNING_ENVELOPE_VERSION, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, SourceUpsertRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexFromKnowledgeShard, aiwgFortemiIndexToCommunityGraph, aiwgFortemiIndexToKnowledgeShard, aiwgFortemiIndexToKnowledgeShardWithReport, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, assertShardComponentRecord, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, buildMetadataPredicateConditions, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, computeBlobHash, computeHash, computeSri, conceptTaggingHandler, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLazyBlobStore, createLegacyProvider, createPGliteBackend, createPGliteInstance, createRecordBackend, createRecordStore, createRemoteBackend, createRoutes, createShardBackend, createShardCapabilityReport, createWorkerEmbedFunction, defaultStorageBackendFactory, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, discoverLocalProviders, dropAttachmentProjection, dropNoteProjection, dumpDbSnapshot, embeddingConfigToShard, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, exportShard, exportShardFromRecords, exportShardFromRecordsWithReport, exportShardWithReport, fetchAndValidateFortemiCompatibility, fetchPluginScript, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, formatFortemiCompatibilitySummary, fortemiCompatibilityUrl, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getAiwgFortemiIndexExportSchema, getEmbedFunction, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getKnowledgeShardContractReceipt, getKnowledgeShardProfileRegistry, getKnowledgeShardSchema, getLlmFunction, getNote, getPrefetchedSha256, handleEmbedRequests, hasFortemiSecureSecrets, importShard, importShardToRecords, isPluginScriptAllowed, isShardPrefetched, isShardSigningSupported, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, migrateLegacyBlobStore, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, prefetchShard, previewRecordStorePurge, profileSupportError, projectAttachments, projectNotes, projectRecords, provenanceEdgeToShard, purgeRecordStoreGraph, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, restoreDbSnapshot, searchTool, selectBackend, selectLlmModel, setEmbedFunction, setLlmFunction, sha256Hex, sidecarBlobDigests, signShard, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsToShard, templateToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, upsertRecordStoreSources, urlLinkToShard, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgFortemiIndexExportSchema, validateAiwgFortemiProjectedRecordSchema, validateAiwgStaticEmbeddingSet, validateChecksums, validateCoreV1ShardArchive, validateFortemiCompatibilityResponse, validateFullV1ShardArchive, validateRecordV1ShardArchive, validateShardArchive, validateShardComponentRecord, validateShardManifest, verifyDbSnapshotMeta, verifyShardSignature, verifySri };
|
|
36836
37876
|
//# sourceMappingURL=index.js.map
|
|
36837
37877
|
//# sourceMappingURL=index.js.map
|