@fortemi/core 2026.7.0 → 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
@@ -126,10 +126,11 @@ Use this subpath for Pagenary-style command palettes, static docs search, and
126
126
  vanilla JavaScript review surfaces. The top-level `@fortemi/core` export remains
127
127
  available for full archive/runtime integrations.
128
128
 
129
- The record contract keeps the existing flat fields for filtering and search:
130
- `facets`, `tags`, `concepts`, `relationships`, and `provenance`. Static
131
- consumers that render metadata tabs can also read optional rich fields from full
132
- records:
129
+ The AIWG index adapter accepts both `aiwg.fortemi.index.export.v1` and
130
+ `aiwg.fortemi.index.export.v2` envelopes. The v1 record contract keeps the
131
+ existing flat fields for filtering and search: `facets`, `tags`, `concepts`,
132
+ `relationships`, and `provenance`. Static consumers that render metadata tabs
133
+ can also read optional rich fields from full records:
133
134
 
134
135
  - `skos_concepts`: concept ids plus labels, definitions, schemes, notation, URIs,
135
136
  alternate labels, and metadata.
@@ -140,10 +141,14 @@ records:
140
141
  - `relationships[*].metadata`: optional relationship labels, confidence,
141
142
  privacy, and structured metadata.
142
143
 
143
- These fields are additive to `aiwg.fortemi.index.record.v1`. Existing consumers
144
- can ignore them and continue querying the flat fields. Chunked indexes with a
145
- projection keep rich metadata in detail records; call `getRecord(id)` before
146
- rendering a metadata panel.
144
+ These fields are additive to `aiwg.fortemi.index.record.v1`. Version 2 records
145
+ can also carry AIWG migration fields such as `search`, `chunks`, `embeddings`,
146
+ `source.origin`, `source.generated`, `source.checksum`, `source.updated_at`,
147
+ `privacy.locality`, and compatibility metadata. Query helpers use v2 `search`
148
+ and `chunks` text when the old top-level `title`/`text` fields are absent.
149
+ Existing consumers can ignore the v2 fields and continue querying the flat
150
+ fields. Chunked indexes with a projection keep rich metadata in detail records;
151
+ call `getRecord(id)` before rendering a metadata panel.
147
152
 
148
153
  Large static indexes can use the chunked browser path instead of downloading one
149
154
  full `aiwg.fortemi.index.export.v1` file. Host a manifest plus deterministic part
@@ -179,9 +184,11 @@ results, but the controller keeps only a bounded part cache and never sets a
179
184
  materialized full export in `getIndex()`.
180
185
 
181
186
  AIWG records can use project-specific string types such as `aiwg.skill`,
182
- `aiwg.command`, `aiwg.rule`, `aiwg.requirement`, and `research.ref`. Validation
183
- still enforces required record fields, but it does not require unknown AIWG,
184
- research, or documentation domains to collapse into `aiwg.artifact`.
187
+ `aiwg.agent`, `aiwg.command`, `aiwg.rule`, `aiwg.flow`, `aiwg.bundle`,
188
+ `aiwg.kb.page`, `aiwg.memory.entry`, `aiwg.issue`, `research.ref`, and
189
+ `aiwg.research.profile`. Validation still enforces required record fields, but
190
+ it does not require unknown AIWG, research, KB, memory, issue, or documentation
191
+ domains to collapse into `aiwg.artifact`.
185
192
 
186
193
  For AIWG command-palette parity, opt into discovery ranking instead of the
187
194
  default deterministic substring lookup:
@@ -203,29 +210,48 @@ Chunked indexes also support graph navigation without loading a full export:
203
210
  const neighbors = await controller.neighbors('aiwg:requirement:search', {
204
211
  direction: 'both',
205
212
  relationshipType: 'cites',
213
+ relationshipDirection: 'downstream',
206
214
  })
207
215
 
208
216
  const graph = await controller.toCommunityGraphChunked()
209
217
  ```
210
218
 
211
- When scan parts are projected, relationship traversal scans the parts and
212
- lazy-loads detail records only when relationships are not present in the scan
213
- projection.
219
+ Relationship edges preserve v2 `source_path`, `target_path`, and
220
+ `direction: 'upstream' | 'downstream' | 'related'` values. Use
221
+ `direction: 'in' | 'out' | 'both'` for graph orientation around a node and
222
+ `relationshipDirection` for AIWG dependency/citation/profile/KB direction
223
+ semantics. When scan parts are projected, relationship traversal scans the parts
224
+ and lazy-loads detail records only when relationships are not present in the
225
+ scan projection.
214
226
 
215
227
  Static semantic search uses an optional sidecar contract:
216
228
 
217
229
  ```ts
218
- import { queryAiwgHybridIndex } from '@fortemi/core/aiwg-index'
230
+ import { buildAiwgStaticEmbeddingSet, queryAiwgHybridIndex } from '@fortemi/core/aiwg-index'
219
231
 
220
232
  const embeddingSet = await fetch('/search/aiwg-index/embeddings.json').then((res) => res.json())
221
233
  const queryEmbedding = await hostEmbed('workspace health check')
222
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
+ })
223
246
  ```
224
247
 
225
248
  `aiwg.fortemi.embedding.set.v1` records the model, dimensions, granularity
226
249
  (`title-summary`, `body`, `chunked-body`, or a project value), source record IDs,
227
250
  and per-vector input hashes. Hosts provide query embeddings; `@fortemi/core` does
228
- 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.
229
255
 
230
256
  ## What You Get
231
257
 
@@ -281,11 +307,11 @@ pnpm add @fortemi/react @fortemi/core react
281
307
 
282
308
  ## Documentation
283
309
 
284
- - [Repository API](https://github.com/Fortemi/fortemi-react/blob/main/docs/api-reference.md#repositories)
285
- - [Integration guide](https://github.com/Fortemi/fortemi-react/blob/main/docs/integration.md)
286
- - [Search guide](https://github.com/Fortemi/fortemi-react/blob/main/docs/search.md)
287
- - [Extending Fortemi](https://github.com/Fortemi/fortemi-react/blob/main/docs/extending.md)
288
- - [Supply-chain controls](https://github.com/Fortemi/fortemi-react/blob/main/docs/security/supply-chain.md)
310
+ - [Repository API](https://github.com/Fortemi/fortemi-react/blob/main/docs/content/api-reference.md#repositories)
311
+ - [Integration guide](https://github.com/Fortemi/fortemi-react/blob/main/docs/content/guides/integration.md)
312
+ - [Search guide](https://github.com/Fortemi/fortemi-react/blob/main/docs/content/guides/search.md)
313
+ - [Extending Fortemi](https://github.com/Fortemi/fortemi-react/blob/main/docs/content/advanced/extending.md)
314
+ - [Supply-chain controls](https://github.com/Fortemi/fortemi-react/blob/main/docs/content/security/supply-chain.md)
289
315
 
290
316
  ## License
291
317
 
@@ -1,17 +1,26 @@
1
1
  type AiwgFortemiKnownRecordType = 'crm.contact' | 'crm.organization' | 'crm.event' | 'crm.interaction' | 'aiwg.artifact' | 'docs.page';
2
2
  type AiwgFortemiRecordType = AiwgFortemiKnownRecordType | `aiwg.${string}` | `research.${string}` | `docs.${string}` | string;
3
+ type AiwgFortemiRecordSchemaVersion = 'aiwg.fortemi.index.record.v1' | 'aiwg.fortemi.index.record.v2';
4
+ type AiwgFortemiIndexExportSchemaVersion = 'aiwg.fortemi.index.export.v1' | 'aiwg.fortemi.index.export.v2';
3
5
  type AiwgPrivacyClassification = 'private' | 'sanitized' | 'public';
4
6
  type AiwgProvenanceConfidence = 'source' | 'candidate' | 'reviewed' | 'rejected';
5
7
  type AiwgReviewAction = 'accept' | 'reject' | 'defer';
8
+ type AiwgFortemiRelationshipDirection = 'upstream' | 'downstream' | 'related';
6
9
  interface AiwgFortemiRecordSource {
7
10
  path: string;
8
11
  repo_relative_path: string;
9
12
  locator: string;
13
+ origin?: string;
14
+ generated?: boolean;
15
+ checksum?: string;
16
+ updated_at?: string;
10
17
  }
11
18
  interface AiwgFortemiRelationship {
12
19
  type: string;
13
20
  target_id: string;
14
21
  source_path?: string;
22
+ target_path?: string;
23
+ direction?: AiwgFortemiRelationshipDirection;
15
24
  label?: string;
16
25
  confidence?: number;
17
26
  privacy?: AiwgPrivacyClassification;
@@ -54,18 +63,65 @@ interface AiwgFortemiProvenanceEvent {
54
63
  privacy?: AiwgPrivacyClassification;
55
64
  attributes?: Record<string, unknown>;
56
65
  }
66
+ interface AiwgFortemiSearchProjection {
67
+ title?: string;
68
+ name?: string;
69
+ summary?: string;
70
+ body?: string;
71
+ triggers?: string[];
72
+ aliases?: string[];
73
+ capability?: string;
74
+ tags?: string[];
75
+ phase?: string;
76
+ type?: string;
77
+ frontmatter?: Record<string, unknown>;
78
+ }
79
+ interface AiwgFortemiChunk {
80
+ id?: string;
81
+ text?: string;
82
+ body?: string;
83
+ summary?: string;
84
+ source_path?: string;
85
+ metadata?: Record<string, unknown>;
86
+ }
87
+ interface AiwgFortemiRecordEmbedding {
88
+ id?: string;
89
+ embedding?: number[];
90
+ vector?: number[];
91
+ model?: string;
92
+ granularity?: string;
93
+ input_hash?: string;
94
+ source_path?: string;
95
+ metadata?: Record<string, unknown>;
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
+ }
57
108
  interface AiwgFortemiRecord {
58
- schema_version: 'aiwg.fortemi.index.record.v1';
109
+ schema_version: AiwgFortemiRecordSchemaVersion;
59
110
  id: string;
60
111
  type: AiwgFortemiRecordType;
61
112
  source: AiwgFortemiRecordSource;
62
- title: string;
63
- text: string;
113
+ title?: string;
114
+ text?: string;
64
115
  facets: Record<string, string[]>;
65
116
  tags: string[];
66
117
  concepts: string[];
67
118
  relationships: AiwgFortemiRelationship[];
68
119
  provenance: AiwgFortemiProvenance[];
120
+ search?: AiwgFortemiSearchProjection;
121
+ chunks?: AiwgFortemiChunk[];
122
+ binary_sources?: AiwgFortemiBinarySource[];
123
+ embeddings?: AiwgFortemiRecordEmbedding[];
124
+ compatibility?: Record<string, unknown>;
69
125
  /** Optional rich SKOS metadata for static consumers that need labels/definitions without opening a shard. */
70
126
  skos_concepts?: AiwgFortemiSkosConcept[];
71
127
  /** Optional SKOS relationship edges among concepts referenced by this record. */
@@ -75,17 +131,23 @@ interface AiwgFortemiRecord {
75
131
  privacy: {
76
132
  classification: AiwgPrivacyClassification;
77
133
  pii: boolean;
134
+ locality?: string;
78
135
  };
79
136
  updated_at: string;
80
137
  }
81
138
  interface AiwgFortemiIndexExport {
82
- schema_version: 'aiwg.fortemi.index.export.v1';
139
+ schema_version: AiwgFortemiIndexExportSchemaVersion;
83
140
  generated_at: string;
84
141
  source: {
85
142
  repo: string;
86
143
  privacy: AiwgPrivacyClassification;
144
+ origin?: string;
145
+ generated?: boolean;
146
+ checksum?: string;
147
+ updated_at?: string;
87
148
  };
88
149
  items: AiwgFortemiRecord[];
150
+ compatibility?: Record<string, unknown>;
89
151
  }
90
152
  interface AiwgFortemiChunkPartRef {
91
153
  href: string;
@@ -215,6 +277,7 @@ type AiwgRelationshipSetOperation = 'intersection' | 'union' | 'difference';
215
277
  interface AiwgRelationshipTraversalOptions {
216
278
  direction?: AiwgRelationshipDirection;
217
279
  relationshipType?: string;
280
+ relationshipDirection?: AiwgFortemiRelationshipDirection;
218
281
  limit?: number;
219
282
  }
220
283
  interface AiwgRelationshipQueryOptions extends AiwgRelationshipTraversalOptions {
@@ -227,6 +290,8 @@ interface AiwgRelationshipEdgeSummary {
227
290
  target_id: string;
228
291
  type: string;
229
292
  source_path?: string;
293
+ target_path?: string;
294
+ direction?: AiwgFortemiRelationshipDirection;
230
295
  }
231
296
  interface AiwgRelationshipNodeSummary {
232
297
  id: string;
@@ -268,6 +333,20 @@ interface AiwgStaticEmbeddingSet {
268
333
  input_hash_algorithm?: string;
269
334
  embeddings: AiwgStaticEmbeddingRecord[];
270
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
+ }
271
350
  interface AiwgStaticSemanticQueryOptions {
272
351
  limit?: number;
273
352
  offset?: number;
@@ -337,6 +416,7 @@ declare function encodeAiwgDetailId(id: string, encoding?: AiwgDetailIdEncoding)
337
416
  declare function aiwgDetailHrefForId(detail: AiwgFortemiChunkDetailRef, id: string): string;
338
417
  declare function createAiwgFetchDetailLoader(baseUrl?: string | URL): AiwgChunkedIndexDetailLoader;
339
418
  declare function getAiwgFortemiFacets(items: AiwgFortemiRecord[]): Record<string, Record<string, number>>;
419
+ declare function buildAiwgStaticEmbeddingSet(index: AiwgFortemiIndexExport, options: BuildAiwgStaticEmbeddingSetOptions): Promise<AiwgStaticEmbeddingSet>;
340
420
  interface AiwgChunkedIndexBuildOptions {
341
421
  partSize?: number;
342
422
  projection?: Array<keyof AiwgFortemiRecord>;
@@ -381,4 +461,4 @@ declare function aiwgFortemiIndexToCommunityGraph(index: AiwgFortemiIndexExport,
381
461
  }[];
382
462
  };
383
463
 
384
- 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 AiwgFortemiChunkDetailRef, type AiwgFortemiChunkManifest, type AiwgFortemiChunkPart, type AiwgFortemiChunkPartRef, type AiwgFortemiIndexExport, type AiwgFortemiKnownRecordType, type AiwgFortemiProjectedRecord, type AiwgFortemiProvenance, type AiwgFortemiProvenanceEvent, type AiwgFortemiRecord, type AiwgFortemiRecordSource, type AiwgFortemiRecordType, type AiwgFortemiRelationship, 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 };