@hviana/sema 0.1.4 → 0.1.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.
Files changed (122) hide show
  1. package/dist/example/demo.js +25 -25
  2. package/dist/example/train_base.d.ts +21 -19
  3. package/dist/example/train_base.js +1952 -1684
  4. package/dist/src/alphabet.d.ts +3 -3
  5. package/dist/src/alphabet.js +27 -24
  6. package/dist/src/alu/src/alu.d.ts +194 -169
  7. package/dist/src/alu/src/alu.js +414 -374
  8. package/dist/src/alu/src/expr.d.ts +43 -36
  9. package/dist/src/alu/src/expr.js +278 -252
  10. package/dist/src/alu/src/index.d.ts +98 -9
  11. package/dist/src/alu/src/index.js +63 -9
  12. package/dist/src/alu/src/kernel-arith.d.ts +5 -1
  13. package/dist/src/alu/src/kernel-arith.js +285 -195
  14. package/dist/src/alu/src/kernel-bits.d.ts +5 -1
  15. package/dist/src/alu/src/kernel-bits.js +115 -81
  16. package/dist/src/alu/src/kernel-logic.js +63 -33
  17. package/dist/src/alu/src/kernel-nd.js +195 -155
  18. package/dist/src/alu/src/kernel-numeric.d.ts +80 -16
  19. package/dist/src/alu/src/kernel-numeric.js +331 -259
  20. package/dist/src/alu/src/operation.d.ts +132 -112
  21. package/dist/src/alu/src/operation.js +163 -149
  22. package/dist/src/alu/src/parser.d.ts +175 -168
  23. package/dist/src/alu/src/parser.js +421 -382
  24. package/dist/src/alu/src/resonance.d.ts +25 -16
  25. package/dist/src/alu/src/resonance.js +56 -49
  26. package/dist/src/alu/src/text.d.ts +7 -3
  27. package/dist/src/alu/src/text.js +37 -31
  28. package/dist/src/alu/src/value.d.ts +14 -14
  29. package/dist/src/alu/src/value.js +160 -150
  30. package/dist/src/alu/test/alu.test.js +656 -543
  31. package/dist/src/bytes.d.ts +5 -1
  32. package/dist/src/bytes.js +40 -33
  33. package/dist/src/config.d.ts +102 -102
  34. package/dist/src/config.js +83 -83
  35. package/dist/src/derive/src/deduction.d.ts +62 -60
  36. package/dist/src/derive/src/deduction.js +109 -103
  37. package/dist/src/derive/src/index.d.ts +7 -1
  38. package/dist/src/derive/src/priority-queue.d.ts +8 -8
  39. package/dist/src/derive/src/priority-queue.js +61 -57
  40. package/dist/src/derive/src/rewrite.d.ts +18 -15
  41. package/dist/src/derive/src/rewrite.js +67 -59
  42. package/dist/src/derive/src/trie.d.ts +56 -56
  43. package/dist/src/derive/src/trie.js +179 -175
  44. package/dist/src/derive/test/derive.test.js +100 -96
  45. package/dist/src/extension.d.ts +16 -13
  46. package/dist/src/geometry.d.ts +42 -17
  47. package/dist/src/geometry.js +266 -235
  48. package/dist/src/index.d.ts +17 -2
  49. package/dist/src/index.js +7 -1
  50. package/dist/src/ingest-cache.d.ts +30 -25
  51. package/dist/src/ingest-cache.js +127 -108
  52. package/dist/src/mind/articulation.d.ts +5 -1
  53. package/dist/src/mind/articulation.js +112 -77
  54. package/dist/src/mind/attention.d.ts +108 -40
  55. package/dist/src/mind/attention.js +871 -749
  56. package/dist/src/mind/canonical.d.ts +19 -4
  57. package/dist/src/mind/canonical.js +31 -27
  58. package/dist/src/mind/graph-search.d.ts +225 -201
  59. package/dist/src/mind/graph-search.js +821 -742
  60. package/dist/src/mind/index.d.ts +10 -2
  61. package/dist/src/mind/junction.d.ts +58 -31
  62. package/dist/src/mind/junction.js +237 -172
  63. package/dist/src/mind/learning.d.ts +52 -16
  64. package/dist/src/mind/learning.js +165 -143
  65. package/dist/src/mind/match.d.ts +69 -20
  66. package/dist/src/mind/match.js +318 -259
  67. package/dist/src/mind/mechanisms/alu.js +16 -16
  68. package/dist/src/mind/mechanisms/cast.d.ts +13 -9
  69. package/dist/src/mind/mechanisms/cast.js +456 -363
  70. package/dist/src/mind/mechanisms/confluence.d.ts +12 -8
  71. package/dist/src/mind/mechanisms/confluence.js +183 -152
  72. package/dist/src/mind/mechanisms/cover.d.ts +8 -2
  73. package/dist/src/mind/mechanisms/cover.js +210 -148
  74. package/dist/src/mind/mechanisms/extraction.d.ts +34 -8
  75. package/dist/src/mind/mechanisms/extraction.js +288 -234
  76. package/dist/src/mind/mechanisms/recall.d.ts +10 -6
  77. package/dist/src/mind/mechanisms/recall.js +185 -126
  78. package/dist/src/mind/mind.d.ts +154 -129
  79. package/dist/src/mind/mind.js +288 -258
  80. package/dist/src/mind/pipeline-mechanism.d.ts +124 -112
  81. package/dist/src/mind/pipeline-mechanism.js +172 -161
  82. package/dist/src/mind/pipeline.d.ts +14 -4
  83. package/dist/src/mind/pipeline.js +189 -125
  84. package/dist/src/mind/primitives.d.ts +32 -8
  85. package/dist/src/mind/primitives.js +117 -99
  86. package/dist/src/mind/rationale.d.ts +98 -86
  87. package/dist/src/mind/rationale.js +121 -113
  88. package/dist/src/mind/reasoning.d.ts +13 -2
  89. package/dist/src/mind/reasoning.js +166 -129
  90. package/dist/src/mind/recognition.d.ts +4 -1
  91. package/dist/src/mind/recognition.js +208 -183
  92. package/dist/src/mind/resonance.d.ts +22 -5
  93. package/dist/src/mind/resonance.js +0 -0
  94. package/dist/src/mind/trace.d.ts +25 -6
  95. package/dist/src/mind/trace.js +58 -50
  96. package/dist/src/mind/traverse.d.ts +58 -16
  97. package/dist/src/mind/traverse.js +357 -304
  98. package/dist/src/mind/types.d.ts +127 -120
  99. package/dist/src/mind/types.js +69 -60
  100. package/dist/src/rabitq-hnsw/src/database.d.ts +179 -177
  101. package/dist/src/rabitq-hnsw/src/database.js +359 -342
  102. package/dist/src/rabitq-hnsw/src/heap.d.ts +12 -12
  103. package/dist/src/rabitq-hnsw/src/heap.js +80 -75
  104. package/dist/src/rabitq-hnsw/src/hnsw.d.ts +103 -103
  105. package/dist/src/rabitq-hnsw/src/hnsw.js +454 -428
  106. package/dist/src/rabitq-hnsw/src/index.d.ts +6 -1
  107. package/dist/src/rabitq-hnsw/src/prng.d.ts +10 -10
  108. package/dist/src/rabitq-hnsw/src/prng.js +27 -25
  109. package/dist/src/rabitq-hnsw/src/rabitq.d.ts +69 -69
  110. package/dist/src/rabitq-hnsw/src/rabitq.js +262 -246
  111. package/dist/src/rabitq-hnsw/src/store.d.ts +132 -132
  112. package/dist/src/rabitq-hnsw/src/store.js +843 -752
  113. package/dist/src/rabitq-hnsw/test/hnsw.test.js +1054 -805
  114. package/dist/src/sema.d.ts +13 -9
  115. package/dist/src/sema.js +40 -26
  116. package/dist/src/store-sqlite.d.ts +166 -144
  117. package/dist/src/store-sqlite.js +660 -578
  118. package/dist/src/store.d.ts +630 -593
  119. package/dist/src/store.js +1581 -1440
  120. package/dist/src/vec.d.ts +9 -5
  121. package/dist/src/vec.js +73 -61
  122. package/package.json +2 -2
@@ -29,7 +29,14 @@ import { conceptThreshold, identityBar, significanceBar } from "../geometry.js";
29
29
  import { indexOf } from "../bytes.js";
30
30
  import { leafIdRun } from "./canonical.js";
31
31
  import { gistOf, read, resolve } from "./primitives.js";
32
- import { argmaxCosine, chooseAmong, chooseNext, guidedFirst, hubBound, hubCap, } from "./traverse.js";
32
+ import {
33
+ argmaxCosine,
34
+ chooseAmong,
35
+ chooseNext,
36
+ guidedFirst,
37
+ hubBound,
38
+ hubCap,
39
+ } from "./traverse.js";
33
40
  import { recognise, segment } from "./recognition.js";
34
41
  // ═══════════════════════════════════════════════════════════════════════════
35
42
  // MATCHERS — locating learned structure in/against bytes, by graded strictness
@@ -47,35 +54,49 @@ import { recognise, segment } from "./recognition.js";
47
54
  *
48
55
  * Returns the absolute byte position, or −1. */
49
56
  export function locate(ctx, haystack, needle, fromPos, sites) {
50
- // 1. Exact match — fast, preserves backward compatibility.
51
- const exact = indexOf(haystack.subarray(fromPos), needle, 0);
52
- if (exact >= 0)
53
- return fromPos + exact;
54
- // 2. Halo-based: the frame bytes' distributional role matches a query form.
55
- if (sites && sites.length > 0) {
56
- const frameId = resolve(ctx, needle);
57
- if (frameId !== null) {
58
- const frameHalo = ctx.store.halo(frameId);
59
- if (frameHalo) {
60
- const bestSite = bestHaloMate(ctx, frameHalo, sites.filter((s) => s.start >= fromPos), (s) => ctx.store.halo(s.payload));
61
- if (bestSite !== null)
62
- return bestSite.item.start;
63
- }
57
+ // 1. Exact match — fast, preserves backward compatibility.
58
+ const exact = indexOf(haystack.subarray(fromPos), needle, 0);
59
+ if (exact >= 0) {
60
+ return fromPos + exact;
61
+ }
62
+ // 2. Halo-based: the frame bytes' distributional role matches a query form.
63
+ if (sites && sites.length > 0) {
64
+ const frameId = resolve(ctx, needle);
65
+ if (frameId !== null) {
66
+ const frameHalo = ctx.store.halo(frameId);
67
+ if (frameHalo) {
68
+ const bestSite = bestHaloMate(
69
+ ctx,
70
+ frameHalo,
71
+ sites.filter((s) => s.start >= fromPos),
72
+ (s) => ctx.store.halo(s.payload),
73
+ );
74
+ if (bestSite !== null) {
75
+ return bestSite.item.start;
64
76
  }
77
+ }
65
78
  }
66
- // 3. Gist resonance: the frame's perceived gist against query segments.
67
- const frameGist = gistOf(ctx, needle);
68
- const segments = segment(ctx, haystack.subarray(fromPos));
69
- // The gist tier claims the WHOLE needle appears as a segment — an
70
- // identity claim over `needle.length` bytes, so its bar is the
71
- // scale-aware {@link identityBar} (one river window of tolerated foreign
72
- // bytes), not the fixed estimator floor. For quantum-sized frames the
73
- // two coincide; for long needles the fixed bar accepted segments that
74
- // differed by whole windows.
75
- const bestSeg = argmaxCosine(frameGist, segments, (s) => s.v, identityBar(ctx.store.D, ctx.space.maxGroup, needle.length), true);
76
- if (bestSeg !== null)
77
- return fromPos + bestSeg.item.start;
78
- return -1;
79
+ }
80
+ // 3. Gist resonance: the frame's perceived gist against query segments.
81
+ const frameGist = gistOf(ctx, needle);
82
+ const segments = segment(ctx, haystack.subarray(fromPos));
83
+ // The gist tier claims the WHOLE needle appears as a segment an
84
+ // identity claim over `needle.length` bytes, so its bar is the
85
+ // scale-aware {@link identityBar} (one river window of tolerated foreign
86
+ // bytes), not the fixed estimator floor. For quantum-sized frames the
87
+ // two coincide; for long needles the fixed bar accepted segments that
88
+ // differed by whole windows.
89
+ const bestSeg = argmaxCosine(
90
+ frameGist,
91
+ segments,
92
+ (s) => s.v,
93
+ identityBar(ctx.store.D, ctx.space.maxGroup, needle.length),
94
+ true,
95
+ );
96
+ if (bestSeg !== null) {
97
+ return fromPos + bestSeg.item.start;
98
+ }
99
+ return -1;
79
100
  }
80
101
  /** The ALIGNED matcher: maximal literal matching runs between `query` and
81
102
  * `ct` (a learned context's bytes), by seed-and-extend over
@@ -84,48 +105,59 @@ export function locate(ctx, haystack, needle, fromPos, sites) {
84
105
  * matcher CAST detects a woven query with. Returns non-overlapping runs
85
106
  * sorted by query position. */
86
107
  export function alignRuns(ctx, query, ct) {
87
- const quantum = Math.min(ctx.space.maxGroup, ct.length);
88
- if (quantum < 1 || query.length < quantum)
89
- return [];
90
- const gram = (b, at) => {
91
- let s = "";
92
- for (let i = 0; i < quantum; i++)
93
- s += String.fromCharCode(b[at + i]);
94
- return s;
95
- };
96
- const seeds = new Map();
97
- for (let i = 0; i + quantum <= query.length; i++) {
98
- const k2 = gram(query, i);
99
- const bucket = seeds.get(k2);
100
- if (bucket === undefined)
101
- seeds.set(k2, [i]);
102
- else
103
- bucket.push(i);
108
+ const quantum = Math.min(ctx.space.maxGroup, ct.length);
109
+ if (quantum < 1 || query.length < quantum) {
110
+ return [];
111
+ }
112
+ const gram = (b, at) => {
113
+ let s = "";
114
+ for (let i = 0; i < quantum; i++) {
115
+ s += String.fromCharCode(b[at + i]);
104
116
  }
105
- const found = [];
106
- for (let j = 0; j + quantum <= ct.length; j++) {
107
- const bucket = seeds.get(gram(ct, j));
108
- if (bucket === undefined)
109
- continue;
110
- for (const i of bucket) {
111
- if (i > 0 && j > 0 && query[i - 1] === ct[j - 1])
112
- continue;
113
- let len = quantum;
114
- while (i + len < query.length && j + len < ct.length &&
115
- query[i + len] === ct[j + len])
116
- len++;
117
- found.push({ qs: i, qe: i + len, cs: j, len });
118
- }
117
+ return s;
118
+ };
119
+ const seeds = new Map();
120
+ for (let i = 0; i + quantum <= query.length; i++) {
121
+ const k2 = gram(query, i);
122
+ const bucket = seeds.get(k2);
123
+ if (bucket === undefined) {
124
+ seeds.set(k2, [i]);
125
+ } else {
126
+ bucket.push(i);
127
+ }
128
+ }
129
+ const found = [];
130
+ for (let j = 0; j + quantum <= ct.length; j++) {
131
+ const bucket = seeds.get(gram(ct, j));
132
+ if (bucket === undefined) {
133
+ continue;
119
134
  }
120
- found.sort((a, b) => b.len - a.len);
121
- const runs = [];
122
- for (const r of found) {
123
- const clash = runs.some((o) => (r.qs < o.qe && o.qs < r.qe) ||
124
- (r.cs < o.cs + (o.qe - o.qs) && o.cs < r.cs + r.len));
125
- if (!clash)
126
- runs.push({ qs: r.qs, qe: r.qe, cs: r.cs });
135
+ for (const i of bucket) {
136
+ if (i > 0 && j > 0 && query[i - 1] === ct[j - 1]) {
137
+ continue;
138
+ }
139
+ let len = quantum;
140
+ while (
141
+ i + len < query.length && j + len < ct.length &&
142
+ query[i + len] === ct[j + len]
143
+ ) {
144
+ len++;
145
+ }
146
+ found.push({ qs: i, qe: i + len, cs: j, len });
127
147
  }
128
- return runs.sort((a, b) => a.qs - b.qs);
148
+ }
149
+ found.sort((a, b) => b.len - a.len);
150
+ const runs = [];
151
+ for (const r of found) {
152
+ const clash = runs.some((o) =>
153
+ (r.qs < o.qe && o.qs < r.qe) ||
154
+ (r.cs < o.cs + (o.qe - o.qs) && o.cs < r.cs + r.len)
155
+ );
156
+ if (!clash) {
157
+ runs.push({ qs: r.qs, qe: r.qe, cs: r.cs });
158
+ }
159
+ }
160
+ return runs.sort((a, b) => a.qs - b.qs);
129
161
  }
130
162
  /** The GRADED alignment matcher: extends literal W-gram alignment
131
163
  * ({@link alignRuns}) with halo-matched recognised sites in query regions
@@ -138,78 +170,86 @@ export function alignRuns(ctx, query, ct) {
138
170
  * (optional — when absent, only literal alignment fires and graded degrades
139
171
  * to the original behaviour). Context sites are recognised internally. */
140
172
  export function alignGraded(ctx, query, contextBytes, querySites) {
141
- const lit = alignRuns(ctx, query, contextBytes);
142
- const out = lit.map((r) => ({ ...r, weight: 1 }));
143
- if (!querySites || querySites.length === 0)
144
- return out;
145
- // Mark query positions ALREADY covered by literal runs — halo fills gaps.
146
- // If literal coverage is already complete, skip the halo step entirely
147
- // (recognise is O(|ctx|·W) wasted when every byte is accounted for).
148
- const covered = new Uint8Array(query.length);
149
- let gaps = false;
150
- for (const r of lit) {
151
- for (let i = r.qs; i < r.qe; i++)
152
- covered[i] = 1;
173
+ const lit = alignRuns(ctx, query, contextBytes);
174
+ const out = lit.map((r) => ({ ...r, weight: 1 }));
175
+ if (!querySites || querySites.length === 0) {
176
+ return out;
177
+ }
178
+ // Mark query positions ALREADY covered by literal runs halo fills gaps.
179
+ // If literal coverage is already complete, skip the halo step entirely
180
+ // (recognise is O(|ctx|·W) — wasted when every byte is accounted for).
181
+ const covered = new Uint8Array(query.length);
182
+ let gaps = false;
183
+ for (const r of lit) {
184
+ for (let i = r.qs; i < r.qe; i++) {
185
+ covered[i] = 1;
153
186
  }
154
- for (let i = 0; i < query.length; i++) {
155
- if (!covered[i]) {
156
- gaps = true;
157
- break;
158
- }
187
+ }
188
+ for (let i = 0; i < query.length; i++) {
189
+ if (!covered[i]) {
190
+ gaps = true;
191
+ break;
159
192
  }
160
- if (!gaps)
161
- return out;
162
- // Recognise sites in the exemplar context — structural positions for halo
163
- // matching. (Circular import with recognition.ts is safe: recognise() is
164
- // called lazily, never at module loadthe same pattern `segment` uses.)
165
- const ctxSites = recognise(ctx, contextBytes).sites;
166
- if (ctxSites.length === 0)
167
- return out;
168
- // Context sites with halos, hoisted: the same set serves every query site.
169
- const ctxCands = ctxSites.filter((cs) => ctx.store.hasHalo(cs.payload));
170
- if (ctxCands.length === 0)
171
- return out;
172
- // Candidate halos, also hoisted (lazily, first query site that needs them):
173
- // bestHaloMate consults every candidate's halo PER QUERY SITE, and sites
174
- // share the candidate set — without this memo the same few dozen halos were
175
- // re-fetched thousands of times per response. Distinct payloads can repeat
176
- // across sites, hence the map by payload id.
177
- const ctxHalos = new Map();
178
- const ctxHaloOf = (cs) => {
179
- let h = ctxHalos.get(cs.payload);
180
- if (h === undefined) {
181
- h = ctx.store.halo(cs.payload);
182
- ctxHalos.set(cs.payload, h);
183
- }
184
- return h;
185
- };
186
- for (const qs of querySites) {
187
- // Only sites that overlap UNCOVERED query regions add new evidence.
188
- let touchesGap = false;
189
- for (let i = qs.start; i < qs.end; i++) {
190
- if (!covered[i]) {
191
- touchesGap = true;
192
- break;
193
- }
194
- }
195
- if (!touchesGap)
196
- continue;
197
- const qHalo = ctx.store.halo(qs.payload);
198
- if (!qHalo)
199
- continue;
200
- // bestHaloMate already gates at conceptThreshold — no second check needed.
201
- const match = bestHaloMate(ctx, qHalo, ctxCands, ctxHaloOf);
202
- if (match === null)
203
- continue;
204
- out.push({
205
- qs: qs.start,
206
- qe: qs.end,
207
- cs: match.item.start,
208
- weight: match.score,
209
- });
210
- }
211
- out.sort((a, b) => a.qs - b.qs);
193
+ }
194
+ if (!gaps) {
195
+ return out;
196
+ }
197
+ // Recognise sites in the exemplar contextstructural positions for halo
198
+ // matching. (Circular import with recognition.ts is safe: recognise() is
199
+ // called lazily, never at module load — the same pattern `segment` uses.)
200
+ const ctxSites = recognise(ctx, contextBytes).sites;
201
+ if (ctxSites.length === 0) {
202
+ return out;
203
+ }
204
+ // Context sites with halos, hoisted: the same set serves every query site.
205
+ const ctxCands = ctxSites.filter((cs) => ctx.store.hasHalo(cs.payload));
206
+ if (ctxCands.length === 0) {
212
207
  return out;
208
+ }
209
+ // Candidate halos, also hoisted (lazily, first query site that needs them):
210
+ // bestHaloMate consults every candidate's halo PER QUERY SITE, and sites
211
+ // share the candidate set — without this memo the same few dozen halos were
212
+ // re-fetched thousands of times per response. Distinct payloads can repeat
213
+ // across sites, hence the map by payload id.
214
+ const ctxHalos = new Map();
215
+ const ctxHaloOf = (cs) => {
216
+ let h = ctxHalos.get(cs.payload);
217
+ if (h === undefined) {
218
+ h = ctx.store.halo(cs.payload);
219
+ ctxHalos.set(cs.payload, h);
220
+ }
221
+ return h;
222
+ };
223
+ for (const qs of querySites) {
224
+ // Only sites that overlap UNCOVERED query regions add new evidence.
225
+ let touchesGap = false;
226
+ for (let i = qs.start; i < qs.end; i++) {
227
+ if (!covered[i]) {
228
+ touchesGap = true;
229
+ break;
230
+ }
231
+ }
232
+ if (!touchesGap) {
233
+ continue;
234
+ }
235
+ const qHalo = ctx.store.halo(qs.payload);
236
+ if (!qHalo) {
237
+ continue;
238
+ }
239
+ // bestHaloMate already gates at conceptThreshold — no second check needed.
240
+ const match = bestHaloMate(ctx, qHalo, ctxCands, ctxHaloOf);
241
+ if (match === null) {
242
+ continue;
243
+ }
244
+ out.push({
245
+ qs: qs.start,
246
+ qe: qs.end,
247
+ cs: match.item.start,
248
+ weight: match.score,
249
+ });
250
+ }
251
+ out.sort((a, b) => a.qs - b.qs);
252
+ return out;
213
253
  }
214
254
  /** The IN-LIST halo matcher: the best halo-mate for `halo` among EXPLICIT
215
255
  * candidates, above the concept threshold — the list counterpart of
@@ -217,7 +257,7 @@ export function alignGraded(ctx, query, contextBytes, querySites) {
217
257
  * Behind locate()'s halo step and articulation's voice matching; a third
218
258
  * "best halo among these" decision must come here, not inline. */
219
259
  export function bestHaloMate(ctx, halo, items, haloOf) {
220
- return argmaxCosine(halo, items, haloOf, conceptThreshold(ctx.store.D));
260
+ return argmaxCosine(halo, items, haloOf, conceptThreshold(ctx.store.D));
221
261
  }
222
262
  /** The HALO-SIBLING matcher: the nodes that keep the same distributional
223
263
  * company as `id`, nearest first — `resonateHalo` filtered to exclude the
@@ -227,35 +267,42 @@ export function bestHaloMate(ctx, halo, items, haloOf) {
227
267
  * no halo. The one sibling enumeration behind the concept hop, the
228
268
  * reasoning stage's synonym expansion, and the analogy matcher below. */
229
269
  const haloSiblingMemo = new WeakMap();
230
- export async function haloSiblings(ctx, id, halo, bar = conceptThreshold(ctx.store.D)) {
231
- // Per-response memo for the DEFAULT-ARGUMENT reading (the one the concept
232
- // hop, the bridge's synonym tier, and reasoning's synonym expansion all
233
- // use): the same node's siblings are asked for repeatedly within one
234
- // response (bridge pairs share sides), each a full halo-ANN query, and the
235
- // store is read-only while a response is in flight. Keyed by the response
236
- // lifecycle object (ctx.climbMemo fresh per respond, nulled after).
237
- // Calls with an explicit halo or bar (analogyStrength's gated reading)
238
- // bypass the memo their filter differs.
239
- const memoable = halo === undefined &&
240
- bar === conceptThreshold(ctx.store.D) && ctx.climbMemo !== null;
241
- let memo;
242
- if (memoable) {
243
- memo = haloSiblingMemo.get(ctx.climbMemo);
244
- if (memo === undefined) {
245
- haloSiblingMemo.set(ctx.climbMemo, memo = new Map());
246
- }
247
- const hit = memo.get(id);
248
- if (hit !== undefined)
249
- return hit;
270
+ export async function haloSiblings(
271
+ ctx,
272
+ id,
273
+ halo,
274
+ bar = conceptThreshold(ctx.store.D),
275
+ ) {
276
+ // Per-response memo for the DEFAULT-ARGUMENT reading (the one the concept
277
+ // hop, the bridge's synonym tier, and reasoning's synonym expansion all
278
+ // use): the same node's siblings are asked for repeatedly within one
279
+ // response (bridge pairs share sides), each a full halo-ANN query, and the
280
+ // store is read-only while a response is in flight. Keyed by the response
281
+ // lifecycle object (ctx.climbMemo — fresh per respond, nulled after).
282
+ // Calls with an explicit halo or bar (analogyStrength's gated reading)
283
+ // bypass the memo their filter differs.
284
+ const memoable = halo === undefined &&
285
+ bar === conceptThreshold(ctx.store.D) && ctx.climbMemo !== null;
286
+ let memo;
287
+ if (memoable) {
288
+ memo = haloSiblingMemo.get(ctx.climbMemo);
289
+ if (memo === undefined) {
290
+ haloSiblingMemo.set(ctx.climbMemo, memo = new Map());
250
291
  }
251
- const h = halo ?? ctx.store.halo(id);
252
- const out = h
253
- ? (await ctx.store.resonateHalo(h, ctx.cfg.haloQueryK))
254
- .filter((sib) => sib.id !== id && sib.score >= bar)
255
- : [];
256
- if (memo !== undefined)
257
- memo.set(id, out);
258
- return out;
292
+ const hit = memo.get(id);
293
+ if (hit !== undefined) {
294
+ return hit;
295
+ }
296
+ }
297
+ const h = halo ?? ctx.store.halo(id);
298
+ const out = h
299
+ ? (await ctx.store.resonateHalo(h, ctx.cfg.haloQueryK))
300
+ .filter((sib) => sib.id !== id && sib.score >= bar)
301
+ : [];
302
+ if (memo !== undefined) {
303
+ memo.set(id, out);
304
+ }
305
+ return out;
259
306
  }
260
307
  /** The DISTRIBUTIONAL matcher between two nodes: mutual-nearest-neighbour
261
308
  * strength, not a pick. Returns the direct halo cosine, or failing that the
@@ -263,28 +310,31 @@ export async function haloSiblings(ctx, id, halo, bar = conceptThreshold(ctx.sto
263
310
  * that the SHARED-FRAME strength (below) — the gate CAST's comparison
264
311
  * schema validates genuine analogs with (bar: significanceBar). */
265
312
  export async function analogyStrength(ctx, a, b) {
266
- const ha = ctx.store.halo(a);
267
- const hb = ctx.store.halo(b);
268
- if (ha && hb) {
269
- const bar = significanceBar(ctx.store.D);
270
- const direct = cosine(ha, hb);
271
- if (direct >= bar)
272
- return direct;
273
- const sibsA = await haloSiblings(ctx, a, ha, bar);
274
- const sibsB = await haloSiblings(ctx, b, hb, bar);
275
- let best = 0;
276
- for (const x of sibsA) {
277
- if (x.id === b)
278
- continue;
279
- const y = sibsB.find((s) => s.id === x.id);
280
- if (y !== undefined) {
281
- best = Math.max(best, Math.min(x.score, y.score));
282
- }
283
- }
284
- if (best > 0)
285
- return best;
313
+ const ha = ctx.store.halo(a);
314
+ const hb = ctx.store.halo(b);
315
+ if (ha && hb) {
316
+ const bar = significanceBar(ctx.store.D);
317
+ const direct = cosine(ha, hb);
318
+ if (direct >= bar) {
319
+ return direct;
286
320
  }
287
- return sharedFrameStrength(ctx, a, b);
321
+ const sibsA = await haloSiblings(ctx, a, ha, bar);
322
+ const sibsB = await haloSiblings(ctx, b, hb, bar);
323
+ let best = 0;
324
+ for (const x of sibsA) {
325
+ if (x.id === b) {
326
+ continue;
327
+ }
328
+ const y = sibsB.find((s) => s.id === x.id);
329
+ if (y !== undefined) {
330
+ best = Math.max(best, Math.min(x.score, y.score));
331
+ }
332
+ }
333
+ if (best > 0) {
334
+ return best;
335
+ }
336
+ }
337
+ return sharedFrameStrength(ctx, a, b);
288
338
  }
289
339
  /** The STRUCTURAL analogy tier: two nodes are analogs when their byte
290
340
  * streams share a LEARNT frame — a content-addressed flat form of at least
@@ -299,29 +349,33 @@ export async function analogyStrength(ctx, a, b) {
299
349
  * is maxGroup, the same quantum differsByOneWindow and canonicalChunkId
300
350
  * measure by; no tuned constants. */
301
351
  export function sharedFrameStrength(ctx, a, b) {
302
- const W = ctx.space.maxGroup;
303
- const A = read(ctx, a);
304
- const B = read(ctx, b);
305
- if (A.length < W || B.length < W)
306
- return 0;
307
- // Mark every byte of the shorter side covered by a learnt W-window that
308
- // also occurs in the longer side.
309
- const [s, l] = A.length <= B.length ? [A, B] : [B, A];
310
- const covered = new Uint8Array(s.length);
311
- for (let off = 0; off + W <= s.length; off++) {
312
- const win = s.subarray(off, off + W);
313
- // Learnt: the window resolves as a content-addressed flat form.
314
- const ids = leafIdRun(ctx, s, off, off + W);
315
- if (ids === null || ctx.store.findBranch(ids) === null)
316
- continue;
317
- if (indexOf(l, win, 0) < 0)
318
- continue;
319
- covered.fill(1, off, off + W);
352
+ const W = ctx.space.maxGroup;
353
+ const A = read(ctx, a);
354
+ const B = read(ctx, b);
355
+ if (A.length < W || B.length < W) {
356
+ return 0;
357
+ }
358
+ // Mark every byte of the shorter side covered by a learnt W-window that
359
+ // also occurs in the longer side.
360
+ const [s, l] = A.length <= B.length ? [A, B] : [B, A];
361
+ const covered = new Uint8Array(s.length);
362
+ for (let off = 0; off + W <= s.length; off++) {
363
+ const win = s.subarray(off, off + W);
364
+ // Learnt: the window resolves as a content-addressed flat form.
365
+ const ids = leafIdRun(ctx, s, off, off + W);
366
+ if (ids === null || ctx.store.findBranch(ids) === null) {
367
+ continue;
368
+ }
369
+ if (indexOf(l, win, 0) < 0) {
370
+ continue;
320
371
  }
321
- let n = 0;
322
- for (let i = 0; i < s.length; i++)
323
- n += covered[i];
324
- return n >= W ? n / s.length : 0;
372
+ covered.fill(1, off, off + W);
373
+ }
374
+ let n = 0;
375
+ for (let i = 0; i < s.length; i++) {
376
+ n += covered[i];
377
+ }
378
+ return n >= W ? n / s.length : 0;
325
379
  }
326
380
  // ═══════════════════════════════════════════════════════════════════════════
327
381
  // PROJECTIONS — what a matched node is projected ALONG (the direction)
@@ -330,12 +384,13 @@ export function sharedFrameStrength(ctx, a, b) {
330
384
  * a concept (halo) sibling — resonate the node's halo, take the first
331
385
  * sibling above the concept threshold that itself has a direct edge. */
332
386
  export async function conceptHop(ctx, id) {
333
- for (const sib of await haloSiblings(ctx, id)) {
334
- const hop = guidedFirst(ctx, sib.id);
335
- if (hop !== undefined)
336
- return hop;
387
+ for (const sib of await haloSiblings(ctx, id)) {
388
+ const hop = guidedFirst(ctx, sib.id);
389
+ if (hop !== undefined) {
390
+ return hop;
337
391
  }
338
- return null;
392
+ }
393
+ return null;
339
394
  }
340
395
  /** FORWARD projection: follow continuation edges from a node to its fixpoint.
341
396
  * The first hop may cross a concept (halo) link — a synonym. The rest
@@ -343,27 +398,29 @@ export async function conceptHop(ctx, id) {
343
398
  * against cycles. `guide` disambiguates multi-continuation nodes by
344
399
  * resonance. */
345
400
  export async function follow(ctx, id, guide) {
346
- const seen = new Set([id]);
347
- // First hop: a direct edge, else a concept sibling's edge (the synonym).
348
- let next = chooseNext(ctx, id, guide);
349
- if (next === undefined) {
350
- const hop = await conceptHop(ctx, id);
351
- if (hop === null)
352
- return null;
353
- next = hop;
401
+ const seen = new Set([id]);
402
+ // First hop: a direct edge, else a concept sibling's edge (the synonym).
403
+ let next = chooseNext(ctx, id, guide);
404
+ if (next === undefined) {
405
+ const hop = await conceptHop(ctx, id);
406
+ if (hop === null) {
407
+ return null;
354
408
  }
355
- // Direct successors to the fixpoint. Only the FIXPOINT's bytes are
356
- // returned, so the walk tracks node ids and reads bytes exactly once at
357
- // the end — a K-hop chain used to pay K full reconstructions and discard
358
- // K−1 of them.
359
- while (!seen.has(next)) {
360
- seen.add(next);
361
- const fwd = chooseNext(ctx, next, guide);
362
- if (fwd === undefined || seen.has(fwd))
363
- break;
364
- next = fwd;
409
+ next = hop;
410
+ }
411
+ // Direct successors to the fixpoint. Only the FIXPOINT's bytes are
412
+ // returned, so the walk tracks node ids and reads bytes exactly once at
413
+ // the end — a K-hop chain used to pay K full reconstructions and discard
414
+ // K−1 of them.
415
+ while (!seen.has(next)) {
416
+ seen.add(next);
417
+ const fwd = chooseNext(ctx, next, guide);
418
+ if (fwd === undefined || seen.has(fwd)) {
419
+ break;
365
420
  }
366
- return read(ctx, next);
421
+ next = fwd;
422
+ }
423
+ return read(ctx, next);
367
424
  }
368
425
  /** REVERSE projection: the context a learnt continuation follows, voiced as
369
426
  * bytes. A common continuation ("Yes.") follows MANY contexts; with a
@@ -380,43 +437,45 @@ export async function follow(ctx, id, guide) {
380
437
  * Uint8Array is truthy, and returning it would flow a hollow "answer"
381
438
  * onward). */
382
439
  export function reverseContext(ctx, id, guide, rev) {
383
- // CAPPED default read: only the first √N predecessors are ever candidates
384
- // (hubCap below / in chooseAmong), so only they are read. hubBound ≥ 2
385
- // keeps the single-predecessor shortcut exact.
386
- const candidates = rev ?? ctx.store.prevFirst(id, hubBound(ctx));
387
- if (candidates.length === 0)
388
- return null;
389
- const pick = candidates.length === 1
390
- ? candidates[0]
391
- : guide
392
- ? chooseAmong(ctx, candidates, guide).id
393
- : pickByMass(ctx, candidates);
394
- const g = read(ctx, pick);
395
- return g.length > 0 ? g : null;
440
+ // CAPPED default read: only the first √N predecessors are ever candidates
441
+ // (hubCap below / in chooseAmong), so only they are read. hubBound ≥ 2
442
+ // keeps the single-predecessor shortcut exact.
443
+ const candidates = rev ?? ctx.store.prevFirst(id, hubBound(ctx));
444
+ if (candidates.length === 0) {
445
+ return null;
446
+ }
447
+ const pick = candidates.length === 1
448
+ ? candidates[0]
449
+ : guide
450
+ ? chooseAmong(ctx, candidates, guide).id
451
+ : pickByMass(ctx, candidates);
452
+ const g = read(ctx, pick);
453
+ return g.length > 0 ? g : null;
396
454
  }
397
455
  /** The most-corroborated candidate by poured halo mass (first-seen wins a
398
456
  * tie). Capped at √N candidates by insertion order — the same hub bound
399
457
  * every fan-out walk uses. */
400
458
  function pickByMass(ctx, ids) {
401
- const capped = hubCap(ctx, ids);
402
- let best = capped[0];
403
- let bestMass = ctx.store.haloMass(best);
404
- for (let i = 1; i < capped.length; i++) {
405
- const mass = ctx.store.haloMass(capped[i]);
406
- if (mass > bestMass) {
407
- best = capped[i];
408
- bestMass = mass;
409
- }
459
+ const capped = hubCap(ctx, ids);
460
+ let best = capped[0];
461
+ let bestMass = ctx.store.haloMass(best);
462
+ for (let i = 1; i < capped.length; i++) {
463
+ const mass = ctx.store.haloMass(capped[i]);
464
+ if (mass > bestMass) {
465
+ best = capped[i];
466
+ bestMass = mass;
410
467
  }
411
- return best;
468
+ }
469
+ return best;
412
470
  }
413
471
  /** THE projection: ground a matched node to answer bytes — FORWARD to its
414
472
  * continuation fixpoint (which may cross a concept hop), else REVERSE to
415
473
  * the context it follows. This is the direction ladder every mechanism's
416
474
  * final grounding step reduces to. */
417
475
  export async function project(ctx, id, guide) {
418
- const fc = await follow(ctx, id, guide);
419
- if (fc)
420
- return fc;
421
- return reverseContext(ctx, id, guide);
476
+ const fc = await follow(ctx, id, guide);
477
+ if (fc) {
478
+ return fc;
479
+ }
480
+ return reverseContext(ctx, id, guide);
422
481
  }