@hviana/sema 0.1.6 → 0.1.8

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 (50) hide show
  1. package/dist/example/train_base.js +42 -9
  2. package/dist/src/alu/src/parser.d.ts +9 -0
  3. package/dist/src/alu/src/parser.js +110 -2
  4. package/dist/src/canon.d.ts +26 -0
  5. package/dist/src/canon.js +57 -0
  6. package/dist/src/geometry.d.ts +13 -2
  7. package/dist/src/geometry.js +101 -2
  8. package/dist/src/index.d.ts +1 -0
  9. package/dist/src/index.js +1 -0
  10. package/dist/src/mind/attention.js +11 -7
  11. package/dist/src/mind/learning.js +33 -1
  12. package/dist/src/mind/match.d.ts +4 -2
  13. package/dist/src/mind/match.js +30 -6
  14. package/dist/src/mind/mechanisms/cast.js +18 -4
  15. package/dist/src/mind/mechanisms/confluence.js +13 -1
  16. package/dist/src/mind/mechanisms/recall.js +115 -31
  17. package/dist/src/mind/mind.d.ts +138 -12
  18. package/dist/src/mind/mind.js +284 -22
  19. package/dist/src/mind/primitives.d.ts +22 -2
  20. package/dist/src/mind/primitives.js +95 -6
  21. package/dist/src/mind/recognition.js +31 -8
  22. package/dist/src/mind/traverse.d.ts +13 -0
  23. package/dist/src/mind/traverse.js +41 -0
  24. package/dist/src/mind/types.d.ts +33 -21
  25. package/dist/src/store-sqlite.d.ts +10 -0
  26. package/dist/src/store-sqlite.js +58 -0
  27. package/dist/src/store.d.ts +23 -0
  28. package/dist/src/store.js +13 -2
  29. package/example/train_base.ts +49 -9
  30. package/index.html +65 -0
  31. package/package.json +1 -1
  32. package/src/alu/src/parser.ts +105 -4
  33. package/src/canon.ts +65 -0
  34. package/src/geometry.ts +105 -1
  35. package/src/index.ts +1 -0
  36. package/src/mind/attention.ts +11 -6
  37. package/src/mind/learning.ts +39 -1
  38. package/src/mind/match.ts +29 -6
  39. package/src/mind/mechanisms/cast.ts +21 -6
  40. package/src/mind/mechanisms/confluence.ts +15 -1
  41. package/src/mind/mechanisms/recall.ts +132 -41
  42. package/src/mind/mind.ts +396 -22
  43. package/src/mind/primitives.ts +109 -7
  44. package/src/mind/recognition.ts +36 -8
  45. package/src/mind/traverse.ts +47 -0
  46. package/src/mind/types.ts +30 -21
  47. package/src/store-sqlite.ts +76 -0
  48. package/src/store.ts +46 -2
  49. package/test/13-conversation.test.mjs +240 -20
  50. package/test/35-prefix-edge.test.mjs +86 -0
@@ -13,8 +13,9 @@ import {
13
13
  } from "../../geometry.js";
14
14
  import type { MindContext } from "../types.js";
15
15
  import { gistOf, read, resolve } from "../primitives.js";
16
+ import { bytesEqual, indexOf } from "../../bytes.js";
16
17
  import { corpusN, hubBound } from "../traverse.js";
17
- import { project, reverseContext } from "../match.js";
18
+ import { follow, project, reverseContext } from "../match.js";
18
19
  import { CONCEPT, STEP } from "../graph-search.js";
19
20
  import { unexplainedLabel } from "../rationale.js";
20
21
  import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
@@ -81,6 +82,51 @@ export async function recallByResonance(
81
82
  }
82
83
  }
83
84
 
85
+ // 0b. ARGUMENT BINDING (RC8): the query is not itself a stored form, but
86
+ // it CONTAINS a recognised constituent that is an edge SOURCE — a learnt
87
+ // pair's left side carried inside a wrapper ("How do you say 'thank you'
88
+ // in French?"). The wrapper is scaffolding; the argument is the span
89
+ // that LEADS somewhere, so its continuation — guided by the whole query's
90
+ // gist — is the answer. Matching the wrapper while ignoring the argument
91
+ // (the observed "good morning" template failure) is worse than silence,
92
+ // so anything short of ONE unambiguous binding falls through: the
93
+ // constituent bar is the same two-quanta (2W) reading confluence binds
94
+ // under, nested recognitions collapse to their MAXIMAL span, and two
95
+ // distinct maximal arguments mean the query asks about neither alone.
96
+ if (qId === null) {
97
+ const W2 = 2 * ctx.space.maxGroup;
98
+ const args = pre.rec.sites.filter((s) =>
99
+ s.end - s.start >= W2 &&
100
+ s.end - s.start < query.length &&
101
+ ctx.store.hasNext(s.payload)
102
+ );
103
+ // Maximal spans by one sorted sweep (starts ascending, ties longest
104
+ // first): every earlier span starts at or before s, so s is contained
105
+ // exactly when the running max end already covers it. O(m log m) — a
106
+ // long input recognises O(|input|) sites, and a pairwise scan here was
107
+ // quadratic in the input.
108
+ args.sort((a, b) => a.start - b.start || b.end - a.end);
109
+ const maximal: typeof args = [];
110
+ let maxEnd = -1;
111
+ for (const s of args) {
112
+ if (s.end <= maxEnd) continue;
113
+ maximal.push(s);
114
+ maxEnd = s.end;
115
+ }
116
+ if (maximal.length === 1) {
117
+ const arg = maximal[0];
118
+ const g = await follow(ctx, arg.payload, queryGist);
119
+ if (g !== null && g.length > 0) {
120
+ return ground(
121
+ g,
122
+ "argument binding — the query's sole edge-source constituent, continuation followed",
123
+ [[arg.start, arg.end]],
124
+ STEP,
125
+ );
126
+ }
127
+ }
128
+ }
129
+
84
130
  const whole = await ctx.store.resonate(queryGist, k);
85
131
  if (whole.length === 0) {
86
132
  return ground(null, "empty store — nothing to resonate with", [], 0);
@@ -99,11 +145,27 @@ export async function recallByResonance(
99
145
  // into bytes — at most one river window (see {@link identityBar}). A
100
146
  // fixed cosine bar let long queries claim "near-identical" while whole
101
147
  // windows — an answer word — differed.
102
- if (
103
- top.score >= identityBar(ctx.store.D, ctx.space.maxGroup, query.length)
104
- ) {
148
+ // A hit RESTATES the query when its bytes are the query's own — exactly,
149
+ // or under the response's equivalence (a case/width twin). Restating
150
+ // hits may only conclude through disciplined reverse recall: voicing
151
+ // their bytes echoes the query back at itself (never an answer — the
152
+ // same principle that keeps cast from voicing stored questions), and
153
+ // projecting them forward is reverse recall's containment failure in the
154
+ // other direction — "whatever followed these bytes in some document".
155
+ const qKey = ctx.canon ? ctx.canon(query) : query;
156
+ const restates = (b: Uint8Array): boolean =>
157
+ bytesEqual(b, query) ||
158
+ (ctx.canon !== null && bytesEqual(ctx.canon(b), qKey));
159
+ const idBar = identityBar(ctx.store.D, ctx.space.maxGroup, query.length);
160
+ if (top.score >= idBar) {
105
161
  for (const h of whole) {
106
- if (h.id === qId || h.score >= 1.0) {
162
+ // The identity claim is PER HIT, not per tier: hits are ranked
163
+ // nearest-first, and grounding one below the bar under this tier's
164
+ // "near-identical" label would launder byte-overlap noise (observed:
165
+ // "merci" projecting through the unrelated near hit "meraih").
166
+ if (h.score < idBar) break;
167
+ const own = read(ctx, h.id);
168
+ if (h.id === qId || restates(own)) {
107
169
  const rev = ctx.store.prevFirst(h.id, hubBound(ctx));
108
170
  const g = reverseContext(ctx, h.id, queryGist, rev);
109
171
  if (g !== null) {
@@ -116,15 +178,7 @@ export async function recallByResonance(
116
178
  STEP,
117
179
  );
118
180
  }
119
- const own = read(ctx, h.id);
120
- if (own.length > 0) {
121
- return ground(
122
- own,
123
- "perfect self-match — the query IS this node",
124
- nothing,
125
- STEP,
126
- );
127
- }
181
+ continue;
128
182
  }
129
183
  const g = await project(ctx, h.id, queryGist);
130
184
  if (g) {
@@ -138,8 +192,35 @@ export async function recallByResonance(
138
192
  }
139
193
  }
140
194
 
195
+ // The query-relative grounding fraction, shared by tiers 2–4 — gated on
196
+ // the FRACTION OF THE QUERY the grounding explains, not the raw cosine.
197
+ // Root gists are unit vectors, but their magnitudes are recoverable from
198
+ // the byte lengths (‖·‖ = √len under the linear fold):
199
+ // cos = shared/√(lenQ·lenG), so shared/lenQ = cos·√(lenG/lenQ).
200
+ // The raw cosine punished honest containment — a query fully inside a
201
+ // longer grounded answer scored √(lenQ/lenG) and was refused — and let a
202
+ // long answer sharing only scaffolding pass; the query-relative fraction
203
+ // measures exactly what the reach bar means: how much of THE QUERY the
204
+ // store accounts for.
205
+ // Chance similarity survives the length conversion AMPLIFIED: the same
206
+ // √(lenG/lenQ) factor that converts an honest shared fraction into a
207
+ // query-relative one multiplies the estimator/chance floor too, so a long
208
+ // stored form (√(lenG/lenQ) ≈ 10 at 100×) lifted a noise-level cosine past
209
+ // the reach bar and grounded pure gibberish (observed). Only the
210
+ // ABOVE-CHANCE part of the similarity is evidence of shared content —
211
+ // subtract the significance bar (3/√D, §8.3) before converting. Derived
212
+ // from the existing bars; never tuned.
213
+ const sig = significanceBar(ctx.store.D);
214
+ const reach = reachThreshold(ctx.space.maxGroup);
215
+ const fracOfQuery = (cos: number, otherLen: number): number =>
216
+ Math.min(
217
+ 1,
218
+ Math.max(0, cos - sig) *
219
+ Math.sqrt(otherLen / Math.max(1, query.length)),
220
+ );
221
+
141
222
  // 2. Scaffolding-dominated.
142
- if (top.score >= significanceBar(ctx.store.D)) {
223
+ if (top.score >= sig) {
143
224
  const N = corpusN(ctx);
144
225
  const minVote = consensusFloor(N);
145
226
  // The committed points of attention ARE the shared climb's roots (same
@@ -148,7 +229,15 @@ export async function recallByResonance(
148
229
  const forest = (await pre.attention()).roots;
149
230
  if (forest.length > 0 && forest[0].vote >= minVote) {
150
231
  const g = await project(ctx, forest[0].anchor, queryGist);
151
- if (g) {
232
+ // The anchor cleared the consensus floor, but the floor prices the
233
+ // ANCHOR's evidence, not the projection's: a junk attractor can clear
234
+ // it and project a PIECE OF THE QUERY back at it (the observed
235
+ // "buenos días in English" → "English" fragment). A projection that
236
+ // is a proper byte-subspan of the query restates part of the question
237
+ // — never an answer (the same principle as `restates` above, extended
238
+ // to fragments). Genuine anchor groundings — longer than the query,
239
+ // or disjoint from it — pass untouched.
240
+ if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
152
241
  return ground(
153
242
  g,
154
243
  "scaffolding-dominated query — ground the consensus-climb anchor",
@@ -159,23 +248,13 @@ export async function recallByResonance(
159
248
  }
160
249
  }
161
250
 
162
- // 3. Last resort — gated on the FRACTION OF THE QUERY the grounding
163
- // explains, not the raw cosine. Root gists are unit vectors, but their
164
- // magnitudes are recoverable from the byte lengths (‖·‖ = √len under the
165
- // linear fold): cos = shared/√(lenQ·lenG), so shared/lenQ = cos·√(lenG/lenQ).
166
- // The raw cosine punished honest containment — a query fully inside a
167
- // longer grounded answer scored √(lenQ/lenG) and was refused — and let a
168
- // long answer sharing only scaffolding pass; the query-relative fraction
169
- // measures exactly what the reach bar means: how much of THE QUERY the
170
- // store accounts for.
171
- const fracOfQuery = (cos: number, otherLen: number): number =>
172
- Math.min(1, cos * Math.sqrt(otherLen / Math.max(1, query.length)));
251
+ // 3. Last resort — the nearest grounded whole-query hit, same gate.
173
252
  for (const h of whole) {
174
253
  const g = await project(ctx, h.id, queryGist);
175
254
  if (g) {
176
255
  if (
177
256
  fracOfQuery(cosine(queryGist, gistOf(ctx, g)), g.length) >=
178
- reachThreshold(ctx.space.maxGroup)
257
+ reach
179
258
  ) {
180
259
  return ground(
181
260
  g,
@@ -186,18 +265,20 @@ export async function recallByResonance(
186
265
  }
187
266
  }
188
267
  }
189
- // The refusal/echo decision, in the same query-relative units the top
190
- // hit's magnitude read from the store (contentLen: √bytes IS the linear
191
- // fold's gist norm), never from re-reading its bytes.
192
- // The magnitude read SATURATES at the decision point: frac reaches the
193
- // reach bar exactly when lenH = lenQ·(reach/score)², so the walk never
194
- // needs to see past that a huge conversation root costs a capped read,
195
- // and a clamped return decides "pass" identically (frac reach).
196
- const reach = reachThreshold(ctx.space.maxGroup);
197
- const lenCap = Math.ceil(
198
- query.length * (reach / Math.max(top.score, 1e-6)) ** 2,
199
- ) + 1;
200
- if (fracOfQuery(top.score, ctx.store.contentLen(top.id, lenCap)) < reach) {
268
+ // The refusal/echo decision. The echo returns a stored form's bytes AS
269
+ // the answer a near-identity claim about the query and identity-grade
270
+ // decisions are never made on an estimated score ("approximate scores may
271
+ // rank and propose; they may never decide", §6.2): the RaBitQ estimate
272
+ // overshooting the reach bar echoed a WRONG-entity neighbour ("capital of
273
+ // Zamunda?" echoed the Armenia fact, observed). The bytes are read
274
+ // anyway to be echoed, so the decision uses their EXACT fold: one river
275
+ // fold of the top hit, measured in the same query-relative,
276
+ // chance-corrected units as the tier above.
277
+ const topBytes = read(ctx, top.id);
278
+ const exact = topBytes.length > 0
279
+ ? cosine(queryGist, gistOf(ctx, topBytes))
280
+ : 0;
281
+ if (fracOfQuery(exact, topBytes.length) < reach) {
201
282
  return ground(
202
283
  null,
203
284
  "below reach threshold — nothing in the store relates to this query",
@@ -205,9 +286,19 @@ export async function recallByResonance(
205
286
  0,
206
287
  );
207
288
  }
289
+ // Echoing the query's own bytes back at it is not an echo of a RELATED
290
+ // form — it is the query restated, which answers nothing.
291
+ if (restates(topBytes)) {
292
+ return ground(
293
+ null,
294
+ "the nearest form IS the query itself — restating it answers nothing",
295
+ [],
296
+ 0,
297
+ );
298
+ }
208
299
  // Honest echo.
209
300
  return ground(
210
- read(ctx, top.id),
301
+ topBytes,
211
302
  "last resort: the nearest resonant form's own bytes (echo, not grounded)",
212
303
  [],
213
304
  0,