@fortemi/core 2026.7.3 → 2026.7.5

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.
@@ -1,1464 +1,16 @@
1
- import { sha256 } from '@noble/hashes/sha256';
2
- import { bytesToHex } from '@noble/hashes/utils';
3
-
4
- // src/hash.ts
5
- function computeHash(data) {
6
- const digest = sha256(data);
7
- return `sha256:${bytesToHex(digest)}`;
8
- }
9
-
10
- // src/aiwg-index.ts
11
- var AIWG_SCAN_REQUIRED_FIELDS = [
12
- "schema_version",
13
- "id",
14
- "type",
15
- "title",
16
- "text",
17
- "facets",
18
- "tags",
19
- "concepts",
20
- "privacy"
21
- ];
22
- function isPrivacyExcluded(record, options) {
23
- const privacy = record.privacy;
24
- if (!privacy) return false;
25
- if (privacy.classification === "private" && !options?.includePrivate) return true;
26
- if (privacy.pii && !options?.includePii) return true;
27
- return false;
28
- }
29
- function filterAiwgRecordsByPrivacy(records, options) {
30
- return records.filter((record) => !isPrivacyExcluded(record, options));
31
- }
32
- var REQUIRED_RECORD_FIELDS = [
33
- "schema_version",
34
- "id",
35
- "type",
36
- "source",
37
- "facets",
38
- "tags",
39
- "concepts",
40
- "relationships",
41
- "provenance",
42
- "privacy",
43
- "updated_at"
44
- ];
45
- var DEFAULT_QUERY_WEIGHTS = {
46
- title: 4,
47
- tag: 3,
48
- concept: 2,
49
- text: 1,
50
- facet: 2,
51
- id: 1,
52
- source: 0.25
53
- };
54
- function hasString(value) {
55
- return typeof value === "string" && value.length > 0;
56
- }
57
- function pushFacet(counts, name, value) {
58
- let bucket = counts[name];
59
- if (bucket === void 0) {
60
- bucket = /* @__PURE__ */ Object.create(null);
61
- counts[name] = bucket;
62
- }
63
- bucket[value] = (bucket[value] ?? 0) + 1;
64
- }
65
- function hasNonNegativeInteger(value) {
66
- return Number.isInteger(value) && typeof value === "number" && value >= 0;
67
- }
68
- function hasPositiveInteger(value) {
69
- return Number.isInteger(value) && typeof value === "number" && value > 0;
70
- }
71
- function isFacetCounts(value) {
72
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
73
- return Object.values(value).every((counts) => !!counts && typeof counts === "object" && !Array.isArray(counts) && Object.values(counts).every((count) => hasNonNegativeInteger(count)));
74
- }
75
- function isPlainRecord(value) {
76
- return !!value && typeof value === "object" && !Array.isArray(value);
77
- }
78
- function isOptionalStringArray(value) {
79
- return value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
80
- }
81
- function isSupportedIndexSchemaVersion(value) {
82
- return value === "aiwg.fortemi.index.export.v1" || value === "aiwg.fortemi.index.export.v2";
83
- }
84
- function isSupportedRecordSchemaVersion(value) {
85
- return value === "aiwg.fortemi.index.record.v1" || value === "aiwg.fortemi.index.record.v2";
86
- }
87
- function validateOptionalRichMetadata(item, index, errors) {
88
- if (item.skos_concepts !== void 0) {
89
- if (!Array.isArray(item.skos_concepts)) {
90
- errors.push("items[" + index + "].skos_concepts must be an array when present");
91
- } else {
92
- for (const [conceptIndex, concept] of item.skos_concepts.entries()) {
93
- if (!hasString(concept.id)) errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "].id is required");
94
- if (!hasString(concept.prefLabel)) errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "].prefLabel is required");
95
- if (!isOptionalStringArray(concept.altLabels)) errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "].altLabels must be a string array");
96
- if (concept.metadata !== void 0 && !isPlainRecord(concept.metadata)) {
97
- errors.push("items[" + index + "].skos_concepts[" + conceptIndex + "].metadata must be an object");
98
- }
99
- }
100
- }
101
- }
102
- if (item.skos_relations !== void 0) {
103
- if (!Array.isArray(item.skos_relations)) {
104
- errors.push("items[" + index + "].skos_relations must be an array when present");
105
- } else {
106
- for (const [relationIndex, relation] of item.skos_relations.entries()) {
107
- if (!hasString(relation.type)) errors.push("items[" + index + "].skos_relations[" + relationIndex + "].type is required");
108
- if (!hasString(relation.source_id)) errors.push("items[" + index + "].skos_relations[" + relationIndex + "].source_id is required");
109
- if (!hasString(relation.target_id)) errors.push("items[" + index + "].skos_relations[" + relationIndex + "].target_id is required");
110
- if (relation.metadata !== void 0 && !isPlainRecord(relation.metadata)) {
111
- errors.push("items[" + index + "].skos_relations[" + relationIndex + "].metadata must be an object");
112
- }
113
- }
114
- }
115
- }
116
- if (item.provenance_events !== void 0) {
117
- if (!Array.isArray(item.provenance_events)) {
118
- errors.push("items[" + index + "].provenance_events must be an array when present");
119
- } else {
120
- for (const [eventIndex, event] of item.provenance_events.entries()) {
121
- if (!hasString(event.activity)) errors.push("items[" + index + "].provenance_events[" + eventIndex + "].activity is required");
122
- if (event.attributes !== void 0 && !isPlainRecord(event.attributes)) {
123
- errors.push("items[" + index + "].provenance_events[" + eventIndex + "].attributes must be an object");
124
- }
125
- }
126
- }
127
- }
128
- if (Array.isArray(item.relationships)) {
129
- for (const [relationshipIndex, relationship] of item.relationships.entries()) {
130
- if (relationship.metadata !== void 0 && !isPlainRecord(relationship.metadata)) {
131
- errors.push("items[" + index + "].relationships[" + relationshipIndex + "].metadata must be an object");
132
- }
133
- if (relationship.direction !== void 0 && relationship.direction !== "upstream" && relationship.direction !== "downstream" && relationship.direction !== "related") {
134
- errors.push("items[" + index + "].relationships[" + relationshipIndex + "].direction must be upstream, downstream, or related");
135
- }
136
- if (relationship.target_path !== void 0 && typeof relationship.target_path !== "string") {
137
- errors.push("items[" + index + "].relationships[" + relationshipIndex + "].target_path must be a string");
138
- }
139
- }
140
- }
141
- if (item.search !== void 0) {
142
- if (!isPlainRecord(item.search)) {
143
- errors.push("items[" + index + "].search must be an object");
144
- } else {
145
- if (!isOptionalStringArray(item.search.triggers)) errors.push("items[" + index + "].search.triggers must be a string array");
146
- if (!isOptionalStringArray(item.search.aliases)) errors.push("items[" + index + "].search.aliases must be a string array");
147
- if (!isOptionalStringArray(item.search.tags)) errors.push("items[" + index + "].search.tags must be a string array");
148
- if (item.search.frontmatter !== void 0 && !isPlainRecord(item.search.frontmatter)) {
149
- errors.push("items[" + index + "].search.frontmatter must be an object");
150
- }
151
- }
152
- }
153
- if (item.chunks !== void 0) {
154
- if (!Array.isArray(item.chunks)) {
155
- errors.push("items[" + index + "].chunks must be an array when present");
156
- } else {
157
- for (const [chunkIndex, chunk] of item.chunks.entries()) {
158
- if (!isPlainRecord(chunk)) errors.push("items[" + index + "].chunks[" + chunkIndex + "] must be an object");
159
- if (chunk.metadata !== void 0 && !isPlainRecord(chunk.metadata)) {
160
- errors.push("items[" + index + "].chunks[" + chunkIndex + "].metadata must be an object");
161
- }
162
- }
163
- }
164
- }
165
- if (item.embeddings !== void 0) {
166
- if (!Array.isArray(item.embeddings)) {
167
- errors.push("items[" + index + "].embeddings must be an array when present");
168
- } else {
169
- for (const [embeddingIndex, embedding] of item.embeddings.entries()) {
170
- if (!isPlainRecord(embedding)) errors.push("items[" + index + "].embeddings[" + embeddingIndex + "] must be an object");
171
- const vector = embedding.embedding ?? embedding.vector;
172
- if (vector !== void 0 && (!Array.isArray(vector) || !vector.every((entry) => typeof entry === "number"))) {
173
- errors.push("items[" + index + "].embeddings[" + embeddingIndex + "].embedding/vector must be a number array");
174
- }
175
- if (embedding.metadata !== void 0 && !isPlainRecord(embedding.metadata)) {
176
- errors.push("items[" + index + "].embeddings[" + embeddingIndex + "].metadata must be an object");
177
- }
178
- }
179
- }
180
- }
181
- if (item.compatibility !== void 0 && !isPlainRecord(item.compatibility)) {
182
- errors.push("items[" + index + "].compatibility must be an object");
183
- }
184
- }
185
- function isPrivacyClassification(value) {
186
- return value === "private" || value === "sanitized" || value === "public";
187
- }
188
- function isProvenanceConfidence(value) {
189
- return value === "source" || value === "candidate" || value === "reviewed" || value === "rejected";
190
- }
191
- function validateProvenanceItems(item, index, errors) {
192
- if (!Array.isArray(item.provenance)) return;
193
- for (const [provIndex, prov] of item.provenance.entries()) {
194
- const at = "items[" + index + "].provenance[" + provIndex + "]";
195
- if (!isPlainRecord(prov)) {
196
- errors.push(at + " must be an object");
197
- continue;
198
- }
199
- if (!hasString(prov.field)) errors.push(at + ".field is required");
200
- if (!hasString(prov.source)) errors.push(at + ".source is required");
201
- if (!hasString(prov.path)) errors.push(at + ".path is required");
202
- if (!isProvenanceConfidence(prov.confidence)) errors.push(at + ".confidence must be one of source, candidate, reviewed, rejected");
203
- if (!isPrivacyClassification(prov.privacy)) errors.push(at + ".privacy must be one of private, sanitized, public");
204
- }
205
- }
206
- var V2_ONLY_RECORD_FIELDS = ["search", "chunks", "embeddings", "skos_concepts", "skos_relations", "compatibility"];
207
- var V2_ONLY_SOURCE_FIELDS = ["origin", "generated", "checksum", "updated_at"];
208
- var V2_ONLY_RELATIONSHIP_FIELDS = ["target_path", "direction", "metadata"];
209
- function forbidV2FieldsOnV1Record(item, index, errors) {
210
- if (item.schema_version !== "aiwg.fortemi.index.record.v1") return;
211
- const at = "items[" + index + "]";
212
- const bag = item;
213
- const v2msg = " is a v2-only field and must be absent on a record.v1 record";
214
- for (const field of V2_ONLY_RECORD_FIELDS) {
215
- if (bag[field] !== void 0) errors.push(at + "." + field + v2msg);
216
- }
217
- if (isPlainRecord(item.source)) {
218
- const src = item.source;
219
- for (const field of V2_ONLY_SOURCE_FIELDS) {
220
- if (src[field] !== void 0) errors.push(at + ".source." + field + v2msg);
221
- }
222
- }
223
- if (isPlainRecord(item.privacy) && item.privacy.locality !== void 0) {
224
- errors.push(at + ".privacy.locality" + v2msg);
225
- }
226
- if (Array.isArray(item.relationships)) {
227
- for (const [relIndex, rel] of item.relationships.entries()) {
228
- if (!isPlainRecord(rel)) continue;
229
- const relBag = rel;
230
- for (const field of V2_ONLY_RELATIONSHIP_FIELDS) {
231
- if (relBag[field] !== void 0) errors.push(at + ".relationships[" + relIndex + "]." + field + v2msg);
232
- }
233
- }
234
- }
235
- }
236
- function validateAiwgFortemiIndexExport(value) {
237
- const errors = [];
238
- const counts = {};
239
- const data = value;
240
- if (!isSupportedIndexSchemaVersion(data?.schema_version)) {
241
- errors.push("schema_version must be aiwg.fortemi.index.export.v1 or aiwg.fortemi.index.export.v2");
242
- }
243
- if (!hasString(data?.generated_at)) errors.push("generated_at is required");
244
- if (!hasString(data?.source?.repo)) errors.push("source.repo is required");
245
- if (!hasString(data?.source?.privacy)) errors.push("source.privacy is required");
246
- else if (!isPrivacyClassification(data?.source?.privacy)) {
247
- errors.push("source.privacy must be one of private, sanitized, public");
248
- }
249
- if (!Array.isArray(data?.items)) errors.push("items must be an array");
250
- if (data.compatibility !== void 0 && !isPlainRecord(data.compatibility)) {
251
- errors.push("compatibility must be an object");
252
- }
253
- if (data?.schema_version === "aiwg.fortemi.index.export.v1") {
254
- if (isPlainRecord(data.source) && data.source.graph !== void 0) {
255
- errors.push("source.graph is a v2-only field and must be absent on an export.v1 export");
256
- }
257
- if (data.compatibility !== void 0) {
258
- errors.push("compatibility is a v2-only field and must be absent on an export.v1 export");
259
- }
260
- }
261
- const ids = /* @__PURE__ */ new Set();
262
- let previousId = "";
263
- for (const [index, item] of (data.items ?? []).entries()) {
264
- for (const field of REQUIRED_RECORD_FIELDS) {
265
- if (!(field in item)) errors.push("items[" + index + "]." + field + " is required");
266
- }
267
- if (!isSupportedRecordSchemaVersion(item.schema_version)) {
268
- errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1 or aiwg.fortemi.index.record.v2");
269
- }
270
- if (!hasString(item.id)) errors.push("items[" + index + "].id is required");
271
- if (hasString(item.id) && ids.has(item.id)) errors.push("duplicate id: " + item.id);
272
- if (hasString(item.id)) ids.add(item.id);
273
- if (previousId && hasString(item.id) && previousId.localeCompare(item.id) > 0) {
274
- errors.push("items must be sorted by id: " + previousId + " before " + item.id);
275
- }
276
- if (hasString(item.id)) previousId = item.id;
277
- if (!hasString(item.type)) errors.push("items[" + index + "].type must be a non-empty string");
278
- else counts[item.type] = (counts[item.type] ?? 0) + 1;
279
- if (!hasString(item.source?.path)) errors.push("items[" + index + "].source.path is required");
280
- if (!hasString(item.source?.repo_relative_path)) errors.push("items[" + index + "].source.repo_relative_path is required");
281
- if (!hasString(item.source?.locator)) errors.push("items[" + index + "].source.locator is required");
282
- if (typeof item.title !== "string" && typeof item.search?.title !== "string" && typeof item.search?.name !== "string") {
283
- errors.push("items[" + index + "].title or search.title/search.name is required");
284
- }
285
- if (typeof item.text !== "string" && typeof item.search?.body !== "string" && typeof item.search?.summary !== "string") {
286
- errors.push("items[" + index + "].text or search.body/search.summary is required");
287
- }
288
- if (!Array.isArray(item.tags)) errors.push("items[" + index + "].tags must be an array");
289
- if (!Array.isArray(item.concepts)) errors.push("items[" + index + "].concepts must be an array");
290
- if (!Array.isArray(item.relationships)) errors.push("items[" + index + "].relationships must be an array");
291
- if (!Array.isArray(item.provenance) || item.provenance.length === 0) {
292
- errors.push("items[" + index + "].provenance must be a non-empty array");
293
- }
294
- validateOptionalRichMetadata(item, index, errors);
295
- validateProvenanceItems(item, index, errors);
296
- forbidV2FieldsOnV1Record(item, index, errors);
297
- if (!item.privacy || typeof item.privacy.pii !== "boolean" || !hasString(item.privacy.classification)) {
298
- errors.push("items[" + index + "].privacy requires classification and pii");
299
- } else if (!isPrivacyClassification(item.privacy.classification)) {
300
- errors.push("items[" + index + "].privacy.classification must be one of private, sanitized, public");
301
- }
302
- }
303
- return { valid: errors.length === 0, errors, counts };
304
- }
305
- function assertAiwgFortemiIndexExport(value) {
306
- const result = validateAiwgFortemiIndexExport(value);
307
- if (!result.valid) {
308
- throw new Error("Invalid AIWG Fortemi index export:\n" + result.errors.join("\n"));
309
- }
310
- return value;
311
- }
312
- function validateAiwgFortemiChunkManifest(value) {
313
- const errors = [];
314
- const data = value;
315
- if (data?.schema_version !== "aiwg.fortemi.index.chunk-manifest.v1") {
316
- errors.push("schema_version must be aiwg.fortemi.index.chunk-manifest.v1");
317
- }
318
- if (!hasString(data?.generated_at)) errors.push("generated_at is required");
319
- if (!hasString(data?.source?.repo)) errors.push("source.repo is required");
320
- if (!hasString(data?.source?.privacy)) errors.push("source.privacy is required");
321
- if (data?.source_export_schema_version !== void 0 && !isSupportedIndexSchemaVersion(data.source_export_schema_version)) {
322
- errors.push("source_export_schema_version must be aiwg.fortemi.index.export.v1 or aiwg.fortemi.index.export.v2 when present");
323
- }
324
- if (!hasNonNegativeInteger(data?.total)) errors.push("total must be a non-negative integer");
325
- if (!hasPositiveInteger(data?.part_size)) errors.push("part_size must be a positive integer");
326
- if (data.facets !== void 0 && !isFacetCounts(data.facets)) {
327
- errors.push("facets must be a nested string-to-number count object");
328
- }
329
- if (data.projection !== void 0) {
330
- if (!Array.isArray(data.projection) || !data.projection.every((field) => typeof field === "string")) {
331
- errors.push("projection must be an array of field names");
332
- } else {
333
- const present = new Set(data.projection);
334
- for (const field of AIWG_SCAN_REQUIRED_FIELDS) {
335
- if (!present.has(field)) errors.push("projection must include scan-required field " + field);
336
- }
337
- }
338
- }
339
- if (data.detail !== void 0) {
340
- if (!hasString(data.detail.href)) errors.push("detail.href is required");
341
- else if (!data.detail.href.includes("{id}")) errors.push("detail.href must contain the {id} placeholder");
342
- if (data.detail.encoding !== void 0 && data.detail.encoding !== "uri" && data.detail.encoding !== "base64url") {
343
- errors.push("detail.encoding must be 'uri' or 'base64url'");
344
- }
345
- }
346
- if (!Array.isArray(data?.parts)) errors.push("parts must be an array");
347
- let expectedOffset = 0;
348
- const parts = Array.isArray(data?.parts) ? data.parts : [];
349
- for (const [index, part] of parts.entries()) {
350
- if (!hasString(part.href)) errors.push("parts[" + index + "].href is required");
351
- if (!hasNonNegativeInteger(part.offset)) errors.push("parts[" + index + "].offset must be a non-negative integer");
352
- if (!hasNonNegativeInteger(part.count)) errors.push("parts[" + index + "].count must be a non-negative integer");
353
- if (hasNonNegativeInteger(part.offset) && part.offset !== expectedOffset) {
354
- errors.push("parts[" + index + "].offset must be " + expectedOffset);
355
- }
356
- if (hasNonNegativeInteger(part.count)) expectedOffset += part.count;
357
- }
358
- if (hasNonNegativeInteger(data?.total) && expectedOffset !== data.total) {
359
- errors.push("parts counts must add up to total");
360
- }
361
- return { valid: errors.length === 0, errors };
362
- }
363
- function assertAiwgFortemiChunkManifest(value) {
364
- const result = validateAiwgFortemiChunkManifest(value);
365
- if (!result.valid) {
366
- throw new Error("Invalid AIWG Fortemi chunk manifest:\n" + result.errors.join("\n"));
367
- }
368
- return value;
369
- }
370
- function validateProjectedRecords(items) {
371
- const errors = [];
372
- const ids = /* @__PURE__ */ new Set();
373
- let previousId = "";
374
- for (const [index, item] of items.entries()) {
375
- if (!isSupportedRecordSchemaVersion(item.schema_version)) {
376
- errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1 or aiwg.fortemi.index.record.v2");
377
- }
378
- if (!hasString(item.id)) errors.push("items[" + index + "].id is required");
379
- if (hasString(item.id) && ids.has(item.id)) errors.push("duplicate id: " + item.id);
380
- if (hasString(item.id)) ids.add(item.id);
381
- if (previousId && hasString(item.id) && previousId.localeCompare(item.id) > 0) {
382
- errors.push("items must be sorted by id: " + previousId + " before " + item.id);
383
- }
384
- if (hasString(item.id)) previousId = item.id;
385
- if (!hasString(item.type)) errors.push("items[" + index + "].type must be a non-empty string");
386
- if (typeof item.title !== "string" && typeof item.search?.title !== "string" && typeof item.search?.name !== "string") {
387
- errors.push("items[" + index + "].title or search.title/search.name is required");
388
- }
389
- if (typeof item.text !== "string" && typeof item.search?.body !== "string" && typeof item.search?.summary !== "string") {
390
- errors.push("items[" + index + "].text or search.body/search.summary is required");
391
- }
392
- if (!item.facets || typeof item.facets !== "object" || Array.isArray(item.facets)) {
393
- errors.push("items[" + index + "].facets must be an object");
394
- }
395
- if (!Array.isArray(item.tags)) errors.push("items[" + index + "].tags must be an array");
396
- if (!Array.isArray(item.concepts)) errors.push("items[" + index + "].concepts must be an array");
397
- if (!item.privacy || !hasString(item.privacy.classification)) {
398
- errors.push("items[" + index + "].privacy.classification is required");
399
- }
400
- }
401
- return errors;
402
- }
403
- function validateAiwgFortemiChunkPart(value, partRef, manifest) {
404
- const errors = [];
405
- const data = value;
406
- if (data?.schema_version !== "aiwg.fortemi.index.chunk.v1") {
407
- errors.push("schema_version must be aiwg.fortemi.index.chunk.v1");
408
- }
409
- if (data?.manifest_schema_version !== "aiwg.fortemi.index.chunk-manifest.v1") {
410
- errors.push("manifest_schema_version must be aiwg.fortemi.index.chunk-manifest.v1");
411
- }
412
- if (!hasNonNegativeInteger(data?.offset)) errors.push("offset must be a non-negative integer");
413
- if (!Array.isArray(data?.items)) errors.push("items must be an array");
414
- if (partRef && hasNonNegativeInteger(data?.offset) && data.offset !== partRef.offset) {
415
- errors.push("offset must match manifest part offset " + partRef.offset);
416
- }
417
- if (partRef && Array.isArray(data?.items) && data.items.length !== partRef.count) {
418
- errors.push("items length must match manifest part count " + partRef.count);
419
- }
420
- if (Array.isArray(data?.items)) {
421
- if (manifest?.projection) {
422
- errors.push(...validateProjectedRecords(data.items).map((error) => "items." + error));
423
- } else {
424
- const validation = validateAiwgFortemiIndexExport({
425
- schema_version: "aiwg.fortemi.index.export.v1",
426
- generated_at: manifest?.generated_at ?? "1970-01-01T00:00:00.000Z",
427
- source: manifest?.source ?? { repo: "chunk", privacy: "public" },
428
- items: data.items
429
- });
430
- errors.push(...validation.errors.map((error) => "items." + error));
431
- }
432
- }
433
- return { valid: errors.length === 0, errors };
434
- }
435
- function assertAiwgFortemiChunkPart(value, partRef, manifest) {
436
- const result = validateAiwgFortemiChunkPart(value, partRef, manifest);
437
- if (!result.valid) {
438
- throw new Error("Invalid AIWG Fortemi chunk part:\n" + result.errors.join("\n"));
439
- }
440
- return value;
441
- }
442
- var ALLOWED_AIWG_FETCH_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "blob:", "data:"]);
443
- function tryParseUrl(value) {
444
- try {
445
- return new URL(value);
446
- } catch {
447
- return null;
448
- }
449
- }
450
- function resolveAiwgFetchUrl(href, baseUrl) {
451
- if (baseUrl === void 0) {
452
- const absolute = tryParseUrl(href);
453
- if (absolute && !ALLOWED_AIWG_FETCH_SCHEMES.has(absolute.protocol)) {
454
- throw new Error("Refusing AIWG index fetch with disallowed scheme: " + absolute.protocol);
455
- }
456
- return href;
457
- }
458
- const base = new URL(baseUrl);
459
- const resolved = new URL(href, base);
460
- if (!ALLOWED_AIWG_FETCH_SCHEMES.has(resolved.protocol)) {
461
- throw new Error("Refusing AIWG index fetch with disallowed scheme: " + resolved.protocol);
462
- }
463
- if (resolved.origin !== base.origin) {
464
- throw new Error("Refusing cross-origin AIWG index fetch: " + resolved.origin + " != " + base.origin);
465
- }
466
- return resolved.toString();
467
- }
468
- function createAiwgFetchChunkLoader(baseUrl) {
469
- return async (part) => {
470
- const href = resolveAiwgFetchUrl(part.href, baseUrl);
471
- const response = await fetch(href);
472
- if (!response.ok) throw new Error("Failed to fetch AIWG index chunk " + href + ": " + response.status);
473
- return response.json();
474
- };
475
- }
476
- function encodeAiwgDetailId(id, encoding = "base64url") {
477
- if (encoding === "uri") return encodeURIComponent(id);
478
- const bytes = new TextEncoder().encode(id);
479
- let binary = "";
480
- for (const byte of bytes) binary += String.fromCharCode(byte);
481
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
482
- }
483
- function aiwgDetailHrefForId(detail, id) {
484
- return detail.href.replace("{id}", encodeAiwgDetailId(id, detail.encoding ?? "uri"));
485
- }
486
- function createAiwgFetchDetailLoader(baseUrl) {
487
- return async (id, manifest) => {
488
- if (!manifest.detail?.href) throw new Error("Manifest has no detail.href for record resolution");
489
- const relative = aiwgDetailHrefForId(manifest.detail, id);
490
- const href = resolveAiwgFetchUrl(relative, baseUrl);
491
- const response = await fetch(href);
492
- if (!response.ok) throw new Error("Failed to fetch AIWG index detail " + href + ": " + response.status);
493
- return response.json();
494
- };
495
- }
496
- function getAiwgFortemiFacets(items) {
497
- const result = /* @__PURE__ */ Object.create(null);
498
- for (const item of items) {
499
- pushFacet(result, "type", item.type);
500
- pushFacet(result, "privacy", item.privacy.classification);
501
- for (const tag of item.tags) pushFacet(result, "tag", tag);
502
- for (const concept of item.concepts) pushFacet(result, "concept", concept);
503
- for (const [name, values] of Object.entries(item.facets)) {
504
- for (const value of values) pushFacet(result, name, value);
505
- }
506
- }
507
- return result;
508
- }
509
- function recordTitle(item) {
510
- return item.title ?? item.search?.title ?? item.search?.name ?? item.id;
511
- }
512
- function recordText(item) {
513
- const base = item.text ?? item.search?.body ?? item.search?.summary ?? item.chunks?.map((chunk) => chunk.text ?? chunk.body ?? chunk.summary ?? "").filter(Boolean).join("\n") ?? "";
514
- const extractedText = "binary_sources" in item ? item.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join("\n") ?? "" : "";
515
- return [base, extractedText].filter(Boolean).join("\n");
516
- }
517
- function defaultEmbeddingInput(record, granularity) {
518
- const title = recordTitle(record);
519
- const text = recordText(record);
520
- if (granularity === "title-summary") {
521
- const extractedText = record.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join("\n") ?? "";
522
- return [title, record.search?.summary ?? "", extractedText].filter(Boolean).join("\n");
523
- }
524
- return [title, text].filter(Boolean).join("\n");
525
- }
526
- function generatedAtString(value) {
527
- if (value instanceof Date) return value.toISOString();
528
- return value ?? (/* @__PURE__ */ new Date()).toISOString();
529
- }
530
- async function buildAiwgStaticEmbeddingSet(index, options) {
531
- assertAiwgFortemiIndexExport(index);
532
- const granularity = options.granularity ?? "body";
533
- const records = filterAiwgRecordsByPrivacy(options.records ?? index.items, options.privacy);
534
- const embeddings = [];
535
- for (const record of records) {
536
- const input = options.textForRecord?.(record) ?? defaultEmbeddingInput(record, granularity);
537
- const embedding = await options.backend.embed(input, record);
538
- if (embedding.length !== options.backend.dimensions) {
539
- throw new Error(`Embedding for ${record.id} has ${embedding.length} dimensions; expected ${options.backend.dimensions}`);
540
- }
541
- embeddings.push({
542
- record_id: record.id,
543
- embedding,
544
- granularity,
545
- input_hash: computeHash(new TextEncoder().encode(input)),
546
- source_path: record.source.path
547
- });
548
- }
549
- const embeddingSet = {
550
- schema_version: "aiwg.fortemi.embedding.set.v1",
551
- id: options.id,
552
- model: options.backend.model,
553
- dimensions: options.backend.dimensions,
554
- generated_at: generatedAtString(options.generatedAt),
555
- granularity,
556
- ...options.metric ? { metric: options.metric } : {},
557
- input_hash_algorithm: "sha256",
558
- embeddings
559
- };
560
- assertAiwgStaticEmbeddingSet(embeddingSet);
561
- return embeddingSet;
562
- }
563
- function recordSearchValues(item) {
564
- const search = item.search;
565
- const values = [
566
- item.id,
567
- recordTitle(item),
568
- recordText(item),
569
- search?.title,
570
- search?.name,
571
- search?.summary,
572
- search?.body,
573
- search?.capability,
574
- search?.phase,
575
- search?.type,
576
- ...search?.triggers ?? [],
577
- ...search?.aliases ?? [],
578
- ...search?.tags ?? [],
579
- ...(item.chunks ?? []).flatMap((chunk) => [chunk.text, chunk.body, chunk.summary, chunk.source_path])
580
- ];
581
- if (search?.frontmatter) {
582
- for (const value of Object.values(search.frontmatter)) {
583
- if (typeof value === "string") values.push(value);
584
- else if (Array.isArray(value)) values.push(...value.filter((entry) => typeof entry === "string"));
585
- }
586
- }
587
- return values.filter((value) => typeof value === "string" && value.length > 0);
588
- }
589
- function buildAiwgChunkedIndex(index, options = {}) {
590
- const partSize = hasPositiveInteger(options.partSize) ? options.partSize : 500;
591
- const projection = options.projection;
592
- const idEncoding = options.idEncoding ?? "base64url";
593
- const detailHref = options.detailHref ?? "detail/{id}.json";
594
- const items = filterAiwgRecordsByPrivacy(index.items, options.privacy);
595
- const pad = (value) => String(value).padStart(4, "0");
596
- const project = (record) => {
597
- if (!projection) return record;
598
- const slim = {};
599
- for (const field of projection) slim[field] = record[field];
600
- return slim;
601
- };
602
- const parts = [];
603
- const partRefs = [];
604
- for (let offset = 0, partIndex = 0; offset < items.length; offset += partSize, partIndex += 1) {
605
- const slice = items.slice(offset, offset + partSize);
606
- const href = "part-" + pad(partIndex) + ".json";
607
- parts.push({
608
- href,
609
- part: {
610
- schema_version: "aiwg.fortemi.index.chunk.v1",
611
- manifest_schema_version: "aiwg.fortemi.index.chunk-manifest.v1",
612
- offset,
613
- items: slice.map(project)
614
- }
615
- });
616
- partRefs.push({ href, offset, count: slice.length });
617
- }
618
- const manifest = {
619
- schema_version: "aiwg.fortemi.index.chunk-manifest.v1",
620
- generated_at: options.generatedAt ?? index.generated_at,
621
- source: index.source,
622
- source_export_schema_version: index.schema_version,
623
- total: items.length,
624
- part_size: partSize,
625
- facets: getAiwgFortemiFacets(items),
626
- parts: partRefs,
627
- ...projection ? { projection, detail: { href: detailHref, encoding: idEncoding } } : {}
628
- };
629
- return {
630
- manifest,
631
- parts,
632
- details: projection ? items.map((record) => ({
633
- id: record.id,
634
- href: aiwgDetailHrefForId({ href: detailHref, encoding: idEncoding }, record.id),
635
- record
636
- })) : []
637
- };
638
- }
639
- function includesAll(actual, expected) {
640
- if (!expected || expected.length === 0) return true;
641
- const actualSet = new Set(actual);
642
- return expected.every((value) => actualSet.has(value));
643
- }
644
- function matchesFacetFilters(item, filters) {
645
- if (!filters) return true;
646
- return Object.entries(filters).every(([name, expected]) => includesAll(item.facets[name] ?? [], expected));
647
- }
648
- function queryMatches(item, q) {
649
- if (!q) return [];
650
- const matches = [];
651
- const title = recordTitle(item);
652
- const text = recordText(item);
653
- if (title.toLowerCase().includes(q)) matches.push({ field: "title", value: title });
654
- if (text.toLowerCase().includes(q)) matches.push({ field: "text", value: text });
655
- for (const tag of item.tags) {
656
- if (tag.toLowerCase().includes(q)) matches.push({ field: "tag", value: tag });
657
- }
658
- for (const concept of item.concepts) {
659
- if (concept.toLowerCase().includes(q)) matches.push({ field: "concept", value: concept });
660
- }
661
- for (const value of recordSearchValues(item)) {
662
- if (value !== title && value !== text && value.toLowerCase().includes(q)) {
663
- matches.push({ field: "text", value, score: DEFAULT_QUERY_WEIGHTS.text });
664
- }
665
- }
666
- return matches;
667
- }
668
- var DISCOVERY_STOPWORDS = /* @__PURE__ */ new Set([
669
- "a",
670
- "an",
671
- "and",
672
- "are",
673
- "as",
674
- "for",
675
- "from",
676
- "how",
677
- "i",
678
- "in",
679
- "is",
680
- "me",
681
- "of",
682
- "on",
683
- "or",
684
- "please",
685
- "the",
686
- "to",
687
- "use",
688
- "with"
689
- ]);
690
- function normalizeDiscoveryText(value) {
691
- return value.toLowerCase().replace(/[_/]+/g, " ").replace(/[^a-z0-9.-]+/g, " ").trim();
692
- }
693
- function canonicalDiscoveryName(value) {
694
- return normalizeDiscoveryText(value).replace(/[\s.-]+/g, "");
695
- }
696
- function discoveryTokens(value) {
697
- return normalizeDiscoveryText(value).split(/\s+/).filter((token) => token.length > 1 && !DISCOVERY_STOPWORDS.has(token));
698
- }
699
- function facetValues(item, names) {
700
- return names.flatMap((name) => item.facets[name] ?? []);
701
- }
702
- function addDiscoveryMatch(matches, match) {
703
- if (!matches.some((existing) => existing.field === match.field && existing.value === match.value && existing.reason === match.reason)) {
704
- matches.push(match);
705
- }
706
- }
707
- function tokenOverlapScore(tokens, value) {
708
- if (tokens.length === 0 || !value) return 0;
709
- const normalized = normalizeDiscoveryText(value);
710
- const hits = tokens.filter((token) => normalized.includes(token)).length;
711
- return hits / tokens.length;
712
- }
713
- function discoveryMatches(item, query) {
714
- if (!query) return [];
715
- const matches = [];
716
- const tokens = discoveryTokens(query);
717
- const canonicalQuery = canonicalDiscoveryName(query);
718
- const idParts = item.id.split(/[:/]/);
719
- const names = [
720
- item.id,
721
- recordTitle(item),
722
- item.search?.name,
723
- item.search?.title,
724
- ...item.search?.aliases ?? [],
725
- ...idParts,
726
- ...facetValues(item, ["name", "canonical_name", "command", "skill", "agent", "rule"])
727
- ].filter((value) => hasString(value));
728
- const triggers = [
729
- ...facetValues(item, ["trigger", "triggers", "trigger_phrase", "trigger_phrases"]),
730
- ...item.search?.triggers ?? []
731
- ];
732
- const capabilities = [
733
- ...facetValues(item, ["capability", "capabilities", "summary", "description"]),
734
- item.search?.capability,
735
- item.search?.summary,
736
- item.search?.phase,
737
- item.search?.type,
738
- ...item.search?.tags ?? [],
739
- ...item.concepts,
740
- ...item.tags
741
- ].filter((value) => hasString(value));
742
- const sourceValues = [item.source?.path, item.source?.repo_relative_path, item.source?.locator].filter((value) => hasString(value));
743
- for (const name of names) {
744
- const canonicalName = canonicalDiscoveryName(name);
745
- if (!canonicalName) continue;
746
- if (canonicalName === canonicalQuery) {
747
- addDiscoveryMatch(matches, { field: "id", value: name, score: 80, reason: "exact canonical name" });
748
- } else if (canonicalName.includes(canonicalQuery) || canonicalQuery.includes(canonicalName)) {
749
- addDiscoveryMatch(matches, { field: "id", value: name, score: 48, reason: "near canonical name" });
750
- }
751
- }
752
- const title = recordTitle(item);
753
- const titleOverlap = tokenOverlapScore(tokens, title);
754
- if (titleOverlap > 0) addDiscoveryMatch(matches, { field: "title", value: title, score: 18 * titleOverlap, reason: "title token overlap" });
755
- for (const trigger of triggers) {
756
- const overlap = tokenOverlapScore(tokens, trigger);
757
- if (overlap > 0) addDiscoveryMatch(matches, { field: "facet", value: trigger, score: 34 * overlap, reason: "trigger phrase" });
758
- }
759
- for (const capability of capabilities) {
760
- const overlap = tokenOverlapScore(tokens, capability);
761
- if (overlap > 0) addDiscoveryMatch(matches, { field: "concept", value: capability, score: 22 * overlap, reason: "capability overlap" });
762
- }
763
- const text = recordText(item);
764
- const textOverlap = tokenOverlapScore(tokens, text);
765
- if (textOverlap > 0) addDiscoveryMatch(matches, { field: "text", value: text, score: 8 * textOverlap, reason: "body token overlap" });
766
- for (const source of sourceValues) {
767
- const overlap = tokenOverlapScore(tokens, source);
768
- if (overlap > 0) addDiscoveryMatch(matches, { field: "source", value: source, score: 2 * overlap, reason: "path overlap" });
769
- }
770
- return matches;
771
- }
772
- function rankMatches(matches, weights) {
773
- return matches.reduce((total, match) => total + (match.score ?? weights[match.field]), 0);
774
- }
775
- function clipSnippet(value, q, maxLength) {
776
- const normalizedLength = Math.max(20, maxLength);
777
- if (!value) return "";
778
- if (!q) return value.length > normalizedLength ? `${value.slice(0, normalizedLength).trimEnd()}...` : value;
779
- const lower = value.toLowerCase();
780
- const index = lower.indexOf(q);
781
- if (index < 0) return value.length > normalizedLength ? `${value.slice(0, normalizedLength).trimEnd()}...` : value;
782
- const context = Math.max(0, Math.floor((normalizedLength - q.length) / 2));
783
- const start = Math.max(0, index - context);
784
- const end = Math.min(value.length, start + normalizedLength);
785
- const prefix = start > 0 ? "..." : "";
786
- const suffix = end < value.length ? "..." : "";
787
- return `${prefix}${value.slice(start, end).trim()}${suffix}`;
788
- }
789
- function createSnippet(item, matches, q, maxLength) {
790
- const textMatch = matches.find((match) => match.field === "text");
791
- const titleMatch = matches.find((match) => match.field === "title");
792
- const firstMatch = textMatch ?? titleMatch ?? matches[0];
793
- return clipSnippet(firstMatch?.value ?? recordText(item), q, maxLength);
794
- }
795
- function createRankedEntries(items, q, options, ordinalBase = 0) {
796
- const weights = { ...DEFAULT_QUERY_WEIGHTS, ...options.weights };
797
- const profile = options.searchProfile ?? "default";
798
- return items.map((item, ordinal) => ({
799
- item,
800
- ordinal: ordinalBase + ordinal,
801
- matches: profile === "aiwg-discovery" ? discoveryMatches(item, q) : queryMatches(item, q)
802
- })).filter(({ item, matches }) => {
803
- if (q && matches.length === 0) return false;
804
- if (options.types && !options.types.includes(item.type)) return false;
805
- if (options.privacy && !options.privacy.includes(item.privacy.classification)) return false;
806
- if (!includesAll(item.tags, options.tags)) return false;
807
- if (!includesAll(item.concepts, options.concepts)) return false;
808
- if (!matchesFacetFilters(item, options.facets)) return false;
809
- if (options.relationshipTargetId && !(item.relationships ?? []).some((rel) => rel.target_id === options.relationshipTargetId)) {
810
- return false;
811
- }
812
- return true;
813
- }).map(({ item, ordinal, matches }) => ({
814
- item,
815
- ordinal,
816
- rank: rankMatches(matches, weights),
817
- matches
818
- }));
819
- }
820
- function sortRankedEntries(entries, rank) {
821
- return [...entries].sort((left, right) => {
822
- if (rank) return right.rank - left.rank || left.ordinal - right.ordinal;
823
- return left.ordinal - right.ordinal;
824
- });
825
- }
826
- function createQueryResultFromRankedEntries(entries, query, options) {
827
- const ranked = sortRankedEntries(entries, options.rank);
828
- const offset = options.offset ?? 0;
829
- const limit = options.limit ?? ranked.length;
830
- const page = ranked.slice(offset, offset + limit);
831
- const result = {
832
- items: page.map((entry) => entry.item),
833
- total: ranked.length,
834
- facets: getAiwgFortemiFacets(ranked.map((entry) => entry.item))
835
- };
836
- if (options.rank || options.snippets || options.includeMatches) {
837
- const snippetLength = options.snippetLength ?? 160;
838
- result.rankedItems = page.map((entry) => ({
839
- item: entry.item,
840
- rank: entry.rank,
841
- ...options.snippets ? { snippet: createSnippet(entry.item, entry.matches, query, snippetLength) } : {},
842
- ...options.includeMatches ? { matches: entry.matches } : {}
843
- }));
844
- }
845
- return result;
846
- }
847
- function queryAiwgFortemiIndex(index, query = "", options = {}) {
848
- const q = query.trim().toLowerCase();
849
- const entries = createRankedEntries(index.items, q, options);
850
- if (entries.length === 0 && q && options.searchProfile === "aiwg-discovery") {
851
- const relaxed = discoveryTokens(q).join(" ");
852
- return createQueryResultFromRankedEntries(createRankedEntries(index.items, relaxed, options), relaxed, options);
853
- }
854
- return createQueryResultFromRankedEntries(entries, q, options);
855
- }
856
- function cosineSimilarity(left, right) {
857
- if (left.length !== right.length || left.length === 0) return 0;
858
- let dot = 0;
859
- let leftMag = 0;
860
- let rightMag = 0;
861
- for (let i = 0; i < left.length; i += 1) {
862
- const l = left[i];
863
- const r = right[i];
864
- dot += l * r;
865
- leftMag += l * l;
866
- rightMag += r * r;
867
- }
868
- if (leftMag === 0 || rightMag === 0) return 0;
869
- return dot / (Math.sqrt(leftMag) * Math.sqrt(rightMag));
870
- }
871
- function validateAiwgStaticEmbeddingSet(value) {
872
- const errors = [];
873
- const data = value;
874
- if (data?.schema_version !== "aiwg.fortemi.embedding.set.v1") errors.push("schema_version must be aiwg.fortemi.embedding.set.v1");
875
- if (!hasString(data?.id)) errors.push("id is required");
876
- if (!hasString(data?.model)) errors.push("model is required");
877
- if (!hasPositiveInteger(data?.dimensions)) errors.push("dimensions must be a positive integer");
878
- if (!hasString(data?.generated_at)) errors.push("generated_at is required");
879
- if (!hasString(data?.granularity)) errors.push("granularity is required");
880
- if (!Array.isArray(data?.embeddings)) errors.push("embeddings must be an array");
881
- for (const [index, embedding] of (data.embeddings ?? []).entries()) {
882
- if (!hasString(embedding.record_id)) errors.push("embeddings[" + index + "].record_id is required");
883
- if (!hasString(embedding.input_hash)) errors.push("embeddings[" + index + "].input_hash is required");
884
- if (!Array.isArray(embedding.embedding)) errors.push("embeddings[" + index + "].embedding must be an array");
885
- else if (hasPositiveInteger(data?.dimensions) && embedding.embedding.length !== data.dimensions) {
886
- errors.push("embeddings[" + index + "].embedding length must match dimensions");
887
- } else if (!embedding.embedding.every((number) => typeof number === "number" && Number.isFinite(number))) {
888
- errors.push("embeddings[" + index + "].embedding must contain finite numbers");
889
- }
890
- }
891
- return { valid: errors.length === 0, errors };
892
- }
893
- function assertAiwgStaticEmbeddingSet(value) {
894
- const result = validateAiwgStaticEmbeddingSet(value);
895
- if (!result.valid) throw new Error("Invalid AIWG Fortemi embedding set:\n" + result.errors.join("\n"));
896
- return value;
897
- }
898
- function queryAiwgSemanticIndex(index, embeddingSet, queryEmbedding, options = {}) {
899
- assertAiwgStaticEmbeddingSet(embeddingSet);
900
- if (queryEmbedding.length !== embeddingSet.dimensions) throw new Error("query embedding length must match embedding set dimensions");
901
- const byId = new Map(index.items.map((item) => [item.id, item]));
902
- const offset = options.offset ?? 0;
903
- const limit = options.limit ?? 20;
904
- return embeddingSet.embeddings.map((embedding) => {
905
- const item = byId.get(embedding.record_id);
906
- if (!item) return null;
907
- return { item, embedding, score: cosineSimilarity(queryEmbedding, embedding.embedding) };
908
- }).filter((result) => result !== null && result.score >= (options.minScore ?? -1)).sort((left, right) => right.score - left.score || left.item.id.localeCompare(right.item.id)).slice(offset, offset + limit);
909
- }
910
- function queryAiwgHybridIndex(index, embeddingSet, query, queryEmbedding, options = {}) {
911
- const lexical = queryAiwgFortemiIndex(index, query, { ...options, rank: true });
912
- const semantic = queryAiwgSemanticIndex(index, embeddingSet, queryEmbedding, { limit: index.items.length });
913
- const lexicalWeight = options.lexicalWeight ?? 0.5;
914
- const semanticWeight = options.semanticWeight ?? 0.5;
915
- const lexicalScores = new Map(lexical.rankedItems?.map((entry) => [entry.item.id, entry.rank]) ?? []);
916
- const maxLexical = Math.max(1, ...lexicalScores.values());
917
- const embeddingById = new Map(semantic.flatMap((entry) => entry.embedding ? [[entry.item.id, entry.embedding]] : []));
918
- const semanticScores = new Map(semantic.map((entry) => [entry.item.id, entry.score]));
919
- const ids = /* @__PURE__ */ new Set([...lexicalScores.keys(), ...semanticScores.keys()]);
920
- const offset = options.offset ?? 0;
921
- const limit = options.limit ?? 20;
922
- return [...ids].map((id) => {
923
- const item = index.items.find((candidate) => candidate.id === id);
924
- const embedding = embeddingById.get(id);
925
- if (!item) return null;
926
- return {
927
- item,
928
- ...embedding ? { embedding } : {},
929
- score: (lexicalScores.get(id) ?? 0) / maxLexical * lexicalWeight + (semanticScores.get(id) ?? 0) * semanticWeight
930
- };
931
- }).filter((result) => result !== null && result.score >= (options.minScore ?? -1)).sort((left, right) => right.score - left.score || left.item.id.localeCompare(right.item.id)).slice(offset, offset + limit);
932
- }
933
- var DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS = 5e3;
934
- function findAiwgStaticDuplicatePairs(index, embeddingSet, threshold = 0.9, options) {
935
- assertAiwgStaticEmbeddingSet(embeddingSet);
936
- const maxEmbeddings = options?.maxEmbeddings ?? DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS;
937
- if (embeddingSet.embeddings.length > maxEmbeddings) {
938
- throw new Error(
939
- "Embedding set too large for duplicate scan: " + embeddingSet.embeddings.length + " > " + maxEmbeddings + " (raise options.maxEmbeddings to override for trusted input)"
940
- );
941
- }
942
- const byId = new Map(index.items.map((item) => [item.id, item]));
943
- const pairs = [];
944
- for (let leftIndex = 0; leftIndex < embeddingSet.embeddings.length; leftIndex += 1) {
945
- for (let rightIndex = leftIndex + 1; rightIndex < embeddingSet.embeddings.length; rightIndex += 1) {
946
- const leftEmbedding = embeddingSet.embeddings[leftIndex];
947
- const rightEmbedding = embeddingSet.embeddings[rightIndex];
948
- const left = byId.get(leftEmbedding.record_id);
949
- const right = byId.get(rightEmbedding.record_id);
950
- if (!left || !right) continue;
951
- const score = cosineSimilarity(leftEmbedding.embedding, rightEmbedding.embedding);
952
- if (score >= threshold) pairs.push({ left, right, score });
953
- }
954
- }
955
- return pairs.sort((left, right) => right.score - left.score || left.left.id.localeCompare(right.left.id));
956
- }
957
- function chunkPartCacheKey(part) {
958
- return `${part.offset}:${part.href}`;
959
- }
960
- function clampMaxCachedParts(value) {
961
- if (!hasPositiveInteger(value)) return 3;
962
- return value;
963
- }
964
- function clampMaxCachedDetails(value) {
965
- if (!hasPositiveInteger(value)) return 32;
966
- return value;
967
- }
968
- function clampMaxCachedMatches(value) {
969
- if (!hasPositiveInteger(value)) return 5e3;
970
- return value;
971
- }
972
- function matchSetCacheKey(q, options) {
973
- return JSON.stringify({
974
- q,
975
- types: options.types ?? null,
976
- facets: options.facets ?? null,
977
- tags: options.tags ?? null,
978
- concepts: options.concepts ?? null,
979
- privacy: options.privacy ?? null,
980
- rel: options.relationshipTargetId ?? null,
981
- weights: { ...DEFAULT_QUERY_WEIGHTS, ...options.weights }
982
- });
983
- }
984
- function cacheMatchEntries(runtime, key, entries) {
985
- runtime.matchCache.delete(key);
986
- runtime.matchCache.set(key, entries);
987
- let total = 0;
988
- for (const set of runtime.matchCache.values()) total += set.length;
989
- while (total > runtime.maxCachedMatches && runtime.matchCache.size > 1) {
990
- const oldest = runtime.matchCache.keys().next().value;
991
- if (oldest === void 0 || oldest === key) break;
992
- total -= runtime.matchCache.get(oldest)?.length ?? 0;
993
- runtime.matchCache.delete(oldest);
994
- }
995
- }
996
- function isDirectChunkBrowse(query, options) {
997
- return query.trim() === "" && !options.rank && !options.snippets && !options.includeMatches && !options.types && !options.facets && !options.tags && !options.concepts && !options.privacy && !options.relationshipTargetId;
998
- }
999
- function getPartsForRange(manifest, offset, limit) {
1000
- const end = offset + limit;
1001
- return manifest.parts.filter((part) => part.count > 0 && part.offset < end && part.offset + part.count > offset);
1002
- }
1003
- async function loadChunkPart(runtime, part) {
1004
- const key = chunkPartCacheKey(part);
1005
- const cached = runtime.partCache.get(key);
1006
- if (cached) {
1007
- runtime.partCache.delete(key);
1008
- runtime.partCache.set(key, cached);
1009
- return { part: cached, fetched: false };
1010
- }
1011
- const parsed = assertAiwgFortemiChunkPart(await runtime.loader(part, runtime.manifest), part, runtime.manifest);
1012
- runtime.partCache.set(key, parsed);
1013
- while (runtime.partCache.size > runtime.maxCachedParts) {
1014
- const oldest = runtime.partCache.keys().next().value;
1015
- if (oldest === void 0) break;
1016
- runtime.partCache.delete(oldest);
1017
- }
1018
- return { part: parsed, fetched: true };
1019
- }
1020
- async function getChunkRecord(runtime, id) {
1021
- const cached = runtime.detailCache.get(id);
1022
- if (cached) {
1023
- runtime.detailCache.delete(id);
1024
- runtime.detailCache.set(id, cached);
1025
- return cached;
1026
- }
1027
- if (!runtime.manifest.projection) {
1028
- for (const part of runtime.partCache.values()) {
1029
- const found = part.items.find((item) => item.id === id);
1030
- if (found) return found;
1031
- }
1032
- }
1033
- if (!runtime.detailLoader) {
1034
- throw new Error("No detailLoader configured to resolve record " + id);
1035
- }
1036
- const raw = await runtime.detailLoader(id, runtime.manifest);
1037
- const record = assertAiwgFortemiIndexExport({
1038
- schema_version: "aiwg.fortemi.index.export.v1",
1039
- generated_at: runtime.manifest.generated_at,
1040
- source: runtime.manifest.source,
1041
- items: [raw]
1042
- }).items[0];
1043
- if (record.id !== id) {
1044
- throw new Error("Detail record id mismatch: expected " + id + ", got " + record.id);
1045
- }
1046
- runtime.detailCache.set(id, record);
1047
- while (runtime.detailCache.size > runtime.maxCachedDetails) {
1048
- const oldest = runtime.detailCache.keys().next().value;
1049
- if (oldest === void 0) break;
1050
- runtime.detailCache.delete(oldest);
1051
- }
1052
- return record;
1053
- }
1054
- function relationshipTypeFilter(options) {
1055
- return options?.relationshipType ?? options?.type;
1056
- }
1057
- function edgeFromRelationship(sourceId, relationship) {
1058
- return {
1059
- source_id: sourceId,
1060
- target_id: relationship.target_id,
1061
- type: relationship.type,
1062
- ...relationship.source_path ? { source_path: relationship.source_path } : {},
1063
- ...relationship.target_path ? { target_path: relationship.target_path } : {},
1064
- ...relationship.direction ? { direction: relationship.direction } : {}
1065
- };
1066
- }
1067
- function relationshipMatches(edge, options = {}) {
1068
- const type = relationshipTypeFilter(options);
1069
- const direction = options.direction ?? "both";
1070
- if (type && edge.type !== type) return false;
1071
- if (options.relationshipDirection && edge.direction !== options.relationshipDirection) return false;
1072
- if (options.sourceId && edge.source_id !== options.sourceId) return false;
1073
- if (options.targetId && edge.target_id !== options.targetId) return false;
1074
- if (direction === "out" && options.targetId && edge.target_id !== options.targetId) return false;
1075
- if (direction === "in" && options.sourceId && edge.source_id !== options.sourceId) return false;
1076
- return true;
1077
- }
1078
- function nodeSummary(item) {
1079
- return { id: item.id, type: item.type, title: recordTitle(item) };
1080
- }
1081
- function addNode(nodes, item) {
1082
- if (item) nodes.set(item.id, nodeSummary(item));
1083
- }
1084
- function relationshipResultFromRecords(records, options = {}) {
1085
- const byId = new Map(records.map((record) => [record.id, record]));
1086
- const edges = [];
1087
- for (const record of records) {
1088
- for (const relationship of record.relationships ?? []) {
1089
- const edge = edgeFromRelationship(record.id, relationship);
1090
- if (!relationshipMatches(edge, options)) continue;
1091
- edges.push(edge);
1092
- }
1093
- }
1094
- const limitedEdges = (options.limit ? edges.slice(0, options.limit) : edges).sort((left, right) => left.source_id.localeCompare(right.source_id) || left.target_id.localeCompare(right.target_id) || left.type.localeCompare(right.type));
1095
- const nodes = /* @__PURE__ */ new Map();
1096
- for (const edge of limitedEdges) {
1097
- addNode(nodes, byId.get(edge.source_id));
1098
- addNode(nodes, byId.get(edge.target_id));
1099
- }
1100
- return {
1101
- nodes: [...nodes.values()].sort((left, right) => left.id.localeCompare(right.id)),
1102
- edges: limitedEdges,
1103
- complete: true
1104
- };
1105
- }
1106
- function neighborQueryOptions(id, options = {}) {
1107
- const direction = options.direction ?? "both";
1108
- return {
1109
- ...options,
1110
- ...direction === "out" ? { sourceId: id } : {},
1111
- ...direction === "in" ? { targetId: id } : {}
1112
- };
1113
- }
1114
- function filterNeighborResult(id, result, options = {}) {
1115
- const direction = options.direction ?? "both";
1116
- const edges = result.edges.filter((edge) => {
1117
- if (direction === "out") return edge.source_id === id;
1118
- if (direction === "in") return edge.target_id === id;
1119
- return edge.source_id === id || edge.target_id === id;
1120
- });
1121
- const ids = /* @__PURE__ */ new Set();
1122
- for (const edge of edges) {
1123
- ids.add(edge.source_id);
1124
- ids.add(edge.target_id);
1125
- }
1126
- return {
1127
- ...result,
1128
- edges,
1129
- nodes: result.nodes.filter((node) => ids.has(node.id))
1130
- };
1131
- }
1132
- async function recordsFromChunkedRuntime(runtime, onProgress) {
1133
- let scannedParts = 0;
1134
- let fetchedParts = 0;
1135
- const records = [];
1136
- for (const partRef of runtime.manifest.parts) {
1137
- const loaded = await loadChunkPart(runtime, partRef);
1138
- if (loaded.fetched) fetchedParts += 1;
1139
- scannedParts += 1;
1140
- onProgress?.({ phase: "part", done: scannedParts, total: runtime.manifest.parts.length, href: partRef.href });
1141
- for (const item of loaded.part.items) {
1142
- records.push(item.relationships ? item : await getChunkRecord(runtime, item.id));
1143
- }
1144
- }
1145
- return { records, scannedParts, fetchedParts };
1146
- }
1147
- async function relationshipResultFromChunkedRuntime(runtime, options = {}) {
1148
- const loaded = await recordsFromChunkedRuntime(runtime);
1149
- return {
1150
- ...relationshipResultFromRecords(loaded.records, options),
1151
- scannedParts: loaded.scannedParts,
1152
- fetchedParts: loaded.fetchedParts
1153
- };
1154
- }
1155
- async function queryChunkedAiwgFortemiIndex(runtime, query = "", options = {}) {
1156
- const q = query.trim().toLowerCase();
1157
- let scannedParts = 0;
1158
- let fetchedParts = 0;
1159
- if (isDirectChunkBrowse(query, options)) {
1160
- const offset = options.offset ?? 0;
1161
- const limit = options.limit ?? runtime.manifest.total;
1162
- const parts = getPartsForRange(runtime.manifest, offset, limit);
1163
- const items = [];
1164
- for (const partRef of parts) {
1165
- const loaded = await loadChunkPart(runtime, partRef);
1166
- if (loaded.fetched) fetchedParts += 1;
1167
- scannedParts += 1;
1168
- options.onProgress?.({ phase: "part", done: scannedParts, total: parts.length, href: partRef.href });
1169
- const start = Math.max(0, offset - partRef.offset);
1170
- const end = Math.min(loaded.part.items.length, offset + limit - partRef.offset);
1171
- items.push(...loaded.part.items.slice(start, end));
1172
- }
1173
- return {
1174
- items,
1175
- total: runtime.manifest.total,
1176
- facets: runtime.manifest.facets ?? {},
1177
- manifestTotal: runtime.manifest.total,
1178
- scannedParts,
1179
- fetchedParts,
1180
- complete: true
1181
- };
1182
- }
1183
- const matchKey = matchSetCacheKey(q, options);
1184
- const cached = runtime.matchCache.get(matchKey);
1185
- if (cached) {
1186
- runtime.matchCache.delete(matchKey);
1187
- runtime.matchCache.set(matchKey, cached);
1188
- return {
1189
- ...createQueryResultFromRankedEntries(cached, q, options),
1190
- manifestTotal: runtime.manifest.total,
1191
- scannedParts: 0,
1192
- fetchedParts: 0,
1193
- complete: true
1194
- };
1195
- }
1196
- const entries = [];
1197
- for (const partRef of runtime.manifest.parts) {
1198
- const loaded = await loadChunkPart(runtime, partRef);
1199
- if (loaded.fetched) fetchedParts += 1;
1200
- scannedParts += 1;
1201
- options.onProgress?.({ phase: "part", done: scannedParts, total: runtime.manifest.parts.length, href: partRef.href });
1202
- entries.push(...createRankedEntries(loaded.part.items, q, options, partRef.offset));
1203
- options.onProgress?.({ phase: "query", done: scannedParts, total: runtime.manifest.parts.length, href: partRef.href });
1204
- }
1205
- cacheMatchEntries(runtime, matchKey, entries);
1206
- return {
1207
- ...createQueryResultFromRankedEntries(entries, q, options),
1208
- manifestTotal: runtime.manifest.total,
1209
- scannedParts,
1210
- fetchedParts,
1211
- complete: true
1212
- };
1213
- }
1214
- function createAiwgReviewDecisionExport(source, decisions, generatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
1215
- return {
1216
- schema_version: "aiwg.fortemi.review-decisions.v1",
1217
- generated_at: generatedAt,
1218
- source_export_schema_version: source.schema_version,
1219
- decisions: [...decisions].sort((left, right) => left.item_id.localeCompare(right.item_id))
1220
- };
1221
- }
1222
- function createAiwgIndexController(initialIndex) {
1223
- let index = initialIndex ?? null;
1224
- let chunked = null;
1225
- let data = null;
1226
- let error = null;
1227
- let reviewDecisions = [];
1228
- const listeners = /* @__PURE__ */ new Set();
1229
- const snapshot = () => ({
1230
- index,
1231
- chunked: chunked ? {
1232
- manifest: chunked.manifest,
1233
- cachedParts: chunked.partCache.size,
1234
- maxCachedParts: chunked.maxCachedParts
1235
- } : null,
1236
- data,
1237
- error,
1238
- reviewDecisions: [...reviewDecisions]
1239
- });
1240
- const notify = () => {
1241
- const current = snapshot();
1242
- for (const listener of listeners) listener(current);
1243
- };
1244
- const requireIndex = () => {
1245
- if (!index) throw new Error("No AIWG index export loaded");
1246
- return index;
1247
- };
1248
- return {
1249
- loadIndex(value) {
1250
- try {
1251
- const parsed = assertAiwgFortemiIndexExport(value);
1252
- index = parsed;
1253
- chunked = null;
1254
- data = null;
1255
- reviewDecisions = [];
1256
- error = null;
1257
- notify();
1258
- return parsed;
1259
- } catch (err) {
1260
- error = err instanceof Error ? err : new Error(String(err));
1261
- notify();
1262
- throw error;
1263
- }
1264
- },
1265
- loadChunkedIndex(manifest, loader, options = {}) {
1266
- try {
1267
- const parsed = assertAiwgFortemiChunkManifest(manifest);
1268
- index = null;
1269
- chunked = {
1270
- manifest: parsed,
1271
- loader,
1272
- maxCachedParts: clampMaxCachedParts(options.maxCachedParts),
1273
- partCache: /* @__PURE__ */ new Map(),
1274
- detailLoader: options.detailLoader,
1275
- maxCachedDetails: clampMaxCachedDetails(options.maxCachedDetails),
1276
- detailCache: /* @__PURE__ */ new Map(),
1277
- maxCachedMatches: clampMaxCachedMatches(options.maxCachedMatches),
1278
- matchCache: /* @__PURE__ */ new Map()
1279
- };
1280
- data = null;
1281
- reviewDecisions = [];
1282
- error = null;
1283
- notify();
1284
- return parsed;
1285
- } catch (err) {
1286
- error = err instanceof Error ? err : new Error(String(err));
1287
- notify();
1288
- throw error;
1289
- }
1290
- },
1291
- getIndex() {
1292
- return index;
1293
- },
1294
- getChunkedManifest() {
1295
- return chunked?.manifest ?? null;
1296
- },
1297
- getSnapshot() {
1298
- return snapshot();
1299
- },
1300
- query(query = "", options) {
1301
- const result = queryAiwgFortemiIndex(requireIndex(), query, options);
1302
- data = result;
1303
- error = null;
1304
- notify();
1305
- return result;
1306
- },
1307
- async queryChunked(query = "", options) {
1308
- if (!chunked) throw new Error("No AIWG chunked index manifest loaded");
1309
- try {
1310
- const result = await queryChunkedAiwgFortemiIndex(chunked, query, options);
1311
- data = result;
1312
- error = null;
1313
- notify();
1314
- return result;
1315
- } catch (err) {
1316
- error = err instanceof Error ? err : new Error(String(err));
1317
- notify();
1318
- throw error;
1319
- }
1320
- },
1321
- async getRecord(id) {
1322
- if (chunked) {
1323
- try {
1324
- return await getChunkRecord(chunked, id);
1325
- } catch (err) {
1326
- error = err instanceof Error ? err : new Error(String(err));
1327
- notify();
1328
- throw error;
1329
- }
1330
- }
1331
- const found = requireIndex().items.find((item) => item.id === id);
1332
- if (!found) throw new Error("Record not found: " + id);
1333
- return found;
1334
- },
1335
- async neighbors(id, options) {
1336
- try {
1337
- const queryOptions = neighborQueryOptions(id, options);
1338
- const result = chunked ? await relationshipResultFromChunkedRuntime(chunked, queryOptions) : relationshipResultFromRecords(requireIndex().items, queryOptions);
1339
- return filterNeighborResult(id, result, options);
1340
- } catch (err) {
1341
- error = err instanceof Error ? err : new Error(String(err));
1342
- notify();
1343
- throw error;
1344
- }
1345
- },
1346
- async relationshipQuery(options) {
1347
- try {
1348
- return chunked ? await relationshipResultFromChunkedRuntime(chunked, options) : relationshipResultFromRecords(requireIndex().items, options);
1349
- } catch (err) {
1350
- error = err instanceof Error ? err : new Error(String(err));
1351
- notify();
1352
- throw error;
1353
- }
1354
- },
1355
- async relationshipSet(options) {
1356
- const [left, right] = await Promise.all([
1357
- this.neighbors(options.a, options),
1358
- this.neighbors(options.b, options)
1359
- ]);
1360
- const leftIds = new Set(left.nodes.map((node) => node.id).filter((id) => id !== options.a));
1361
- const rightIds = new Set(right.nodes.map((node) => node.id).filter((id) => id !== options.b));
1362
- let ids;
1363
- if (options.op === "intersection") ids = [...leftIds].filter((id) => rightIds.has(id));
1364
- else if (options.op === "difference") ids = [...leftIds].filter((id) => !rightIds.has(id));
1365
- else ids = [.../* @__PURE__ */ new Set([...leftIds, ...rightIds])];
1366
- return { op: options.op, ids: ids.sort() };
1367
- },
1368
- clearChunkCache() {
1369
- chunked?.partCache.clear();
1370
- chunked?.detailCache.clear();
1371
- chunked?.matchCache.clear();
1372
- error = null;
1373
- notify();
1374
- },
1375
- toCommunityGraph(options) {
1376
- return aiwgFortemiIndexToCommunityGraph(requireIndex(), options);
1377
- },
1378
- async toCommunityGraphChunked(options) {
1379
- if (!chunked) return aiwgFortemiIndexToCommunityGraph(requireIndex(), options);
1380
- const loaded = await recordsFromChunkedRuntime(chunked, options?.onProgress);
1381
- return aiwgFortemiIndexToCommunityGraph({
1382
- generated_at: chunked.manifest.generated_at,
1383
- source: chunked.manifest.source,
1384
- items: loaded.records
1385
- }, options);
1386
- },
1387
- setReviewDecision(input) {
1388
- const decision = {
1389
- ...input,
1390
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
1391
- };
1392
- reviewDecisions = [
1393
- ...reviewDecisions.filter((item) => item.item_id !== decision.item_id),
1394
- decision
1395
- ].sort((left, right) => left.item_id.localeCompare(right.item_id));
1396
- error = null;
1397
- notify();
1398
- return decision;
1399
- },
1400
- clearReviewDecision(itemId) {
1401
- reviewDecisions = reviewDecisions.filter((item) => item.item_id !== itemId);
1402
- error = null;
1403
- notify();
1404
- },
1405
- createReviewDecisionExport(generatedAt) {
1406
- const source = index ?? (chunked ? { schema_version: chunked.manifest.source_export_schema_version ?? "aiwg.fortemi.index.export.v1" } : null);
1407
- if (!source) throw new Error("No AIWG index export or chunked manifest loaded");
1408
- return createAiwgReviewDecisionExport(source, reviewDecisions, generatedAt);
1409
- },
1410
- subscribe(listener) {
1411
- listeners.add(listener);
1412
- return () => {
1413
- listeners.delete(listener);
1414
- };
1415
- }
1416
- };
1417
- }
1418
- function aiwgFortemiIndexToCommunityGraph(index, options = {}) {
1419
- const ids = new Set(index.items.map((item) => item.id));
1420
- const relationshipWeights = options.relationshipWeights ?? {};
1421
- const edgeCounts = /* @__PURE__ */ new Map();
1422
- for (const item of index.items) {
1423
- for (const relationship of item.relationships) {
1424
- if (!ids.has(relationship.target_id) && !options.includeDanglingRelationships) continue;
1425
- const kind = relationship.type;
1426
- const baseWeight = relationshipWeights[kind] ?? 1;
1427
- const key = `${item.id}\0${relationship.target_id}\0${kind}`;
1428
- const existing = edgeCounts.get(key);
1429
- if (existing) existing.weight += baseWeight;
1430
- else edgeCounts.set(key, { source: item.id, target: relationship.target_id, kind, weight: baseWeight });
1431
- }
1432
- }
1433
- const communities = /* @__PURE__ */ new Map();
1434
- for (const item of index.items) {
1435
- const communityIds = communityIdsFor(item, options);
1436
- for (const communityId of communityIds) {
1437
- const nodes = communities.get(communityId) ?? [];
1438
- nodes.push(item.id);
1439
- communities.set(communityId, nodes);
1440
- }
1441
- }
1442
- return {
1443
- nodes: index.items.map((item) => ({ id: item.id })),
1444
- edges: Array.from(edgeCounts.values()).sort((left, right) => left.source.localeCompare(right.source) || left.target.localeCompare(right.target) || left.kind.localeCompare(right.kind)),
1445
- communities: Array.from(communities.entries()).map(([id, nodes]) => ({ id, nodes: [...new Set(nodes)].sort() })).sort((left, right) => left.id.localeCompare(right.id))
1446
- };
1447
- }
1448
- function communityIdsFor(item, options) {
1449
- if (options.communityFacet) {
1450
- const values = item.facets[options.communityFacet] ?? [];
1451
- if (values.length > 0) return values.map((value) => `${options.communityFacet}:${value}`);
1452
- }
1453
- if (options.communityTagPrefix) {
1454
- const prefix = options.communityTagPrefix;
1455
- const tags = item.tags.filter((tag) => tag.startsWith(prefix));
1456
- if (tags.length > 0) return tags;
1457
- }
1458
- if (item.concepts.length > 0) return item.concepts.map((concept) => `concept:${concept}`);
1459
- return [`type:${item.type}`];
1460
- }
1461
-
1462
- export { AIWG_SCAN_REQUIRED_FIELDS, DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, filterAiwgRecordsByPrivacy, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, resolveAiwgFetchUrl, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
1463
- //# sourceMappingURL=aiwg-index.js.map
1
+ var we=["schema_version","id","type","title","text","facets","tags","concepts","privacy"];function ye(e,t){let i=e.privacy;return !!(!i||!P(i.classification)||typeof i.pii!="boolean"||i.classification==="private"&&!t?.includePrivate||i.pii&&!t?.includePii)}function ie(e,t){return e.filter(i=>!ye(i,t))}var Ae=["schema_version","id","type","source","facets","tags","concepts","relationships","provenance","privacy","updated_at"],K={title:4,tag:3,concept:2,text:1,facet:2,id:1,source:.25};function l(e){return typeof e=="string"&&e.length>0}function M(e,t,i){let r=e[t];r===void 0&&(r=Object.create(null),e[t]=r),r[i]=(r[i]??0)+1;}function C(e){return Number.isInteger(e)&&typeof e=="number"&&e>=0}function S(e){return Number.isInteger(e)&&typeof e=="number"&&e>0}function xe(e){return !e||typeof e!="object"||Array.isArray(e)?false:Object.values(e).every(t=>!!t&&typeof t=="object"&&!Array.isArray(t)&&Object.values(t).every(i=>C(i)))}function h(e){return !!e&&typeof e=="object"&&!Array.isArray(e)}function L(e){return e===void 0||Array.isArray(e)&&e.every(t=>typeof t=="string")}function re(e){return e==="aiwg.fortemi.index.export.v1"||e==="aiwg.fortemi.index.export.v2"}function ne(e){return e==="aiwg.fortemi.index.record.v1"||e==="aiwg.fortemi.index.record.v2"}function be(e,t,i){if(e.skos_concepts!==void 0)if(!Array.isArray(e.skos_concepts))i.push("items["+t+"].skos_concepts must be an array when present");else for(let[r,n]of e.skos_concepts.entries()){if(!h(n)){i.push("items["+t+"].skos_concepts["+r+"] must be an object");continue}l(n.id)||i.push("items["+t+"].skos_concepts["+r+"].id is required"),l(n.prefLabel)||i.push("items["+t+"].skos_concepts["+r+"].prefLabel is required"),L(n.altLabels)||i.push("items["+t+"].skos_concepts["+r+"].altLabels must be a string array"),n.metadata!==void 0&&!h(n.metadata)&&i.push("items["+t+"].skos_concepts["+r+"].metadata must be an object");}if(e.skos_relations!==void 0)if(!Array.isArray(e.skos_relations))i.push("items["+t+"].skos_relations must be an array when present");else for(let[r,n]of e.skos_relations.entries()){if(!h(n)){i.push("items["+t+"].skos_relations["+r+"] must be an object");continue}l(n.type)||i.push("items["+t+"].skos_relations["+r+"].type is required"),l(n.source_id)||i.push("items["+t+"].skos_relations["+r+"].source_id is required"),l(n.target_id)||i.push("items["+t+"].skos_relations["+r+"].target_id is required"),n.metadata!==void 0&&!h(n.metadata)&&i.push("items["+t+"].skos_relations["+r+"].metadata must be an object");}if(e.provenance_events!==void 0)if(!Array.isArray(e.provenance_events))i.push("items["+t+"].provenance_events must be an array when present");else for(let[r,n]of e.provenance_events.entries()){if(!h(n)){i.push("items["+t+"].provenance_events["+r+"] must be an object");continue}l(n.activity)||i.push("items["+t+"].provenance_events["+r+"].activity is required"),n.attributes!==void 0&&!h(n.attributes)&&i.push("items["+t+"].provenance_events["+r+"].attributes must be an object");}if(Array.isArray(e.relationships))for(let[r,n]of e.relationships.entries()){if(!h(n)){i.push("items["+t+"].relationships["+r+"] must be an object");continue}n.metadata!==void 0&&!h(n.metadata)&&i.push("items["+t+"].relationships["+r+"].metadata must be an object"),n.direction!==void 0&&n.direction!=="upstream"&&n.direction!=="downstream"&&n.direction!=="related"&&i.push("items["+t+"].relationships["+r+"].direction must be upstream, downstream, or related"),n.target_path!==void 0&&typeof n.target_path!="string"&&i.push("items["+t+"].relationships["+r+"].target_path must be a string");}if(e.search!==void 0&&(h(e.search)?(L(e.search.triggers)||i.push("items["+t+"].search.triggers must be a string array"),L(e.search.aliases)||i.push("items["+t+"].search.aliases must be a string array"),L(e.search.tags)||i.push("items["+t+"].search.tags must be a string array"),e.search.frontmatter!==void 0&&!h(e.search.frontmatter)&&i.push("items["+t+"].search.frontmatter must be an object")):i.push("items["+t+"].search must be an object")),e.chunks!==void 0)if(!Array.isArray(e.chunks))i.push("items["+t+"].chunks must be an array when present");else for(let[r,n]of e.chunks.entries()){if(!h(n)){i.push("items["+t+"].chunks["+r+"] must be an object");continue}n.metadata!==void 0&&!h(n.metadata)&&i.push("items["+t+"].chunks["+r+"].metadata must be an object");}if(e.embeddings!==void 0)if(!Array.isArray(e.embeddings))i.push("items["+t+"].embeddings must be an array when present");else for(let[r,n]of e.embeddings.entries()){if(!h(n)){i.push("items["+t+"].embeddings["+r+"] must be an object");continue}let s=n.embedding??n.vector;s!==void 0&&(!Array.isArray(s)||!s.every(o=>typeof o=="number"))&&i.push("items["+t+"].embeddings["+r+"].embedding/vector must be a number array"),n.metadata!==void 0&&!h(n.metadata)&&i.push("items["+t+"].embeddings["+r+"].metadata must be an object");}e.compatibility!==void 0&&!h(e.compatibility)&&i.push("items["+t+"].compatibility must be an object");}function P(e){return e==="private"||e==="sanitized"||e==="public"}function se(e){return e==="source"||e==="candidate"||e==="reviewed"||e==="rejected"}function ve(e,t,i){if(Array.isArray(e.provenance))for(let[r,n]of e.provenance.entries()){let s="items["+t+"].provenance["+r+"]";if(!h(n)){i.push(s+" must be an object");continue}l(n.field)||i.push(s+".field is required"),l(n.source)||i.push(s+".source is required"),l(n.path)||i.push(s+".path is required"),se(n.confidence)||i.push(s+".confidence must be one of source, candidate, reviewed, rejected"),P(n.privacy)||i.push(s+".privacy must be one of private, sanitized, public");}}var Re=["search","chunks","embeddings","skos_concepts","skos_relations","provenance_events","compatibility"],ke=["origin","generated","checksum","updated_at"],_e=["target_path","direction","metadata"];function Ie(e,t,i){if(e.schema_version!=="aiwg.fortemi.index.record.v1")return;let r="items["+t+"]",n=e,s=" is a v2-only field and must be absent on a record.v1 record";for(let o of Re)n[o]!==void 0&&i.push(r+"."+o+s);if(h(e.source)){let o=e.source;for(let a of ke)o[a]!==void 0&&i.push(r+".source."+a+s);}if(h(e.privacy)&&e.privacy.locality!==void 0&&i.push(r+".privacy.locality"+s),Array.isArray(e.relationships))for(let[o,a]of e.relationships.entries()){if(!h(a))continue;let c=a;for(let g of _e)c[g]!==void 0&&i.push(r+".relationships["+o+"]."+g+s);}}function oe(e){let t=[],i=Object.create(null),r=h(e)?e:{};h(e)||t.push("index export must be an object"),re(r?.schema_version)||t.push("schema_version must be aiwg.fortemi.index.export.v1 or aiwg.fortemi.index.export.v2"),l(r?.generated_at)||t.push("generated_at is required"),l(r?.source?.repo)||t.push("source.repo is required"),l(r?.source?.privacy)?P(r?.source?.privacy)||t.push("source.privacy must be one of private, sanitized, public"):t.push("source.privacy is required"),Array.isArray(r?.items)||t.push("items must be an array"),r.compatibility!==void 0&&!h(r.compatibility)&&t.push("compatibility must be an object"),r?.schema_version==="aiwg.fortemi.index.export.v1"&&(h(r.source)&&r.source.graph!==void 0&&t.push("source.graph is a v2-only field and must be absent on an export.v1 export"),r.compatibility!==void 0&&t.push("compatibility is a v2-only field and must be absent on an export.v1 export"));let n=new Set,s="",o=Array.isArray(r.items)?r.items:[];for(let[a,c]of o.entries()){if(!h(c)){t.push("items["+a+"] must be an object");continue}for(let g of Ae)g in c||t.push("items["+a+"]."+g+" is required");ne(c.schema_version)||t.push("items["+a+"].schema_version must be aiwg.fortemi.index.record.v1 or aiwg.fortemi.index.record.v2"),r.schema_version==="aiwg.fortemi.index.export.v1"&&c.schema_version==="aiwg.fortemi.index.record.v2"&&t.push("items["+a+"].schema_version must match aiwg.fortemi.index.export.v1"),l(c.id)||t.push("items["+a+"].id is required"),l(c.id)&&n.has(c.id)&&t.push("duplicate id: "+c.id),l(c.id)&&n.add(c.id),s&&l(c.id)&&s>c.id&&t.push("items must be sorted by id: "+s+" before "+c.id),l(c.id)&&(s=c.id),l(c.type)?i[c.type]=(i[c.type]??0)+1:t.push("items["+a+"].type must be a non-empty string"),l(c.source?.path)||t.push("items["+a+"].source.path is required"),l(c.source?.repo_relative_path)||t.push("items["+a+"].source.repo_relative_path is required"),l(c.source?.locator)||t.push("items["+a+"].source.locator is required"),typeof c.title!="string"&&typeof c.search?.title!="string"&&typeof c.search?.name!="string"&&t.push("items["+a+"].title or search.title/search.name is required"),typeof c.text!="string"&&typeof c.search?.body!="string"&&typeof c.search?.summary!="string"&&t.push("items["+a+"].text or search.body/search.summary is required"),Array.isArray(c.tags)||t.push("items["+a+"].tags must be an array"),Array.isArray(c.concepts)||t.push("items["+a+"].concepts must be an array"),Array.isArray(c.relationships)||t.push("items["+a+"].relationships must be an array"),(!Array.isArray(c.provenance)||c.provenance.length===0)&&t.push("items["+a+"].provenance must be a non-empty array"),be(c,a,t),ve(c,a,t),Ie(c,a,t),!c.privacy||typeof c.privacy.pii!="boolean"||!l(c.privacy.classification)?t.push("items["+a+"].privacy requires classification and pii"):P(c.privacy.classification)||t.push("items["+a+"].privacy.classification must be one of private, sanitized, public");}return {valid:t.length===0,errors:t,counts:i}}function q(e){let t=oe(e);if(!t.valid)throw new Error(`Invalid AIWG Fortemi index export:
2
+ `+t.errors.join(`
3
+ `));return e}function Ce(e){let t=[],i=h(e)?e:{};if(h(e)||t.push("chunk manifest must be an object"),i?.schema_version!=="aiwg.fortemi.index.chunk-manifest.v1"&&t.push("schema_version must be aiwg.fortemi.index.chunk-manifest.v1"),l(i?.generated_at)||t.push("generated_at is required"),l(i?.source?.repo)||t.push("source.repo is required"),l(i?.source?.privacy)||t.push("source.privacy is required"),i?.source_export_schema_version!==void 0&&!re(i.source_export_schema_version)&&t.push("source_export_schema_version must be aiwg.fortemi.index.export.v1 or aiwg.fortemi.index.export.v2 when present"),C(i?.total)||t.push("total must be a non-negative integer"),S(i?.part_size)||t.push("part_size must be a positive integer"),i.facets!==void 0&&!xe(i.facets)&&t.push("facets must be a nested string-to-number count object"),i.projection!==void 0)if(!Array.isArray(i.projection)||!i.projection.every(s=>typeof s=="string"))t.push("projection must be an array of field names");else {let s=new Set(i.projection);for(let o of we)s.has(o)||t.push("projection must include scan-required field "+o);}i.detail!==void 0&&!h(i.detail)?t.push("detail must be an object"):i.detail!==void 0&&(l(i.detail.href)?i.detail.href.includes("{id}")||t.push("detail.href must contain the {id} placeholder"):t.push("detail.href is required"),i.detail.encoding!==void 0&&i.detail.encoding!=="uri"&&i.detail.encoding!=="base64url"&&t.push("detail.encoding must be 'uri' or 'base64url'")),Array.isArray(i?.parts)||t.push("parts must be an array");let r=0,n=Array.isArray(i?.parts)?i.parts:[];for(let[s,o]of n.entries()){if(!h(o)){t.push("parts["+s+"] must be an object");continue}l(o.href)||t.push("parts["+s+"].href is required"),C(o.offset)||t.push("parts["+s+"].offset must be a non-negative integer"),C(o.count)||t.push("parts["+s+"].count must be a non-negative integer"),C(o.offset)&&o.offset!==r&&t.push("parts["+s+"].offset must be "+r),C(o.count)&&(r+=o.count);}return C(i?.total)&&r!==i.total&&t.push("parts counts must add up to total"),{valid:t.length===0,errors:t}}function Fe(e){let t=Ce(e);if(!t.valid)throw new Error(`Invalid AIWG Fortemi chunk manifest:
4
+ `+t.errors.join(`
5
+ `));return e}function Se(e,t,i){let r="items["+t+"]";if(!e.privacy||!h(e.privacy)?i.push(r+"/privacy requires classification and pii"):(P(e.privacy.classification)||i.push(r+"/privacy/classification must be one of private, sanitized, public"),typeof e.privacy.pii!="boolean"&&i.push(r+"/privacy/pii must be boolean")),Array.isArray(e.provenance))for(let[n,s]of e.provenance.entries()){if(!h(s)){i.push(r+"/provenance/"+n+" must be an object");continue}se(s.confidence)||i.push(r+"/provenance/"+n+"/confidence must be one of source, candidate, reviewed, rejected"),P(s.privacy)||i.push(r+"/provenance/"+n+"/privacy must be one of private, sanitized, public");}}function Pe(e,t){let i=[],r=new Set,n="";for(let[s,o]of e.entries()){if(!h(o)){i.push("items["+s+"] must be an object");continue}Se(o,s,i);let a=t==="aiwg.fortemi.index.export.v2"?"aiwg.fortemi.index.record.v2":"aiwg.fortemi.index.record.v1";o.schema_version!==a&&i.push(`items[${s}].schema_version must match ${t}`),ne(o.schema_version)||i.push("items["+s+"].schema_version must be aiwg.fortemi.index.record.v1 or aiwg.fortemi.index.record.v2"),l(o.id)||i.push("items["+s+"].id is required"),l(o.id)&&r.has(o.id)&&i.push("duplicate id: "+o.id),l(o.id)&&r.add(o.id),n&&l(o.id)&&n>o.id&&i.push("items must be sorted by id: "+n+" before "+o.id),l(o.id)&&(n=o.id),l(o.type)||i.push("items["+s+"].type must be a non-empty string"),typeof o.title!="string"&&typeof o.search?.title!="string"&&typeof o.search?.name!="string"&&i.push("items["+s+"].title or search.title/search.name is required"),typeof o.text!="string"&&typeof o.search?.body!="string"&&typeof o.search?.summary!="string"&&i.push("items["+s+"].text or search.body/search.summary is required"),(!o.facets||typeof o.facets!="object"||Array.isArray(o.facets))&&i.push("items["+s+"].facets must be an object"),Array.isArray(o.tags)||i.push("items["+s+"].tags must be an array"),Array.isArray(o.concepts)||i.push("items["+s+"].concepts must be an array");}return i}function Ee(e,t,i){let r=[],n=h(e)?e:{};if(h(e)||r.push("chunk part must be an object"),n?.schema_version!=="aiwg.fortemi.index.chunk.v1"&&r.push("schema_version must be aiwg.fortemi.index.chunk.v1"),n?.manifest_schema_version!=="aiwg.fortemi.index.chunk-manifest.v1"&&r.push("manifest_schema_version must be aiwg.fortemi.index.chunk-manifest.v1"),C(n?.offset)||r.push("offset must be a non-negative integer"),Array.isArray(n?.items)||r.push("items must be an array"),t&&C(n?.offset)&&n.offset!==t.offset&&r.push("offset must match manifest part offset "+t.offset),t&&Array.isArray(n?.items)&&n.items.length!==t.count&&r.push("items length must match manifest part count "+t.count),Array.isArray(n?.items))if(i?.projection)r.push(...Pe(n.items,i.source_export_schema_version??"aiwg.fortemi.index.export.v1").map(s=>"items."+s));else {let s=oe({schema_version:i?.source_export_schema_version??"aiwg.fortemi.index.export.v1",generated_at:i?.generated_at??"1970-01-01T00:00:00.000Z",source:i?.source??{repo:"chunk",privacy:"public"},...(i?.source_export_schema_version??"aiwg.fortemi.index.export.v1")==="aiwg.fortemi.index.export.v2"?{compatibility:{previous_schema_version:"aiwg.fortemi.index.export.v1",strategy:"supported"}}:{},items:n.items});r.push(...s.errors.map(o=>"items."+o));}return {valid:r.length===0,errors:r}}function Oe(e,t,i){let r=Ee(e,t,i);if(!r.valid)throw new Error(`Invalid AIWG Fortemi chunk part:
6
+ `+r.errors.join(`
7
+ `));return e}var X=new Set(["http:","https:","blob:","data:"]);function Me(e){try{return new URL(e)}catch{return null}}function ae(e,t){if(t===void 0){let n=Me(e);if(n&&!X.has(n.protocol))throw new Error("Refusing AIWG index fetch with disallowed scheme: "+n.protocol);return e}let i=new URL(t),r=new URL(e,i);if(!X.has(r.protocol))throw new Error("Refusing AIWG index fetch with disallowed scheme: "+r.protocol);if(r.origin!==i.origin)throw new Error("Refusing cross-origin AIWG index fetch: "+r.origin+" != "+i.origin);return r.toString()}function xt(e){return async t=>{let i=ae(t.href,e),r=await fetch(i);if(!r.ok)throw new Error("Failed to fetch AIWG index chunk "+i+": "+r.status);return r.json()}}function je(e,t="base64url"){if(t==="uri")return encodeURIComponent(e);let i=new TextEncoder().encode(e),r="";for(let n of i)r+=String.fromCharCode(n);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function ce(e,t){return e.href.replace("{id}",je(t,e.encoding??"uri"))}function bt(e){return async(t,i)=>{if(!i.detail?.href)throw new Error("Manifest has no detail.href for record resolution");let r=ce(i.detail,t),n=ae(r,e),s=await fetch(n);if(!s.ok)throw new Error("Failed to fetch AIWG index detail "+n+": "+s.status);return s.json()}}function de(e){let t=Object.create(null);for(let i of e){M(t,"type",i.type),M(t,"privacy",i.privacy.classification);for(let r of i.tags)M(t,"tag",r);for(let r of i.concepts)M(t,"concept",r);for(let[r,n]of Object.entries(i.facets))for(let s of n)M(t,r,s);}return t}function E(e){return e.title??e.search?.title??e.search?.name??e.id}function j(e){let t=e.text??e.search?.body??e.search?.summary??e.chunks?.map(r=>r.text??r.body??r.summary??"").filter(Boolean).join(`
8
+ `)??"",i=ue(e);return [t,i].filter(Boolean).join(`
9
+ `)}function ue(e){return "binary_sources"in e?e.binary_sources?.map(t=>t.extracted_text).filter(Boolean).join(`
10
+ `)??"":""}function De(e,t){let i=E(e),r=j(e);return t==="title-summary"?[i,e.search?.summary??"",ue(e)].filter(Boolean).join(`
11
+ `):[i,r].filter(Boolean).join(`
12
+ `)}function Le(e){return e instanceof Date?e.toISOString():e??new Date().toISOString()}var Qe=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function _(e,t){return e>>>t|e<<32-t}function Te(e){let t=e.length*8,i=new Uint8Array(e.length+9+63>>6<<6);i.set(e),i[e.length]=128;let r=new DataView(i.buffer);r.setUint32(i.length-4,t>>>0),r.setUint32(i.length-8,Math.floor(t/4294967296));let n=1779033703,s=3144134277,o=1013904242,a=2773480762,c=1359893119,g=2600822924,u=528734635,f=1541459225,d=new Uint32Array(64);for(let m=0;m<i.length;m+=64){for(let w=0;w<16;w++)d[w]=r.getUint32(m+w*4);for(let w=16;w<64;w++){let p=_(d[w-15],7)^_(d[w-15],18)^d[w-15]>>>3,F=_(d[w-2],17)^_(d[w-2],19)^d[w-2]>>>10;d[w]=d[w-16]+p+d[w-7]+F>>>0;}let y=n,b=s,x=o,A=a,v=c,R=g,k=u,I=f;for(let w=0;w<64;w++){let p=_(v,6)^_(v,11)^_(v,25),F=v&R^~v&k,O=I+p+F+Qe[w]+d[w]>>>0,W=_(y,2)^_(y,13)^_(y,22),G=y&b^y&x^b&x,D=W+G>>>0;I=k,k=R,R=v,v=A+O>>>0,A=x,x=b,b=y,y=O+D>>>0;}n=n+y>>>0,s=s+b>>>0,o=o+x>>>0,a=a+A>>>0,c=c+v>>>0,g=g+R>>>0,u=u+k>>>0,f=f+I>>>0;}return [n,s,o,a,c,g,u,f].map(m=>m.toString(16).padStart(8,"0")).join("")}function qe(e){return `sha256:${Te(e)}`}async function vt(e,t){q(e);let i=t.granularity??"body",r=ie(t.records??e.items,t.privacy),n=[];for(let o of r){let a=t.textForRecord?.(o)??De(o,i),c=await t.backend.embed(a,o);if(c.length!==t.backend.dimensions)throw new Error(`Embedding for ${o.id} has ${c.length} dimensions; expected ${t.backend.dimensions}`);n.push({record_id:o.id,embedding:c,granularity:i,input_hash:qe(new TextEncoder().encode(a)),source_path:o.source.path});}let s={schema_version:"aiwg.fortemi.embedding.set.v1",id:t.id,model:t.backend.model,dimensions:t.backend.dimensions,generated_at:Le(t.generatedAt),granularity:i,...t.metric?{metric:t.metric}:{},input_hash_algorithm:"sha256",embeddings:n};return Y(s),s}function We(e){let t=e.search,i=[e.id,E(e),j(e),t?.title,t?.name,t?.summary,t?.body,t?.capability,t?.phase,t?.type,...t?.triggers??[],...t?.aliases??[],...t?.tags??[],...(e.chunks??[]).flatMap(r=>[r.text,r.body,r.summary,r.source_path])];if(t?.frontmatter)for(let r of Object.values(t.frontmatter))typeof r=="string"?i.push(r):Array.isArray(r)&&i.push(...r.filter(n=>typeof n=="string"));return i.filter(r=>typeof r=="string"&&r.length>0)}function Rt(e,t={}){q(e);let i=S(t.partSize)?t.partSize:500,r=t.projection,n=t.idEncoding??"base64url",s=t.detailHref??"detail/{id}.json",o=ie(e.items,t.privacy),a=d=>String(d).padStart(4,"0"),c=d=>{if(!r)return d;let m={};for(let y of r)m[y]=d[y];return m},g=[],u=[];for(let d=0,m=0;d<o.length;d+=i,m+=1){let y=o.slice(d,d+i),b="part-"+a(m)+".json";g.push({href:b,part:{schema_version:"aiwg.fortemi.index.chunk.v1",manifest_schema_version:"aiwg.fortemi.index.chunk-manifest.v1",offset:d,items:y.map(c)}}),u.push({href:b,offset:d,count:y.length});}return {manifest:{schema_version:"aiwg.fortemi.index.chunk-manifest.v1",generated_at:t.generatedAt??e.generated_at,source:e.source,source_export_schema_version:e.schema_version,total:o.length,part_size:i,facets:de(o),parts:u,...r?{projection:r,detail:{href:s,encoding:n}}:{}},parts:g,details:r?o.map(d=>({id:d.id,href:ce({href:s,encoding:n},d.id),record:d})):[]}}function z(e,t){if(!t||t.length===0)return true;let i=new Set(e);return t.every(r=>i.has(r))}function Ge(e,t){return t?Object.entries(t).every(([i,r])=>z(e.facets[i]??[],r)):true}function Ve(e,t){if(!t)return [];let i=[],r=E(e),n=j(e);r.toLowerCase().includes(t)&&i.push({field:"title",value:r}),n.toLowerCase().includes(t)&&i.push({field:"text",value:n});for(let s of e.tags)s.toLowerCase().includes(t)&&i.push({field:"tag",value:s});for(let s of e.concepts)s.toLowerCase().includes(t)&&i.push({field:"concept",value:s});for(let s of We(e))s!==r&&s!==n&&s.toLowerCase().includes(t)&&i.push({field:"text",value:s,score:K.text});return i}var Ne=new Set(["the","a","an","and","or","of","for","to","in","on","with","into","from","is","are","be","i","we","my","it","you","me","us","your","our","this","that","these","those","there","here","some","any","all","also","please","about","how","what","which","where","when","who","why","find","give","show","need","want","looking","look","help","do","does","did","can","could","should","would","will","handle","handles","handling","aiwg","skill","skills","agent","agents","command","commands","rule","rules","flow","flows","workflow","workflows"]);function Q(e){return e.toLowerCase().replace(/[-_\s]+/g," ").trim()}function Be(e){return e.toLowerCase().split(/[^a-z0-9-]+/).filter(t=>t.length>1&&!Ne.has(t))}function N(e,t){return t.flatMap(i=>e.facets[i]??[])}function Z(e,t){e.some(i=>i.field===t.field&&i.value===t.value&&i.reason===t.reason)||e.push(t);}function ze(e,t){if(e===t)return true;if(Math.abs(e.length-t.length)>1)return false;if(e.length===t.length){let a=-1,c=0;for(let g=0;g<e.length;g+=1)e[g]!==t[g]&&(a<0&&(a=g),c+=1);return c===1?true:c===2&&a+1<e.length&&e[a]===t[a+1]&&e[a+1]===t[a]}let i=e.length<t.length?e:t,r=e.length<t.length?t:e,n=0,s=0,o=0;for(;n<i.length&&s<r.length;)if(i[n]===r[s])n+=1,s+=1;else {if(o+=1,o>1)return false;s+=1;}return true}function Ue(e,t){let i=Q(e).split(/\s+/).filter(Boolean),r=Q(t).split(/\s+/).filter(Boolean);return i.length!==r.length?false:i.every((n,s)=>{let o=r[s];return n===o?true:n.length<5||o.length<5?false:ze(n,o)})}function $e(e){return e.map(t=>t.toLowerCase())}function He(e,t,i={}){if(!t)return [];let r=[],n=Be(t),s=n.length>0?n.join(" "):t.toLowerCase().trim(),o=t.toLowerCase().trim(),a=e.id.split(/[:/]/),c=[e.id,E(e),e.search?.name,e.search?.title,...e.search?.aliases??[],...a,...N(e,["name","canonical_name","command","skill","agent","rule"])].filter(p=>l(p)),g=[...N(e,["trigger","triggers","trigger_phrase","trigger_phrases"]),...e.search?.triggers??[]],u=[...N(e,["capability","capabilities","summary","description"]),e.search?.capability,e.search?.summary,e.search?.phase,e.search?.type,...e.search?.tags??[],...e.concepts,...e.tags].filter(p=>l(p)),f=[e.source?.path,e.source?.repo_relative_path,e.source?.locator].filter(p=>l(p)),d=E(e),m=[e.search?.summary,j(e)].filter(p=>l(p)).join(`
13
+ `),y=e.search?.type??e.type,b=[...e.search?.tags??[],...e.tags],x=n.length>1,A=x?i.relaxOverlap?1:Math.ceil(n.length/2):1,v=p=>x&&p>=A,R=p=>Z(r,{...p,score:0}),k=p=>{p>0&&Z(r,{field:"id",value:e.id,score:p,reason:"aiwg discovery score"});};for(let p of c){if(Q(t)===Q(p))return R({field:"id",value:p,reason:"exact canonical name"}),k(1.001),r;if(Ue(t,p))return R({field:"id",value:p,reason:"near canonical name"}),k(.951),r}let I=0,w=(p,F,O,W,G,D,pe=0)=>{let V=F.toLowerCase();if(V.includes(s))I+=W*D,V===s&&(I+=pe),R({field:p,value:F,reason:O});else if(x){let J=n.filter(he=>V.includes(he)).length;v(J)&&(I+=G*D*(J/n.length),R({field:p,value:F,reason:O}));}};for(let p of $e(g))if(p===s||p===o)return R({field:"facet",value:p,reason:"trigger phrase"}),k(1.0008),r;for(let p of g)w("facet",p,"trigger phrase",.25,.06,4);for(let p of u)w("concept",p,"capability overlap",.2,.1,2);w("title",d,"title token overlap",.3,.08,3,.2);for(let p of b)w("tag",p,"tag token overlap",.2,.05,2);m&&w("text",m,"body token overlap",.15,.04,1);for(let p of f)w("source",p,"path overlap",.1,.03,1);return y.toLowerCase().includes(s)&&(I+=.1,R({field:"facet",value:y,reason:"type overlap"})),k(Math.min(I,1)),r}function Ke(e,t){return e.reduce((i,r)=>i+(r.score??t[r.field]),0)}function Ye(e,t,i){let r=Math.max(20,i);if(!e)return "";if(!t)return e.length>r?`${e.slice(0,r).trimEnd()}...`:e;let s=e.toLowerCase().indexOf(t);if(s<0)return e.length>r?`${e.slice(0,r).trimEnd()}...`:e;let o=Math.max(0,Math.floor((r-t.length)/2)),a=Math.max(0,s-o),c=Math.min(e.length,a+r),g=a>0?"...":"",u=c<e.length?"...":"";return `${g}${e.slice(a,c).trim()}${u}`}function Je(e,t,i,r){let n=t.find(a=>a.field==="text"),s=t.find(a=>a.field==="title"),o=n??s??t[0];return Ye(o?.value??j(e),i,r)}function U(e,t,i,r=0,n={}){let s={...K,...i.weights},o=i.searchProfile??"default";return e.map((a,c)=>({item:a,ordinal:r+c,matches:o==="aiwg-discovery"?He(a,t,n):Ve(a,t)})).filter(({item:a,matches:c})=>!(t&&c.length===0||i.types&&!i.types.includes(a.type)||i.privacy&&!i.privacy.includes(a.privacy.classification)||!z(a.tags,i.tags)||!z(a.concepts,i.concepts)||!Ge(a,i.facets)||i.relationshipTargetId&&!(a.relationships??[]).some(g=>g.target_id===i.relationshipTargetId))).map(({item:a,ordinal:c,matches:g})=>({item:a,ordinal:c,rank:Ke(g,s),matches:g}))}function Xe(e,t){return [...e].sort((i,r)=>t&&r.rank-i.rank||i.ordinal-r.ordinal)}function T(e,t,i){let r=Xe(e,i.rank),n=i.offset??0,s=i.limit??r.length,o=r.slice(n,n+s),a={items:o.map(c=>c.item),total:r.length,facets:de(r.map(c=>c.item))};if(i.rank||i.snippets||i.includeMatches){let c=i.snippetLength??160;a.rankedItems=o.map(g=>({item:g.item,rank:g.rank,...i.snippets?{snippet:Je(g.item,g.matches,t,c)}:{},...i.includeMatches?{matches:g.matches}:{}}));}return a}function ge(e,t="",i={}){let r=t.trim().toLowerCase(),n=U(e.items,r,i);return n.length===0&&r&&i.searchProfile==="aiwg-discovery"?T(U(e.items,r,i,0,{relaxOverlap:true}),r,i):T(n,r,i)}function fe(e,t){if(e.length!==t.length||e.length===0)return 0;let i=0,r=0,n=0;for(let s=0;s<e.length;s+=1){let o=e[s],a=t[s];i+=o*a,r+=o*o,n+=a*a;}return r===0||n===0?0:i/(Math.sqrt(r)*Math.sqrt(n))}function Ze(e){let t=[],i=h(e)?e:{};h(e)||t.push("embedding set must be an object"),i?.schema_version!=="aiwg.fortemi.embedding.set.v1"&&t.push("schema_version must be aiwg.fortemi.embedding.set.v1"),l(i?.id)||t.push("id is required"),l(i?.model)||t.push("model is required"),S(i?.dimensions)||t.push("dimensions must be a positive integer"),l(i?.generated_at)||t.push("generated_at is required"),l(i?.granularity)||t.push("granularity is required"),Array.isArray(i?.embeddings)||t.push("embeddings must be an array");let r=Array.isArray(i.embeddings)?i.embeddings:[];for(let[n,s]of r.entries()){if(!h(s)){t.push("embeddings["+n+"] must be an object");continue}l(s.record_id)||t.push("embeddings["+n+"].record_id is required"),l(s.input_hash)||t.push("embeddings["+n+"].input_hash is required"),Array.isArray(s.embedding)?S(i?.dimensions)&&s.embedding.length!==i.dimensions?t.push("embeddings["+n+"].embedding length must match dimensions"):s.embedding.every(o=>typeof o=="number"&&Number.isFinite(o))||t.push("embeddings["+n+"].embedding must contain finite numbers"):t.push("embeddings["+n+"].embedding must be an array");}return {valid:t.length===0,errors:t}}function Y(e){let t=Ze(e);if(!t.valid)throw new Error(`Invalid AIWG Fortemi embedding set:
14
+ `+t.errors.join(`
15
+ `));return e}function et(e,t,i,r={}){if(Y(t),i.length!==t.dimensions)throw new Error("query embedding length must match embedding set dimensions");let n=new Map(e.items.map(a=>[a.id,a])),s=r.offset??0,o=r.limit??20;return t.embeddings.map(a=>{let c=n.get(a.record_id);return c?{item:c,embedding:a,score:fe(i,a.embedding)}:null}).filter(a=>a!==null&&a.score>=(r.minScore??-1)).sort((a,c)=>c.score-a.score||a.item.id.localeCompare(c.item.id)).slice(s,s+o)}function kt(e,t,i,r,n={}){let s={...n};delete s.limit,delete s.offset;let o=ge(e,i,{...s,rank:true}),a=et(e,t,r,{limit:e.items.length}),c=n.lexicalWeight??.5,g=n.semanticWeight??.5,u=new Map(o.rankedItems?.map(A=>[A.item.id,A.rank])??[]),f=Math.max(1,...u.values()),d=new Map(a.flatMap(A=>A.embedding?[[A.item.id,A.embedding]]:[])),m=new Map(a.map(A=>[A.item.id,A.score])),y=new Set([...u.keys(),...m.keys()]),b=n.offset??0,x=n.limit??20;return [...y].map(A=>{let v=e.items.find(k=>k.id===A),R=d.get(A);return v?{item:v,...R?{embedding:R}:{},score:(u.get(A)??0)/f*c+(m.get(A)??0)*g}:null}).filter(A=>A!==null&&A.score>=(n.minScore??-1)).sort((A,v)=>v.score-A.score||A.item.id.localeCompare(v.item.id)).slice(b,b+x)}var tt=5e3;function _t(e,t,i=.9,r){Y(t);let n=r?.maxEmbeddings??tt;if(t.embeddings.length>n)throw new Error("Embedding set too large for duplicate scan: "+t.embeddings.length+" > "+n+" (raise options.maxEmbeddings to override for trusted input)");let s=new Map(e.items.map(a=>[a.id,a])),o=[];for(let a=0;a<t.embeddings.length;a+=1)for(let c=a+1;c<t.embeddings.length;c+=1){let g=t.embeddings[a],u=t.embeddings[c],f=s.get(g.record_id),d=s.get(u.record_id);if(!f||!d)continue;let m=fe(g.embedding,u.embedding);m>=i&&o.push({left:f,right:d,score:m});}return o.sort((a,c)=>c.score-a.score||a.left.id.localeCompare(c.left.id))}function it(e){return `${e.offset}:${e.href}`}function rt(e){return S(e)?e:3}function nt(e){return S(e)?e:32}function st(e){return S(e)?e:5e3}function ot(e,t){return JSON.stringify({q:e,types:t.types??null,facets:t.facets??null,tags:t.tags??null,concepts:t.concepts??null,privacy:t.privacy??null,rel:t.relationshipTargetId??null,searchProfile:t.searchProfile??"default",weights:{...K,...t.weights}})}function at(e,t,i){e.matchCache.delete(t),e.matchCache.set(t,i);let r=0;for(let n of e.matchCache.values())r+=n.length;for(;r>e.maxCachedMatches&&e.matchCache.size>1;){let n=e.matchCache.keys().next().value;if(n===void 0||n===t)break;r-=e.matchCache.get(n)?.length??0,e.matchCache.delete(n);}}function ct(e,t){return e.trim()===""&&!t.rank&&!t.snippets&&!t.includeMatches&&!t.types&&!t.facets&&!t.tags&&!t.concepts&&!t.privacy&&!t.relationshipTargetId}function dt(e,t,i){let r=t+i;return e.parts.filter(n=>n.count>0&&n.offset<r&&n.offset+n.count>t)}async function $(e,t){let i=it(t),r=e.partCache.get(i);if(r)return e.partCache.delete(i),e.partCache.set(i,r),{part:r,fetched:false};let n=Oe(await e.loader(t,e.manifest),t,e.manifest);for(e.partCache.set(i,n);e.partCache.size>e.maxCachedParts;){let s=e.partCache.keys().next().value;if(s===void 0)break;e.partCache.delete(s);}return {part:n,fetched:true}}async function le(e,t){let i=e.detailCache.get(t);if(i)return e.detailCache.delete(t),e.detailCache.set(t,i),i;if(!e.manifest.projection)for(let s of e.partCache.values()){let o=s.items.find(a=>a.id===t);if(o)return o}if(!e.detailLoader)throw new Error("No detailLoader configured to resolve record "+t);let r=await e.detailLoader(t,e.manifest),n=q({schema_version:"aiwg.fortemi.index.export.v1",generated_at:e.manifest.generated_at,source:e.manifest.source,items:[r]}).items[0];if(n.id!==t)throw new Error("Detail record id mismatch: expected "+t+", got "+n.id);for(e.detailCache.set(t,n);e.detailCache.size>e.maxCachedDetails;){let s=e.detailCache.keys().next().value;if(s===void 0)break;e.detailCache.delete(s);}return n}function ut(e){return e?.relationshipType??e?.type}function gt(e,t){return {source_id:e,target_id:t.target_id,type:t.type,...t.source_path?{source_path:t.source_path}:{},...t.target_path?{target_path:t.target_path}:{},...t.direction?{direction:t.direction}:{}}}function ft(e,t={}){let i=ut(t);return !(i&&e.type!==i||t.relationshipDirection&&e.direction!==t.relationshipDirection||t.sourceId&&e.source_id!==t.sourceId||t.targetId&&e.target_id!==t.targetId||t.endpointId&&e.source_id!==t.endpointId&&e.target_id!==t.endpointId)}function lt(e){if(e.direction==="out"&&!e.sourceId)throw new Error("relationship direction 'out' requires sourceId");if(e.direction==="in"&&!e.targetId)throw new Error("relationship direction 'in' requires targetId")}function mt(e){return {id:e.id,type:e.type,title:E(e)}}function ee(e,t){t&&e.set(t.id,mt(t));}function H(e,t={}){lt(t);let i=new Map(e.map(a=>[a.id,a])),r=[];for(let a of e)for(let c of a.relationships??[]){let g=gt(a.id,c);ft(g,t)&&r.push(g);}let n=r.sort((a,c)=>a.source_id.localeCompare(c.source_id)||a.target_id.localeCompare(c.target_id)||a.type.localeCompare(c.type)),s=t.limit?n.slice(0,t.limit):n,o=new Map;for(let a of s)ee(o,i.get(a.source_id)),ee(o,i.get(a.target_id));return {nodes:[...o.values()].sort((a,c)=>a.id.localeCompare(c.id)),edges:s,complete:true}}function pt(e,t={}){let i=t.direction??"both";return {...t,...i==="out"?{sourceId:e}:{},...i==="in"?{targetId:e}:{},...i==="both"?{endpointId:e}:{}}}function ht(e,t,i={}){let r=i.direction??"both",n=t.edges.filter(o=>r==="out"?o.source_id===e:r==="in"?o.target_id===e:o.source_id===e||o.target_id===e),s=new Set;for(let o of n)s.add(o.source_id),s.add(o.target_id);return {...t,edges:n,nodes:t.nodes.filter(o=>s.has(o.id))}}async function me(e,t){let i=0,r=0,n=[];for(let s of e.manifest.parts){let o=await $(e,s);o.fetched&&(r+=1),i+=1,t?.({phase:"part",done:i,total:e.manifest.parts.length,href:s.href});for(let a of o.part.items)n.push(a.relationships?a:await le(e,a.id));}return {records:n,scannedParts:i,fetchedParts:r}}async function te(e,t={}){let i=await me(e);return {...H(i.records,t),scannedParts:i.scannedParts,fetchedParts:i.fetchedParts}}async function wt(e,t="",i={}){let r=t.trim().toLowerCase(),n=0,s=0;if(ct(t,i)){let g=i.offset??0,u=i.limit??e.manifest.total,f=dt(e.manifest,g,u),d=[];for(let m of f){let y=await $(e,m);y.fetched&&(s+=1),n+=1,i.onProgress?.({phase:"part",done:n,total:f.length,href:m.href});let b=Math.max(0,g-m.offset),x=Math.min(y.part.items.length,g+u-m.offset);d.push(...y.part.items.slice(b,x));}return {items:d,total:e.manifest.total,facets:e.manifest.facets??{},manifestTotal:e.manifest.total,scannedParts:n,fetchedParts:s,complete:true}}let o=ot(r,i),a=e.matchCache.get(o);if(a)return e.matchCache.delete(o),e.matchCache.set(o,a),{...T(a,r,i),manifestTotal:e.manifest.total,scannedParts:0,fetchedParts:0,complete:true};let c=[];for(let g of e.manifest.parts){let u=await $(e,g);u.fetched&&(s+=1),n+=1,i.onProgress?.({phase:"part",done:n,total:e.manifest.parts.length,href:g.href}),c.push(...U(u.part.items,r,i,g.offset)),i.onProgress?.({phase:"query",done:n,total:e.manifest.parts.length,href:g.href});}return at(e,o,c),{...T(c,r,i),manifestTotal:e.manifest.total,scannedParts:n,fetchedParts:s,complete:true}}function yt(e,t,i=new Date().toISOString()){return {schema_version:"aiwg.fortemi.review-decisions.v1",generated_at:i,source_export_schema_version:e.schema_version,decisions:[...t].sort((r,n)=>r.item_id.localeCompare(n.item_id))}}function It(e){let t=e??null,i=null,r=null,n=null,s=[],o=new Set,a=()=>({index:t,chunked:i?{manifest:i.manifest,cachedParts:i.partCache.size,maxCachedParts:i.maxCachedParts}:null,data:r,error:n,reviewDecisions:[...s]}),c=()=>{let u=a();for(let f of o)f(u);},g=()=>{if(!t)throw new Error("No AIWG index export loaded");return t};return {loadIndex(u){try{let f=q(u);return t=f,i=null,r=null,s=[],n=null,c(),f}catch(f){throw n=f instanceof Error?f:new Error(String(f)),c(),n}},loadChunkedIndex(u,f,d={}){try{let m=Fe(u);return t=null,i={manifest:m,loader:f,maxCachedParts:rt(d.maxCachedParts),partCache:new Map,detailLoader:d.detailLoader,maxCachedDetails:nt(d.maxCachedDetails),detailCache:new Map,maxCachedMatches:st(d.maxCachedMatches),matchCache:new Map},r=null,s=[],n=null,c(),m}catch(m){throw n=m instanceof Error?m:new Error(String(m)),c(),n}},getIndex(){return t},getChunkedManifest(){return i?.manifest??null},getSnapshot(){return a()},query(u="",f){let d=ge(g(),u,f);return r=d,n=null,c(),d},async queryChunked(u="",f){if(!i)throw new Error("No AIWG chunked index manifest loaded");try{let d=await wt(i,u,f);return r=d,n=null,c(),d}catch(d){throw n=d instanceof Error?d:new Error(String(d)),c(),n}},async getRecord(u){if(i)try{return await le(i,u)}catch(d){throw n=d instanceof Error?d:new Error(String(d)),c(),n}let f=g().items.find(d=>d.id===u);if(!f)throw new Error("Record not found: "+u);return f},async neighbors(u,f){try{let d=pt(u,f),m=i?await te(i,d):H(g().items,d);return ht(u,m,f)}catch(d){throw n=d instanceof Error?d:new Error(String(d)),c(),n}},async relationshipQuery(u){try{return i?await te(i,u):H(g().items,u)}catch(f){throw n=f instanceof Error?f:new Error(String(f)),c(),n}},async relationshipSet(u){let[f,d]=await Promise.all([this.neighbors(u.a,u),this.neighbors(u.b,u)]),m=new Set(f.nodes.map(x=>x.id).filter(x=>x!==u.a)),y=new Set(d.nodes.map(x=>x.id).filter(x=>x!==u.b)),b;return u.op==="intersection"?b=[...m].filter(x=>y.has(x)):u.op==="difference"?b=[...m].filter(x=>!y.has(x)):b=[...new Set([...m,...y])],{op:u.op,ids:b.sort()}},clearChunkCache(){i?.partCache.clear(),i?.detailCache.clear(),i?.matchCache.clear(),n=null,c();},toCommunityGraph(u){return B(g(),u)},async toCommunityGraphChunked(u){if(!i)return B(g(),u);let f=await me(i,u?.onProgress);return B({generated_at:i.manifest.generated_at,source:i.manifest.source,items:f.records},u)},setReviewDecision(u){let f={...u,updated_at:new Date().toISOString()};return s=[...s.filter(d=>d.item_id!==f.item_id),f].sort((d,m)=>d.item_id.localeCompare(m.item_id)),n=null,c(),f},clearReviewDecision(u){s=s.filter(f=>f.item_id!==u),n=null,c();},createReviewDecisionExport(u){let f=t??(i?{schema_version:i.manifest.source_export_schema_version??"aiwg.fortemi.index.export.v1"}:null);if(!f)throw new Error("No AIWG index export or chunked manifest loaded");return yt(f,s,u)},subscribe(u){return o.add(u),()=>{o.delete(u);}}}}function B(e,t={}){let i=new Set(e.items.map(o=>o.id)),r=t.relationshipWeights??Object.create(null),n=new Map;for(let o of e.items)for(let a of o.relationships){if(!i.has(a.target_id)&&!t.includeDanglingRelationships)continue;let c=a.type,g=Object.prototype.hasOwnProperty.call(r,c)?r[c]:void 0,u=typeof g=="number"&&Number.isFinite(g)?g:1,f=`${o.id}\0${a.target_id}\0${c}`,d=n.get(f);d?d.weight+=u:n.set(f,{source:o.id,target:a.target_id,kind:c,weight:u});}let s=new Map;for(let o of e.items){let a=At(o,t);for(let c of a){let g=s.get(c)??[];g.push(o.id),s.set(c,g);}}return {nodes:e.items.map(o=>({id:o.id})),edges:Array.from(n.values()).sort((o,a)=>o.source.localeCompare(a.source)||o.target.localeCompare(a.target)||o.kind.localeCompare(a.kind)),communities:Array.from(s.entries()).map(([o,a])=>({id:o,nodes:[...new Set(a)].sort()})).sort((o,a)=>o.id.localeCompare(a.id))}}function At(e,t){if(t.communityFacet){let i=e.facets[t.communityFacet]??[];if(i.length>0)return i.map(r=>`${t.communityFacet}:${r}`)}if(t.communityTagPrefix){let i=t.communityTagPrefix,r=e.tags.filter(n=>n.startsWith(i));if(r.length>0)return r}return e.concepts.length>0?e.concepts.map(i=>`concept:${i}`):[`type:${e.type}`]}export{we as AIWG_SCAN_REQUIRED_FIELDS,tt as DEFAULT_AIWG_DUPLICATE_SCAN_MAX_EMBEDDINGS,ce as aiwgDetailHrefForId,B as aiwgFortemiIndexToCommunityGraph,Fe as assertAiwgFortemiChunkManifest,Oe as assertAiwgFortemiChunkPart,q as assertAiwgFortemiIndexExport,Y as assertAiwgStaticEmbeddingSet,Rt as buildAiwgChunkedIndex,vt as buildAiwgStaticEmbeddingSet,xt as createAiwgFetchChunkLoader,bt as createAiwgFetchDetailLoader,It as createAiwgIndexController,yt as createAiwgReviewDecisionExport,je as encodeAiwgDetailId,ie as filterAiwgRecordsByPrivacy,_t as findAiwgStaticDuplicatePairs,de as getAiwgFortemiFacets,ge as queryAiwgFortemiIndex,kt as queryAiwgHybridIndex,et as queryAiwgSemanticIndex,ae as resolveAiwgFetchUrl,Ce as validateAiwgFortemiChunkManifest,Ee as validateAiwgFortemiChunkPart,oe as validateAiwgFortemiIndexExport,Ze as validateAiwgStaticEmbeddingSet};//# sourceMappingURL=aiwg-index.js.map
1464
16
  //# sourceMappingURL=aiwg-index.js.map