@fortemi/core 2026.7.1 → 2026.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -227,17 +227,31 @@ scan projection.
227
227
  Static semantic search uses an optional sidecar contract:
228
228
 
229
229
  ```ts
230
- import { queryAiwgHybridIndex } from '@fortemi/core/aiwg-index'
230
+ import { buildAiwgStaticEmbeddingSet, queryAiwgHybridIndex } from '@fortemi/core/aiwg-index'
231
231
 
232
232
  const embeddingSet = await fetch('/search/aiwg-index/embeddings.json').then((res) => res.json())
233
233
  const queryEmbedding = await hostEmbed('workspace health check')
234
234
  const semanticResults = queryAiwgHybridIndex(index, embeddingSet, 'health check', queryEmbedding)
235
+
236
+ // Node/CLI generation path. Supply any Node-safe model backend; @fortemi/core
237
+ // only writes the shared embedding-set graph format and does not require DOM or WebGL.
238
+ const cliEmbeddingSet = await buildAiwgStaticEmbeddingSet(index, {
239
+ id: 'aiwg-cli-embeddings',
240
+ backend: {
241
+ model: 'local-node-embedder',
242
+ dimensions: 384,
243
+ embed: async (input) => nodeEmbed(input),
244
+ },
245
+ })
235
246
  ```
236
247
 
237
248
  `aiwg.fortemi.embedding.set.v1` records the model, dimensions, granularity
238
249
  (`title-summary`, `body`, `chunked-body`, or a project value), source record IDs,
239
250
  and per-vector input hashes. Hosts provide query embeddings; `@fortemi/core` does
240
- not add a model runtime dependency for static AIWG search.
251
+ not add a model runtime dependency for static AIWG search. The builder uses the
252
+ same record text projection as static search, including attachment
253
+ `extracted_text`, and records only attachment metadata references, never raw blob
254
+ bytes.
241
255
 
242
256
  ## What You Get
243
257
 
@@ -94,6 +94,17 @@ interface AiwgFortemiRecordEmbedding {
94
94
  source_path?: string;
95
95
  metadata?: Record<string, unknown>;
96
96
  }
97
+ interface AiwgFortemiAttachmentReference {
98
+ id: string;
99
+ path: string;
100
+ mime: string | null;
101
+ checksum: string;
102
+ bytes: number;
103
+ }
104
+ interface AiwgFortemiBinarySource {
105
+ extracted_text: string;
106
+ attachment: AiwgFortemiAttachmentReference;
107
+ }
97
108
  interface AiwgFortemiRecord {
98
109
  schema_version: AiwgFortemiRecordSchemaVersion;
99
110
  id: string;
@@ -108,6 +119,7 @@ interface AiwgFortemiRecord {
108
119
  provenance: AiwgFortemiProvenance[];
109
120
  search?: AiwgFortemiSearchProjection;
110
121
  chunks?: AiwgFortemiChunk[];
122
+ binary_sources?: AiwgFortemiBinarySource[];
111
123
  embeddings?: AiwgFortemiRecordEmbedding[];
112
124
  compatibility?: Record<string, unknown>;
113
125
  /** Optional rich SKOS metadata for static consumers that need labels/definitions without opening a shard. */
@@ -321,6 +333,20 @@ interface AiwgStaticEmbeddingSet {
321
333
  input_hash_algorithm?: string;
322
334
  embeddings: AiwgStaticEmbeddingRecord[];
323
335
  }
336
+ interface AiwgHeadlessEmbeddingBackend {
337
+ model: string;
338
+ dimensions: number;
339
+ embed(input: string, record: AiwgFortemiRecord): number[] | Promise<number[]>;
340
+ }
341
+ interface BuildAiwgStaticEmbeddingSetOptions {
342
+ id: string;
343
+ backend: AiwgHeadlessEmbeddingBackend;
344
+ records?: AiwgFortemiRecord[];
345
+ generatedAt?: string | Date;
346
+ granularity?: AiwgStaticEmbeddingSet['granularity'];
347
+ metric?: AiwgStaticEmbeddingSet['metric'];
348
+ textForRecord?: (record: AiwgFortemiRecord) => string;
349
+ }
324
350
  interface AiwgStaticSemanticQueryOptions {
325
351
  limit?: number;
326
352
  offset?: number;
@@ -390,6 +416,7 @@ declare function encodeAiwgDetailId(id: string, encoding?: AiwgDetailIdEncoding)
390
416
  declare function aiwgDetailHrefForId(detail: AiwgFortemiChunkDetailRef, id: string): string;
391
417
  declare function createAiwgFetchDetailLoader(baseUrl?: string | URL): AiwgChunkedIndexDetailLoader;
392
418
  declare function getAiwgFortemiFacets(items: AiwgFortemiRecord[]): Record<string, Record<string, number>>;
419
+ declare function buildAiwgStaticEmbeddingSet(index: AiwgFortemiIndexExport, options: BuildAiwgStaticEmbeddingSetOptions): Promise<AiwgStaticEmbeddingSet>;
393
420
  interface AiwgChunkedIndexBuildOptions {
394
421
  partSize?: number;
395
422
  projection?: Array<keyof AiwgFortemiRecord>;
@@ -434,4 +461,4 @@ declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport,
434
461
  }[];
435
462
  };
436
463
 
437
- export { AIWG_SCAN_REQUIRED_FIELDS, type AiwgChunkedIndexBuildOptions, type AiwgChunkedIndexBuildResult, type AiwgChunkedIndexDetailLoader, type AiwgChunkedIndexLoadOptions, type AiwgChunkedIndexLoader, type AiwgChunkedIndexProgress, type AiwgChunkedIndexProgressPhase, type AiwgChunkedIndexQueryOptions, type AiwgChunkedIndexQueryResult, type AiwgChunkedIndexValidationResult, type AiwgDetailIdEncoding, type AiwgFortemiChunk, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiIndexExportSchemaVersion, type AiwgFortemiKnownRecordType, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiProvenanceEvent, type AiwgFortemiRecord, type AiwgFortemiRecordEmbedding, type AiwgFortemiRecordSchemaVersion, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgFortemiRelationshipDirection, type AiwgFortemiSearchProjection, type AiwgFortemiSkosConcept, type AiwgFortemiSkosRelation, type AiwgFortemiSkosRelationType, type AiwgIndexController, type AiwgIndexControllerListener, type AiwgIndexControllerSnapshot, type AiwgIndexGraphOptions, type AiwgIndexQueryMatch, type AiwgIndexQueryOptions, type AiwgIndexQueryRankedItem, type AiwgIndexQueryResult, type AiwgIndexQueryWeights, type AiwgIndexValidationResult, type AiwgPrivacyClassification, type AiwgProvenanceConfidence, type AiwgRelationshipDirection, type AiwgRelationshipEdgeSummary, type AiwgRelationshipNodeSummary, type AiwgRelationshipQueryOptions, type AiwgRelationshipSetOperation, type AiwgRelationshipSetOptions, type AiwgRelationshipSetResult, type AiwgRelationshipTraversalOptions, type AiwgRelationshipTraversalResult, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, type AiwgStaticDuplicatePair, type AiwgStaticEmbeddingRecord, type AiwgStaticEmbeddingSet, type AiwgStaticHybridQueryOptions, type AiwgStaticSemanticQueryOptions, type AiwgStaticSemanticResult, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
464
+ export { AIWG_SCAN_REQUIRED_FIELDS, type AiwgChunkedIndexBuildOptions, type AiwgChunkedIndexBuildResult, type AiwgChunkedIndexDetailLoader, type AiwgChunkedIndexLoadOptions, type AiwgChunkedIndexLoader, type AiwgChunkedIndexProgress, type AiwgChunkedIndexProgressPhase, type AiwgChunkedIndexQueryOptions, type AiwgChunkedIndexQueryResult, type AiwgChunkedIndexValidationResult, type AiwgDetailIdEncoding, type AiwgFortemiAttachmentReference, type AiwgFortemiBinarySource, type AiwgFortemiChunk, type AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiIndexExportSchemaVersion, type AiwgFortemiKnownRecordType, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiProvenanceEvent, type AiwgFortemiRecord, type AiwgFortemiRecordEmbedding, type AiwgFortemiRecordSchemaVersion, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, type AiwgFortemiRelationshipDirection, type AiwgFortemiSearchProjection, type AiwgFortemiSkosConcept, type AiwgFortemiSkosRelation, type AiwgFortemiSkosRelationType, type AiwgHeadlessEmbeddingBackend, type AiwgIndexController, type AiwgIndexControllerListener, type AiwgIndexControllerSnapshot, type AiwgIndexGraphOptions, type AiwgIndexQueryMatch, type AiwgIndexQueryOptions, type AiwgIndexQueryRankedItem, type AiwgIndexQueryResult, type AiwgIndexQueryWeights, type AiwgIndexValidationResult, type AiwgPrivacyClassification, type AiwgProvenanceConfidence, type AiwgRelationshipDirection, type AiwgRelationshipEdgeSummary, type AiwgRelationshipNodeSummary, type AiwgRelationshipQueryOptions, type AiwgRelationshipSetOperation, type AiwgRelationshipSetOptions, type AiwgRelationshipSetResult, type AiwgRelationshipTraversalOptions, type AiwgRelationshipTraversalResult, type AiwgReviewAction, type AiwgReviewDecision, type AiwgReviewDecisionExport, type AiwgReviewInput, type AiwgStaticDuplicatePair, type AiwgStaticEmbeddingRecord, type AiwgStaticEmbeddingSet, type AiwgStaticHybridQueryOptions, type AiwgStaticSemanticQueryOptions, type AiwgStaticSemanticResult, type BuildAiwgStaticEmbeddingSetOptions, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
@@ -1,3 +1,12 @@
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
+
1
10
  // src/aiwg-index.ts
2
11
  var AIWG_SCAN_REQUIRED_FIELDS = [
3
12
  "schema_version",
@@ -392,7 +401,55 @@ function recordTitle(item) {
392
401
  return item.title ?? item.search?.title ?? item.search?.name ?? item.id;
393
402
  }
394
403
  function recordText(item) {
395
- return item.text ?? item.search?.body ?? item.search?.summary ?? item.chunks?.map((chunk) => chunk.text ?? chunk.body ?? chunk.summary ?? "").filter(Boolean).join("\n") ?? "";
404
+ const base = item.text ?? item.search?.body ?? item.search?.summary ?? item.chunks?.map((chunk) => chunk.text ?? chunk.body ?? chunk.summary ?? "").filter(Boolean).join("\n") ?? "";
405
+ const extractedText = "binary_sources" in item ? item.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join("\n") ?? "" : "";
406
+ return [base, extractedText].filter(Boolean).join("\n");
407
+ }
408
+ function defaultEmbeddingInput(record, granularity) {
409
+ const title = recordTitle(record);
410
+ const text = recordText(record);
411
+ if (granularity === "title-summary") {
412
+ const extractedText = record.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join("\n") ?? "";
413
+ return [title, record.search?.summary ?? "", extractedText].filter(Boolean).join("\n");
414
+ }
415
+ return [title, text].filter(Boolean).join("\n");
416
+ }
417
+ function generatedAtString(value) {
418
+ if (value instanceof Date) return value.toISOString();
419
+ return value ?? (/* @__PURE__ */ new Date()).toISOString();
420
+ }
421
+ async function buildAiwgStaticEmbeddingSet(index, options) {
422
+ assertAiwgFortemiIndexExport(index);
423
+ const granularity = options.granularity ?? "body";
424
+ const records = options.records ?? index.items;
425
+ const embeddings = [];
426
+ for (const record of records) {
427
+ const input = options.textForRecord?.(record) ?? defaultEmbeddingInput(record, granularity);
428
+ const embedding = await options.backend.embed(input, record);
429
+ if (embedding.length !== options.backend.dimensions) {
430
+ throw new Error(`Embedding for ${record.id} has ${embedding.length} dimensions; expected ${options.backend.dimensions}`);
431
+ }
432
+ embeddings.push({
433
+ record_id: record.id,
434
+ embedding,
435
+ granularity,
436
+ input_hash: computeHash(new TextEncoder().encode(input)),
437
+ source_path: record.source.path
438
+ });
439
+ }
440
+ const embeddingSet = {
441
+ schema_version: "aiwg.fortemi.embedding.set.v1",
442
+ id: options.id,
443
+ model: options.backend.model,
444
+ dimensions: options.backend.dimensions,
445
+ generated_at: generatedAtString(options.generatedAt),
446
+ granularity,
447
+ ...options.metric ? { metric: options.metric } : {},
448
+ input_hash_algorithm: "sha256",
449
+ embeddings
450
+ };
451
+ assertAiwgStaticEmbeddingSet(embeddingSet);
452
+ return embeddingSet;
396
453
  }
397
454
  function recordSearchValues(item) {
398
455
  const search = item.search;
@@ -1285,6 +1342,6 @@ function communityIdsFor(item, options) {
1285
1342
  return [`type:${item.type}`];
1286
1343
  }
1287
1344
 
1288
- export { AIWG_SCAN_REQUIRED_FIELDS, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
1345
+ export { AIWG_SCAN_REQUIRED_FIELDS, aiwgDetailHrefForId, aiwgFortemiIndexToCommunityGraph, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, encodeAiwgDetailId, findAiwgStaticDuplicatePairs, getAiwgFortemiFacets, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet };
1289
1346
  //# sourceMappingURL=aiwg-index.js.map
1290
1347
  //# sourceMappingURL=aiwg-index.js.map