@hviana/sema 0.5.3 → 0.5.4

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 (143) hide show
  1. package/AGENTS.md +6 -6
  2. package/HOW_IT_WORKS.md +16 -0
  3. package/jsr.json +1 -1
  4. package/package.json +1 -1
  5. package/src/mind/match.ts +81 -0
  6. package/src/mind/mechanisms/recall.ts +29 -3
  7. package/test/76-reference-binding.test.mjs +72 -0
  8. package/dist/example/demo.d.ts +0 -1
  9. package/dist/example/demo.js +0 -39
  10. package/dist/example/train_base.d.ts +0 -93
  11. package/dist/example/train_base.js +0 -2269
  12. package/dist/src/alphabet.d.ts +0 -7
  13. package/dist/src/alphabet.js +0 -33
  14. package/dist/src/alu/src/alu.d.ts +0 -185
  15. package/dist/src/alu/src/alu.js +0 -440
  16. package/dist/src/alu/src/expr.d.ts +0 -61
  17. package/dist/src/alu/src/expr.js +0 -318
  18. package/dist/src/alu/src/index.d.ts +0 -11
  19. package/dist/src/alu/src/index.js +0 -19
  20. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  21. package/dist/src/alu/src/kernel-arith.js +0 -264
  22. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  23. package/dist/src/alu/src/kernel-bits.js +0 -152
  24. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  25. package/dist/src/alu/src/kernel-logic.js +0 -60
  26. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  27. package/dist/src/alu/src/kernel-nd.js +0 -208
  28. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  29. package/dist/src/alu/src/kernel-numeric.js +0 -366
  30. package/dist/src/alu/src/operation.d.ts +0 -168
  31. package/dist/src/alu/src/operation.js +0 -189
  32. package/dist/src/alu/src/parser.d.ts +0 -221
  33. package/dist/src/alu/src/parser.js +0 -577
  34. package/dist/src/alu/src/resonance.d.ts +0 -55
  35. package/dist/src/alu/src/resonance.js +0 -126
  36. package/dist/src/alu/src/text.d.ts +0 -31
  37. package/dist/src/alu/src/text.js +0 -73
  38. package/dist/src/alu/src/value.d.ts +0 -109
  39. package/dist/src/alu/src/value.js +0 -300
  40. package/dist/src/alu/test/alu.test.d.ts +0 -1
  41. package/dist/src/alu/test/alu.test.js +0 -764
  42. package/dist/src/bytes.d.ts +0 -14
  43. package/dist/src/bytes.js +0 -59
  44. package/dist/src/canon.d.ts +0 -45
  45. package/dist/src/canon.js +0 -85
  46. package/dist/src/config.d.ts +0 -111
  47. package/dist/src/config.js +0 -91
  48. package/dist/src/derive/src/deduction.d.ts +0 -136
  49. package/dist/src/derive/src/deduction.js +0 -159
  50. package/dist/src/derive/src/index.d.ts +0 -8
  51. package/dist/src/derive/src/index.js +0 -11
  52. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  53. package/dist/src/derive/src/priority-queue.js +0 -73
  54. package/dist/src/derive/src/rewrite.d.ts +0 -56
  55. package/dist/src/derive/src/rewrite.js +0 -100
  56. package/dist/src/derive/src/trie.d.ts +0 -90
  57. package/dist/src/derive/src/trie.js +0 -217
  58. package/dist/src/derive/test/derive.test.d.ts +0 -1
  59. package/dist/src/derive/test/derive.test.js +0 -122
  60. package/dist/src/extension.d.ts +0 -37
  61. package/dist/src/extension.js +0 -7
  62. package/dist/src/geometry.d.ts +0 -233
  63. package/dist/src/geometry.js +0 -1089
  64. package/dist/src/index.d.ts +0 -17
  65. package/dist/src/index.js +0 -19
  66. package/dist/src/ingest-cache.d.ts +0 -41
  67. package/dist/src/ingest-cache.js +0 -165
  68. package/dist/src/meter.d.ts +0 -176
  69. package/dist/src/meter.js +0 -274
  70. package/dist/src/mind/articulation.d.ts +0 -6
  71. package/dist/src/mind/articulation.js +0 -99
  72. package/dist/src/mind/attention.d.ts +0 -430
  73. package/dist/src/mind/attention.js +0 -2418
  74. package/dist/src/mind/bridge.d.ts +0 -37
  75. package/dist/src/mind/bridge.js +0 -907
  76. package/dist/src/mind/canonical.d.ts +0 -34
  77. package/dist/src/mind/canonical.js +0 -93
  78. package/dist/src/mind/graph-search.d.ts +0 -294
  79. package/dist/src/mind/graph-search.js +0 -996
  80. package/dist/src/mind/index.d.ts +0 -9
  81. package/dist/src/mind/index.js +0 -5
  82. package/dist/src/mind/junction.d.ts +0 -137
  83. package/dist/src/mind/junction.js +0 -358
  84. package/dist/src/mind/learning.d.ts +0 -75
  85. package/dist/src/mind/learning.js +0 -265
  86. package/dist/src/mind/match.d.ts +0 -351
  87. package/dist/src/mind/match.js +0 -959
  88. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  89. package/dist/src/mind/mechanisms/alu.js +0 -36
  90. package/dist/src/mind/mechanisms/cast.d.ts +0 -89
  91. package/dist/src/mind/mechanisms/cast.js +0 -1186
  92. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  93. package/dist/src/mind/mechanisms/confluence.js +0 -271
  94. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  95. package/dist/src/mind/mechanisms/cover.js +0 -232
  96. package/dist/src/mind/mechanisms/extraction.d.ts +0 -33
  97. package/dist/src/mind/mechanisms/extraction.js +0 -327
  98. package/dist/src/mind/mechanisms/prefix-completion.d.ts +0 -22
  99. package/dist/src/mind/mechanisms/prefix-completion.js +0 -243
  100. package/dist/src/mind/mechanisms/recall.d.ts +0 -16
  101. package/dist/src/mind/mechanisms/recall.js +0 -460
  102. package/dist/src/mind/mechanisms/reference.d.ts +0 -6
  103. package/dist/src/mind/mechanisms/reference.js +0 -296
  104. package/dist/src/mind/mind.d.ts +0 -386
  105. package/dist/src/mind/mind.js +0 -754
  106. package/dist/src/mind/pipeline-mechanism.d.ts +0 -238
  107. package/dist/src/mind/pipeline-mechanism.js +0 -622
  108. package/dist/src/mind/pipeline.d.ts +0 -49
  109. package/dist/src/mind/pipeline.js +0 -343
  110. package/dist/src/mind/primitives.d.ts +0 -85
  111. package/dist/src/mind/primitives.js +0 -333
  112. package/dist/src/mind/rationale.d.ts +0 -139
  113. package/dist/src/mind/rationale.js +0 -163
  114. package/dist/src/mind/reasoning.d.ts +0 -42
  115. package/dist/src/mind/reasoning.js +0 -305
  116. package/dist/src/mind/recognition.d.ts +0 -20
  117. package/dist/src/mind/recognition.js +0 -696
  118. package/dist/src/mind/resonance.d.ts +0 -35
  119. package/dist/src/mind/resonance.js +0 -365
  120. package/dist/src/mind/trace.d.ts +0 -15
  121. package/dist/src/mind/trace.js +0 -73
  122. package/dist/src/mind/traverse.d.ts +0 -196
  123. package/dist/src/mind/traverse.js +0 -806
  124. package/dist/src/mind/types.d.ts +0 -418
  125. package/dist/src/mind/types.js +0 -179
  126. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  127. package/dist/src/rabitq-ivf/src/database.js +0 -201
  128. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  129. package/dist/src/rabitq-ivf/src/index.js +0 -4
  130. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  131. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  132. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  133. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  134. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  135. package/dist/src/rabitq-ivf/src/rabitq.js +0 -313
  136. package/dist/src/sema.d.ts +0 -41
  137. package/dist/src/sema.js +0 -77
  138. package/dist/src/store-sqlite.d.ts +0 -184
  139. package/dist/src/store-sqlite.js +0 -942
  140. package/dist/src/store.d.ts +0 -762
  141. package/dist/src/store.js +0 -1823
  142. package/dist/src/vec.d.ts +0 -31
  143. package/dist/src/vec.js +0 -109
package/AGENTS.md CHANGED
@@ -179,12 +179,12 @@ matchers (`locate`, `alignRuns`, `alignGraded`, `alignAround`/`frameSlots`,
179
179
  `bestHaloMate`, `haloSiblings`, `analogyStrength` and its structural tier
180
180
  `sharedFrameStrength`, `spanHalo`, `spanSynonymStrength`), projections
181
181
  (`follow`, `reverseContext`, `project`, `conceptHop`), the span-shape family
182
- (`skillExemplar`, `isSpanShaped`, `containsSpan`), and the two STRUCTURAL gates
183
- that are byte predicates rather than derived thresholds (`isSpanShaped`,
184
- `carriesFillers`). `mind/traverse.ts` owns the graph readings (`edgeAncestors`,
185
- `reachOf`, `chooseNext`/`chooseAmong`, `guidedFirst`, `leadsSomewhere`,
186
- `allWindowsAreScaffolding`, `formsOpenedBy`) and the corpus scale (`corpusN`,
187
- `hubBound`, `hubCap`, `atomReach`).
182
+ (`skillExemplar`, `isSpanShaped`, `containsSpan`), and the STRUCTURAL gates that
183
+ are byte predicates rather than derived thresholds (`isSpanShaped`,
184
+ `carriesFillers`, `voicesDisplacedFiller`). `mind/traverse.ts` owns the graph
185
+ readings (`edgeAncestors`, `reachOf`, `chooseNext`/`chooseAmong`, `guidedFirst`,
186
+ `leadsSomewhere`, `allWindowsAreScaffolding`, `formsOpenedBy`) and the corpus
187
+ scale (`corpusN`, `hubBound`, `hubCap`, `atomReach`).
188
188
 
189
189
  _Follow it:_ before writing a new generalising mechanism, express it as a
190
190
  (matcher, direction, gate) triple. If those already exist, the mechanism is a
package/HOW_IT_WORKS.md CHANGED
@@ -4314,6 +4314,22 @@ The tier accounts for exactly the query span whose evidence carried the winning
4314
4314
  point of attention (§17.6's `start`–`end`), not the whole query — a consensus
4315
4315
  vote for "ice" among scaffolding does not explain the word "steel". One CONCEPT.
4316
4316
 
4317
+ One further refusal is specific to grounding _through an anchor_. This tier
4318
+ voices the anchor's continuation, so when the query is the same structure as the
4319
+ anchor with one position filled differently, that continuation speaks the
4320
+ **anchor's** occupant of the position — fluent, specific, and about the wrong
4321
+ thing (`How do I compile main.c?` answered `Run gcc hello.c`). A displaced slot
4322
+ alone does not license the refusal, because it cannot separate a different
4323
+ _thing_ from a different _word_: `symbol` ← `formula` has exactly the shape
4324
+ `main` ← `hello` does, and refusing it silences a correct answer phrased in the
4325
+ corpus's own words. The discriminator is whether the projection still shares
4326
+ perceivable content with the query outside that position — one river window is
4327
+ enough. `The chemical formula for water is H2O.` shares `for water`;
4328
+ `Run gcc hello.c` shares only `.c`, two bytes, below the scale at which overlap
4329
+ stops being chance. The gate refuses only; putting the asker's own occupant in
4330
+ its place is a stronger claim needing the corpus's carriage evidence, which is
4331
+ §20.5's business.
4332
+
4317
4333
  ### 21.4 Tier 3 — the nearest grounded hit, at the query-relative fraction
4318
4334
 
4319
4335
  Walk the hits nearest-first and ground the first whose grounding explains enough
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://jsr.io/schema/config-file.v1.json",
3
3
  "name": "@hviana/sema",
4
- "version": "0.5.3",
4
+ "version": "0.5.4",
5
5
  "exports": "./src/index.ts"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hviana/sema",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Sema: a non-parametric, instance-based reasoning system.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
package/src/mind/match.ts CHANGED
@@ -612,6 +612,87 @@ export function frameSlots(
612
612
  return { id, slots, matched: spans, covered };
613
613
  }
614
614
 
615
+ /** THE DISPLACED-FILLER GATE: does `projection` speak the ANCHOR's occupant of
616
+ * a position the query fills differently?
617
+ *
618
+ * A mechanism grounding through an anchor voices that anchor's continuation.
619
+ * When the query is the same structure as the anchor with one position filled
620
+ * differently — a different filename, a different word — the anchor's
621
+ * continuation is ABOUT THE ANCHOR'S occupant, and voicing it answers a
622
+ * question the asker did not ask. It is worse than silence, because it is
623
+ * fluent and specific and wrong:
624
+ *
625
+ * trained `How do I compile hello.c?` -> `Run gcc hello.c`
626
+ * asked `How do I compile main.c?`
627
+ * voiced `Run gcc hello.c` <- the corpus's file, not the asker's
628
+ *
629
+ * The same shape on the trained 15.7M-node store: `How do you say 'flurbish'
630
+ * in French?` answers "the way to say hello is \"Bonjour\"".
631
+ *
632
+ * THIS IS NOT THE RESTATED-FRAGMENT GUARD. That one asks whether the
633
+ * projection is a piece of the QUERY; this asks whether it is a piece of the
634
+ * ANCHOR that the query displaced. Neither implies the other, and the
635
+ * observed failures pass the restatement guard cleanly.
636
+ *
637
+ * Three conditions, all byte-exact and all necessary:
638
+ *
639
+ * 1. the query and the anchor must be ONE STRUCTURE — what they share has to
640
+ * dominate the query, or the query is not a variant of the anchor at all
641
+ * and the anchor's occupant of anything is beside the point;
642
+ * 2. both sides of the position must reach one river window — below it byte
643
+ * overlap is chance, not evidence (the floor identityBar and the bridge's
644
+ * attestedQ both draw);
645
+ * 3. the projection must voice the anchor's filler and NOT the query's
646
+ * referent. Voicing both is a projection that carried the asker's own
647
+ * occupant through, which is exactly what a licensed reference does and
648
+ * must stay allowed;
649
+ * 4. and the projection must share NO perceivable content with the query
650
+ * outside that position — no run of one river window.
651
+ *
652
+ * GATE 4 IS WHAT SEPARATES A DIFFERENT THING FROM A DIFFERENT WORD, and
653
+ * without it this refuses correct answers. A displaced slot alone cannot
654
+ * tell them apart: `symbol` <- `formula` and `main` <- `hello` are the same
655
+ * shape to the matcher — one substitution slot, frame dominating. Measured
656
+ * on the trained store, gates 1-3 alone silenced
657
+ *
658
+ * Q `What is the chemical symbol for water?`
659
+ * A `The chemical formula for water is H2O.`
660
+ *
661
+ * which is right, and merely phrased in the corpus's own words. The answer
662
+ * shares `the chemical ` and ` for water` with the question, so it is plainly
663
+ * about what was asked. `Run gcc hello.c` against `How do I compile main.c?`
664
+ * shares nothing but `.c` — two bytes, below the window where overlap stops
665
+ * being chance — so it is not about what was asked at all. No new constant:
666
+ * W is the same floor identityBar, attestedQ and the site test already draw. */
667
+ export function voicesDisplacedFiller(
668
+ ctx: MindContext,
669
+ query: Uint8Array,
670
+ anchor: Uint8Array,
671
+ projection: Uint8Array,
672
+ ): boolean {
673
+ const W = ctx.space.maxGroup;
674
+ const inst = frameSlots(ctx, query, anchor, 0);
675
+ if (inst === null) return false;
676
+ if (!dominates(inst.covered, query.length)) return false;
677
+ for (const slot of inst.slots) {
678
+ if (slot.kind !== "substitution") continue;
679
+ if (slot.qe - slot.qs < W || slot.filler.length < W) continue;
680
+ if (indexOf(projection, slot.filler, 0) < 0) continue;
681
+ const referent = query.subarray(slot.qs, slot.qe);
682
+ if (indexOf(projection, referent, 0) >= 0) continue;
683
+ // Gate 4: does the projection still speak about the query's FRAME? A run
684
+ // of one window anywhere outside the displaced position is enough — the
685
+ // answer is then about the thing that was asked, in the corpus's own
686
+ // wording. Sharing nothing means it is about something else.
687
+ const shared = alignRuns(ctx, query, projection).some((r) =>
688
+ r.qe - r.qs >= W && (r.qe <= slot.qs || r.qs >= slot.qe)
689
+ );
690
+ if (shared) continue;
691
+ return true;
692
+ }
693
+ return false;
694
+ }
695
+
615
696
  /** Whether every member is byte-distinct from the others. */
616
697
  export function distinct(items: readonly Uint8Array[]): boolean {
617
698
  for (let i = 0; i < items.length; i++) {
@@ -17,7 +17,12 @@ import type { MindContext } from "../types.js";
17
17
  import { gistOf, read, resolve } from "../primitives.js";
18
18
  import { bytesEqual, indexOf } from "../../bytes.js";
19
19
  import { allWindowsAreScaffolding, corpusN, hubBound } from "../traverse.js";
20
- import { follow, project, reverseContext } from "../match.js";
20
+ import {
21
+ follow,
22
+ project,
23
+ reverseContext,
24
+ voicesDisplacedFiller,
25
+ } from "../match.js";
21
26
  import { CONCEPT, STEP } from "../graph-search.js";
22
27
  import { unexplainedLabel } from "../rationale.js";
23
28
  import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
@@ -349,7 +354,28 @@ export async function recallByResonance(
349
354
  (dominates(forest[0].breadth, 1) && forest[0].peak > Math.LN2))
350
355
  ) {
351
356
  const g = await project(ctx, forest[0].anchor, queryGist);
352
- // The anchor cleared the consensus floor, but the floor prices the
357
+ // THE ANCHOR'S OCCUPANT IS NOT THE ASKER'S. This tier grounds an anchor
358
+ // the climb elected, and voices that anchor's continuation. When the
359
+ // query is the same structure with one position filled differently, the
360
+ // continuation speaks the ANCHOR's occupant of that position — a fluent,
361
+ // specific, wrong answer (see voicesDisplacedFiller). Refusing here is
362
+ // silence, not a redirection: putting the asker's referent in its place
363
+ // is a stronger claim that needs the corpus's own carriage evidence, and
364
+ // that is `reference`'s job.
365
+ const anchorBytes = read(
366
+ ctx,
367
+ forest[0].anchor,
368
+ query.length * ctx.space.maxGroup + 1,
369
+ );
370
+ if (g !== null && voicesDisplacedFiller(ctx, query, anchorBytes, g)) {
371
+ ctx.trace?.step(
372
+ "displacedFiller",
373
+ [rItem(query, "query"), rNode(ctx, forest[0].anchor, "anchor")],
374
+ [rItem(g, "withheld")],
375
+ "refused — the anchor's continuation speaks the anchor's own " +
376
+ "occupant of a position this query fills differently",
377
+ );
378
+ } // The anchor cleared the consensus floor, but the floor prices the
353
379
  // ANCHOR's evidence, not the projection's: a junk attractor can clear
354
380
  // it and project a PIECE OF THE QUERY back at it (the observed
355
381
  // "buenos días in English" → "English" fragment). A projection that
@@ -357,7 +383,7 @@ export async function recallByResonance(
357
383
  // — never an answer (the same principle as `restates` above, extended
358
384
  // to fragments). Genuine anchor groundings — longer than the query,
359
385
  // or disjoint from it — pass untouched.
360
- if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
386
+ else if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
361
387
  return ground(
362
388
  g,
363
389
  "scaffolding-dominated query — ground the consensus-climb anchor",
@@ -403,6 +403,78 @@ test("a referent may not be lifted out of the engine's OWN reply", async () => {
403
403
  await m.store.close();
404
404
  });
405
405
 
406
+ // ── THE DISPLACED-FILLER GATE ──────────────────────────────────────────────
407
+ //
408
+ // Reference needs two instances before it will voice a binding: one instance
409
+ // agrees with nothing, so nothing distinguishes "Run gcc hello.c" (a carriage
410
+ // that follows the filename) from "hello.c needs -lm" (a fact about that one
411
+ // file). Abstaining there is right.
412
+ //
413
+ // What is NOT right is what used to happen next. Recall's
414
+ // scaffolding-dominated tier grounds the anchor the climb elected and voices
415
+ // that anchor's continuation — which speaks the ANCHOR's occupant of the very
416
+ // position the query fills differently. Measured on a one-example corpus,
417
+ // `How do I compile main.c?` answered `Run gcc hello.c`; on the trained
418
+ // 15.7M-node store, `How do you say 'flurbish' in French?` answers "the way to
419
+ // say hello is \"Bonjour\"". Fluent, specific, and about the wrong thing.
420
+ //
421
+ // The gate REFUSES ONLY. It never puts the asker's referent in the corpus's
422
+ // place — that is a stronger claim needing the corpus's own carriage evidence,
423
+ // which is what the licence above supplies. Where the corpus cannot license a
424
+ // binding, silence is the honest degradation.
425
+
426
+ test("a projection may not voice the ANCHOR's occupant of a displaced slot", async () => {
427
+ const m = await mindWith([
428
+ ["How do I compile hello.c?", "Run gcc hello.c"],
429
+ ["What is the capital of France?", "Paris"],
430
+ ]);
431
+ const out = clean((await m.respond("How do I compile main.c?")).bytes).trim();
432
+ assert.ok(
433
+ !out.includes("hello"),
434
+ `voiced the corpus's filename for the asker's file: ${JSON.stringify(out)}`,
435
+ );
436
+ assert.equal(out, "", "the honest degradation here is silence");
437
+ await m.store.close();
438
+ });
439
+
440
+ test("the gate is not the restated-fragment guard", async () => {
441
+ // The refused projection is not a piece of the QUERY — it passes the
442
+ // restatement guard cleanly — it is a piece of the ANCHOR the query
443
+ // displaced. Neither guard implies the other.
444
+ const m = await mindWith([
445
+ ["How do I compile hello.c?", "Run gcc hello.c"],
446
+ ["What is the capital of France?", "Paris"],
447
+ ]);
448
+ const q = "How do I compile main.c?";
449
+ assert.ok(
450
+ !q.includes("Run gcc hello.c"),
451
+ "fixture broken: the projection must NOT be a query fragment",
452
+ );
453
+ assert.equal(clean((await m.respond(q)).bytes).trim(), "");
454
+ await m.store.close();
455
+ });
456
+
457
+ test("a projection that carries the ASKER's own occupant is untouched", async () => {
458
+ // Voicing the anchor's filler AND the query's referent is what a licensed
459
+ // reference does; the gate must not touch it.
460
+ const m = await mindWith(CARRIED);
461
+ const r = await m.respond("How do I compile main.c?");
462
+ assert.equal(clean(r.bytes).trim(), "Run gcc main.c");
463
+ assert.equal(r.provenance, "reference");
464
+ await m.store.close();
465
+ });
466
+
467
+ test("a sub-quantum difference does not trip the gate", async () => {
468
+ // Below one river window byte overlap is chance, not a displaced slot.
469
+ const m = await mindWith([
470
+ ["What is the capital of France?", "Paris"],
471
+ ["Who wrote Hamlet?", "Shakespeare"],
472
+ ]);
473
+ const out = clean((await m.respond("What is the capital of Franc?")).bytes);
474
+ assert.match(out, /Paris/, `a near-identical query lost its answer: ${out}`);
475
+ await m.store.close();
476
+ });
477
+
406
478
  // ── NO HIJACK ──────────────────────────────────────────────────────────────
407
479
 
408
480
  test("a trained query is answered by its own edge, not by reference", async () => {
@@ -1 +0,0 @@
1
- export {};
@@ -1,39 +0,0 @@
1
- // demo.ts — one short session that drives the WHOLE pipeline from one memory.
2
- //
3
- // We give Sema a handful of plain notes, then ask things that no single note
4
- // answers. The headline query is the third one: from three worked examples Sema
5
- // learns the shape of "X was painted by Y", lifts the painter out of a sentence
6
- // it has NEVER seen, and then — in the same pass — reasons forward to a separate
7
- // fact about that painter. The reply contains no word from the question. That is
8
- // retrieval, generalization, and reasoning composing as a single act, with every
9
- // step traceable back to the notes behind it.
10
- import { Mind } from "../src/index.js";
11
- import { SQliteStore } from "../src/store-sqlite.js";
12
- async function main() {
13
- const mind = new Mind({ store: new SQliteStore({ path: ":memory:" }) });
14
- const ask = async (q) => (await mind.respondText(q)).trim();
15
- // ── Jot down what we know. Each line is just (context → what follows). ──
16
- await mind.ingest([
17
- // One relation, shown three times — a pattern taught purely by example:
18
- ["The Mona Lisa was painted by Leonardo da Vinci.", "Leonardo da Vinci"],
19
- ["The Starry Night was painted by Vincent van Gogh.", "Vincent van Gogh"],
20
- [
21
- "The Night Watch was painted by Rembrandt van Rijn.",
22
- "Rembrandt van Rijn",
23
- ],
24
- // One stray fact, keyed on a name none of the examples mention:
25
- ["Pablo Picasso", "Pablo Picasso co-founded the Cubist movement"],
26
- ]);
27
- // 1) GENERALIZE — apply the learned pattern to an unseen sentence and read out
28
- // the painter. "Pablo Picasso" was never given as an answer; Sema locates it
29
- // by analogy to the three examples.
30
- console.log(await ask("The Weeping Woman was painted by Pablo Picasso."));
31
- // → "Pablo Picasso co-founded the Cubist movement"
32
- // …and, having found the painter, it KEEPS GOING: the name bridges into the
33
- // one fact it holds about him. The answer appears in no word of the question.
34
- // 2) COMPUTE — exact arithmetic, grounded right where the notes go silent.
35
- console.log(await ask("a museum charges 12*4 for a family ticket"));
36
- // → "48"
37
- await mind.store.close();
38
- }
39
- main();
@@ -1,93 +0,0 @@
1
- export interface Episode {
2
- context: string;
3
- continuation: string;
4
- }
5
- export type TrainingItem = string | Episode;
6
- /** Dedup + trim a concept's items: drop empty/degenerate pairs and exact
7
- * repeats so a concept never deposits the same form twice. */
8
- export declare function refineItems(items: TrainingItem[]): TrainingItem[];
9
- /** One normalized SmolSent row. */
10
- export interface SmolSentRow {
11
- src: string;
12
- trg: string;
13
- sl: string;
14
- tl: string;
15
- }
16
- /** Normalize a raw datasets-server row into a SmolSentRow, or null when it lacks
17
- * both sides or a side is implausibly large (a dump, not a sentence). */
18
- export declare function toSmolSentRow(row: unknown): SmolSentRow | null;
19
- /** Translate ONE SmolSent pair into SEMA facts: the two sentences are one
20
- * meaning in two languages, so bind them BOTH ways. refineItems drops the
21
- * degenerate case where src === trg. */
22
- export declare function smolSentRowToItems(row: SmolSentRow): TrainingItem[];
23
- /** One normalized Aya row. */
24
- export interface AyaRow {
25
- inputs: string;
26
- targets: string;
27
- language: string;
28
- }
29
- /** Normalize a raw datasets-server row object into an AyaRow, or null when it
30
- * lacks a usable prompt/answer or a field is implausibly large (a dump, not a
31
- * cognitive example). Trims surrounding whitespace; keeps inner text verbatim
32
- * (human prose, possibly multi-paragraph). */
33
- export declare function toAyaRow(row: unknown): AyaRow | null;
34
- /** Translate ONE Aya row into SEMA training items. A row is a single human
35
- * (question → answer) exchange — exactly one FACT, the (inputs → targets) edge.
36
- * No standalone-answer experience and no one-exchange "cumulative" walk: a lone
37
- * Q→A is not multi-turn, and both would only replicate the same edge. */
38
- export declare function ayaRowToItems(row: AyaRow): TrainingItem[];
39
- /** A single oasst2 message node (the fields we use; the tree nests via replies). */
40
- interface OasstNode {
41
- role?: string;
42
- text?: string;
43
- rank?: number | null;
44
- deleted?: boolean;
45
- replies?: OasstNode[];
46
- }
47
- /** One conversational turn extracted from a tree. */
48
- export interface OasstTurn {
49
- role: string;
50
- text: string;
51
- }
52
- /** Collapse a conversation tree to ONE linear path: at each node, descend into
53
- * its best-ranked, non-deleted reply (rank 0 preferred; unranked sorts last).
54
- * Returns the ordered turns (already strictly alternating in this corpus). */
55
- export declare function bestOasstPath(root: OasstNode): OasstTurn[];
56
- /** Translate ONE multi-turn oasst2 conversation into SEMA training items.
57
- *
58
- * This is the ONE stage where cumulative continuous context is truly necessary:
59
- * the data is a real multi-turn dialogue, and what must be learned is how each
60
- * turn follows from the WHOLE conversation so far — not from the previous turn
61
- * alone. The conversation is emitted ONLY as the accumulated walk; standalone
62
- * turn experiences and local adjacent-pair facts are NOT emitted (they are
63
- * subsumed by it and would merely replicate the content).
64
- *
65
- * The walk is the pattern proven in test/13-conversation.test.mjs
66
- * ("teachConversation"): each turn is the continuation of all prior turns,
67
- * with BARE turn text — NO "User:/Assistant:" labels. The SHAPE is identical
68
- * (cumulative context → next turn); the join string is not, and does not need
69
- * to be — that file joins with nothing and this corpus joins with "\n" (see
70
- * `accumulate`). Saying "byte-for-byte", as this comment used to, invites the
71
- * reading that the two must agree on a separator. They must not agree,
72
- * because there is nothing to agree about: turn boundaries are offsets, and
73
- * the join string is just corpus text. Roles already
74
- * alternate by position in an oasst2 best-path (the root is a prompter), so a
75
- * label adds nothing the position does not, while a clean continuation matches
76
- * the test's recall (predictNext queries bare prior turns) and lets a turn share
77
- * its gist with the same text elsewhere (e.g. an Aya question stored bare).
78
- *
79
- * Returns [] for a conversation below the multi-turn threshold, so callers can
80
- * simply skip empties. */
81
- export declare function oasstConversationToItems(turns: OasstTurn[]): TrainingItem[];
82
- /** One normalized General-Knowledge row. */
83
- export interface GenKnowRow {
84
- question: string;
85
- answer: string;
86
- }
87
- /** Normalize a raw datasets-server row into a GenKnowRow, or null when it lacks
88
- * a usable question/answer or a side is implausibly large (corruption). */
89
- export declare function toGenKnowRow(row: unknown): GenKnowRow | null;
90
- /** Translate ONE General-Knowledge row into SEMA items: exactly one
91
- * (question → answer) FACT. refineItems drops a degenerate question === answer. */
92
- export declare function genKnowRowToItems(row: GenKnowRow): TrainingItem[];
93
- export {};