@hviana/sema 0.9.0 → 0.9.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.
Files changed (118) hide show
  1. package/AGENTS.md +7 -7
  2. package/dist/src/alu/src/index.d.ts +1 -1
  3. package/dist/src/alu/src/index.js +1 -1
  4. package/dist/src/alu/src/parser.js +2 -6
  5. package/dist/src/alu/src/resonance.d.ts +13 -0
  6. package/dist/src/alu/src/resonance.js +41 -0
  7. package/dist/src/alu/test/alu.test.js +39 -0
  8. package/dist/src/bytes.d.ts +6 -2
  9. package/dist/src/bytes.js +10 -4
  10. package/dist/src/canon.js +44 -0
  11. package/dist/src/geometry.d.ts +19 -1
  12. package/dist/src/geometry.js +125 -141
  13. package/dist/src/meter.d.ts +33 -0
  14. package/dist/src/meter.js +34 -1
  15. package/dist/src/mind/articulation.js +14 -1
  16. package/dist/src/mind/attention.d.ts +12 -0
  17. package/dist/src/mind/attention.js +44 -16
  18. package/dist/src/mind/bridge.js +3 -3
  19. package/dist/src/mind/derivation.d.ts +40 -0
  20. package/dist/src/mind/derivation.js +34 -0
  21. package/dist/src/mind/evidence.d.ts +24 -0
  22. package/dist/src/mind/evidence.js +90 -0
  23. package/dist/src/mind/graph-search.d.ts +89 -15
  24. package/dist/src/mind/graph-search.js +345 -174
  25. package/dist/src/mind/learning.js +1 -1
  26. package/dist/src/mind/mechanisms/cover.d.ts +19 -3
  27. package/dist/src/mind/mechanisms/cover.js +142 -61
  28. package/dist/src/mind/mechanisms/recall.js +10 -3
  29. package/dist/src/mind/mind.d.ts +6 -0
  30. package/dist/src/mind/mind.js +5 -2
  31. package/dist/src/mind/pipeline.d.ts +5 -1
  32. package/dist/src/mind/pipeline.js +220 -90
  33. package/dist/src/mind/primitives.d.ts +25 -5
  34. package/dist/src/mind/primitives.js +107 -44
  35. package/dist/src/mind/reasoning.d.ts +18 -4
  36. package/dist/src/mind/reasoning.js +487 -328
  37. package/dist/src/mind/recognition.js +29 -13
  38. package/dist/src/mind/resonance.js +1 -11
  39. package/dist/src/mind/traverse.d.ts +45 -5
  40. package/dist/src/mind/traverse.js +285 -8
  41. package/dist/src/mind/types.d.ts +16 -1
  42. package/dist/src/store-sqlite.d.ts +25 -0
  43. package/dist/src/store-sqlite.js +89 -1
  44. package/dist/src/store.d.ts +48 -4
  45. package/dist/src/store.js +86 -6
  46. package/docs/INDEX.md +20 -19
  47. package/docs/INVARIANTS.md +17 -16
  48. package/docs/architecture/bounded-reads.md +1 -1
  49. package/docs/architecture/caches.md +5 -4
  50. package/docs/architecture/closure.md +45 -5
  51. package/docs/architecture/cost-model.md +16 -0
  52. package/docs/architecture/evidence.md +113 -0
  53. package/docs/architecture/exact-vs-approximate.md +10 -9
  54. package/docs/architecture/factored-machinery.md +14 -13
  55. package/docs/architecture/fold-contract.md +51 -1
  56. package/docs/architecture/mechanism-market.md +21 -0
  57. package/docs/architecture/memoization.md +3 -3
  58. package/docs/architecture/meter.md +2 -1
  59. package/docs/architecture/saturation.md +12 -0
  60. package/docs/architecture/store.md +25 -2
  61. package/docs/failures/tempting-but-wrong.md +13 -2
  62. package/docs/harness/gates.md +12 -10
  63. package/docs/mechanisms/cover.md +23 -6
  64. package/jsr.json +1 -1
  65. package/package.json +1 -1
  66. package/src/alu/README.md +10 -2
  67. package/src/alu/src/index.ts +1 -0
  68. package/src/alu/src/parser.ts +6 -6
  69. package/src/alu/src/resonance.ts +42 -0
  70. package/src/alu/test/alu.test.ts +40 -0
  71. package/src/bytes.ts +13 -3
  72. package/src/canon.ts +40 -0
  73. package/src/geometry.ts +183 -154
  74. package/src/meter.ts +34 -1
  75. package/src/mind/articulation.ts +14 -2
  76. package/src/mind/attention.ts +47 -25
  77. package/src/mind/bridge.ts +3 -3
  78. package/src/mind/derivation.ts +77 -0
  79. package/src/mind/evidence.ts +107 -0
  80. package/src/mind/graph-search.ts +449 -221
  81. package/src/mind/learning.ts +1 -7
  82. package/src/mind/match.ts +1 -2
  83. package/src/mind/mechanisms/cast.ts +1 -2
  84. package/src/mind/mechanisms/cover.ts +207 -87
  85. package/src/mind/mechanisms/extraction.ts +1 -2
  86. package/src/mind/mechanisms/prefix-completion.ts +1 -1
  87. package/src/mind/mechanisms/recall.ts +17 -5
  88. package/src/mind/mechanisms/reference.ts +1 -1
  89. package/src/mind/mind.ts +9 -30
  90. package/src/mind/pipeline.ts +263 -104
  91. package/src/mind/primitives.ts +119 -43
  92. package/src/mind/reasoning.ts +611 -419
  93. package/src/mind/recognition.ts +24 -9
  94. package/src/mind/resonance.ts +2 -16
  95. package/src/mind/trace.ts +1 -1
  96. package/src/mind/traverse.ts +321 -8
  97. package/src/mind/types.ts +15 -11
  98. package/src/store-sqlite.ts +92 -1
  99. package/src/store.ts +113 -7
  100. package/test/105-derive-through-reports-its-refusal.test.mjs +8 -5
  101. package/test/106-the-join-fires.test.mjs +21 -0
  102. package/test/111-the-cover-assembly-is-counted.test.mjs +8 -5
  103. package/test/128-the-leads-somewhere-pair-agrees.test.mjs +18 -12
  104. package/test/136-the-two-named-limits.test.mjs +3 -2
  105. package/test/137-the-law-lives-once-and-below.test.mjs +21 -0
  106. package/test/148-exact-shortcuts-agree.test.mjs +188 -0
  107. package/test/149-the-closure-engine.test.mjs +138 -0
  108. package/test/150-the-join-is-output-sensitive.test.mjs +66 -0
  109. package/test/151-the-cover-pays-for-what-it-reaches.test.mjs +142 -0
  110. package/test/152-the-read-side-names-as-the-write-side.test.mjs +146 -0
  111. package/test/153-a-cheaper-bound-is-looked-at-first.test.mjs +155 -0
  112. package/test/154-the-question-names-the-step.test.mjs +281 -0
  113. package/test/24-generalization.test.mjs +32 -0
  114. package/test/36-bloom.test.mjs +53 -0
  115. package/test/37-cluster-dispersion-fusion.test.mjs +75 -0
  116. package/test/48-recognise-turn-connective.test.mjs +3 -2
  117. package/test/55-cost-meter.test.mjs +4 -4
  118. package/test/90-connector-read-cap.test.mjs +7 -7
@@ -2,25 +2,11 @@
2
2
  //
3
3
  // Address — bytes → node (perceive, foldTree, resolve)
4
4
  // Read — node → bytes (read)
5
- import { bytesToTree, contentFoldIncremental, gridToTree, hilbertBytes, stackGrids, } from "../geometry.js";
5
+ import { bytesToTree, contentFoldIncremental, contentIdentity, gridToTree, hilbertBytes, stackGrids, } from "../geometry.js";
6
6
  import { canonHash } from "../canon.js";
7
- import { bytesEqual } from "../bytes.js";
7
+ import { bytesEqual, concatBytes, latin1 } from "../bytes.js";
8
8
  import { ALL } from "./types.js";
9
9
  // ── Address: bytes → node ──────────────────────────────────────────────
10
- /** The content key of a byte span — one latin1 char per byte, an exact,
11
- * collision-free encoding. Spans on the perception path are query-scale
12
- * (windows, regions, candidate spans), so key construction is far cheaper
13
- * than the river fold it deduplicates. */
14
- export function latin1Key(bytes) {
15
- // Batched String.fromCharCode — avoids the O(n²) cost of repeated += on
16
- // potentially-large query spans, and stays well under the ~65536 arg limit.
17
- const n = bytes.length;
18
- let s = "";
19
- for (let i = 0; i < n; i += 4096) {
20
- s += String.fromCharCode(...bytes.subarray(i, Math.min(i + 4096, n)));
21
- }
22
- return s;
23
- }
24
10
  /** The {@link perceive} memo key: the span's content PLUS the boundary set it
25
11
  * was folded under. The tree is a function of BOTH — the same bytes fold
26
12
  * plainly with no boundaries and into a left-nested stable-prefix shape with
@@ -32,7 +18,7 @@ export function latin1Key(bytes) {
32
18
  * the boundary rendering is digits and commas, so no content byte can forge
33
19
  * the split. */
34
20
  export function perceiveKey(bytes, boundaries) {
35
- const k = latin1Key(bytes);
21
+ const k = latin1(bytes);
36
22
  return boundaries === undefined || boundaries.length === 0
37
23
  ? k
38
24
  : k + "\u0000" + boundaries.join(",");
@@ -111,7 +97,7 @@ export function perceiveDeposit(ctx, bytes, conversational = false) {
111
97
  .filter((L) => L >= 2 && L < bytes.length)
112
98
  .sort((a, b) => b - a);
113
99
  for (const L of lens) {
114
- const hit = ctx._depositTrees.get(latin1Key(bytes.subarray(0, L)));
100
+ const hit = ctx._depositTrees.get(latin1(bytes.subarray(0, L)));
115
101
  if (hit !== undefined) {
116
102
  prev = hit.content;
117
103
  break;
@@ -130,7 +116,7 @@ export function perceiveDeposit(ctx, bytes, conversational = false) {
130
116
  ctx._depositLens.clear();
131
117
  ctx._depositTrees.clear();
132
118
  }
133
- ctx._depositTrees.set(latin1Key(bytes), { content: folded.fold });
119
+ ctx._depositTrees.set(latin1(bytes), { content: folded.fold });
134
120
  ctx._depositLens.add(bytes.length);
135
121
  }
136
122
  return folded.tree;
@@ -203,14 +189,10 @@ export function foldTree(ctx, n, start, visit) {
203
189
  return { end, node };
204
190
  }
205
191
  let pos = start;
206
- let known = true;
207
192
  const kids = [];
208
193
  for (const k of n.kids) {
209
194
  const r = foldTree(ctx, k, pos, visit);
210
- if (r.node === null)
211
- known = false;
212
- else if (known)
213
- kids.push(r.node);
195
+ kids.push(r.node);
214
196
  pos = r.end;
215
197
  }
216
198
  // Same store-probe elision as the leaf case: a cached entry already names
@@ -218,17 +200,103 @@ export function foldTree(ctx, n, start, visit) {
218
200
  // id need not be re-derived. Using it also keeps a warm walk's ids
219
201
  // bit-identical to a cold walk's rather than re-deriving them from children
220
202
  // that may themselves have come from cache.
221
- const node = cached !== undefined
222
- ? cached.id
223
- : known
224
- ? ctx.store.findBranch(kids)
225
- : null;
203
+ const named = cached !== undefined
204
+ ? { id: cached.id, byBytes: false }
205
+ : branchNaming(ctx, kids, treeBytes(n));
206
+ const node = named.id;
226
207
  visit?.(n, start, pos, node);
227
208
  if (node !== null && ctx._resolvedSubtrees) {
228
209
  ctx._resolvedSubtrees.set(n, { id: node, len: pos - start });
229
210
  }
230
211
  return { end: pos, node };
231
212
  }
213
+ /** A perceived subtree's bytes, its leaves in order. */
214
+ function treeBytes(n) {
215
+ const parts = [];
216
+ const walk = (x) => {
217
+ if (x.kids === null)
218
+ parts.push(x.leaf ?? new Uint8Array(0));
219
+ else
220
+ for (const k of x.kids)
221
+ walk(k);
222
+ };
223
+ walk(n);
224
+ return concatBytes(parts);
225
+ }
226
+ /** The EXACT content-addressed node of a byte stream — `foldTree(perceive)`,
227
+ * read for identity alone.
228
+ *
229
+ * A fold names a branch only when every child is named, so identity needs the
230
+ * fold's SHAPE and the store's answer per node, never its vectors:
231
+ * {@link contentIdentity} walks the same shape (geometry.ts — one grouping
232
+ * rule, two algebras) and asks the store bottom-up, building no D-dimensional
233
+ * gist and leaving nothing in the perception memo. Each node is named the
234
+ * way the store's write side names it ({@link branchNaming}). `test/148` pins
235
+ * the agreement with the full fold over random and corpus spans. */
236
+ export function exactNode(ctx, bytes) {
237
+ return exactNaming(ctx, bytes).id;
238
+ }
239
+ /** {@link exactNode}, with whether the span's own name was found only through
240
+ * its BYTES — its children named no branch, and the flat node over the same
241
+ * bytes did ({@link branchNaming}). That is where the exact lookup used to
242
+ * MISS, so it is where {@link resolve} still asks the canonical class: the
243
+ * class may hold the learnt member that leads somewhere, which a flat index
244
+ * entry need not (measured: `tonight` named an edge-less window and
245
+ * pre-empted the case-folded `Tonight` whose edge a composition stood on).
246
+ * Recognition's probes reach it through `resolve`; asking it again for the
247
+ * perceived tree's own byte-named groups changed none of 116 real queries and
248
+ * no test, so it is not asked there. */
249
+ export function exactNaming(ctx, bytes) {
250
+ if (bytes.length === 0) {
251
+ return { id: foldTree(ctx, perceive(ctx, bytes), 0).node, byBytes: false };
252
+ }
253
+ if (ctx.meter)
254
+ ctx.meter.identityBytes += bytes.length;
255
+ let byBytes = false;
256
+ const id = contentIdentity(ctx.space, ctx.alphabet, bytes, (from, to) => to - from === 1
257
+ ? ctx.store.findLeaf(bytes.subarray(from, to))
258
+ : flatNode(ctx, bytes.subarray(from, to)), (kids, from, to) => {
259
+ const named = branchNaming(ctx, kids, bytes.subarray(from, to));
260
+ if (from === 0 && to === bytes.length)
261
+ byBytes = named.byBytes;
262
+ return named.id;
263
+ });
264
+ return { id, byBytes };
265
+ }
266
+ /** The flat node over a span's single-byte atoms — the node every deposit
267
+ * interns for its whole input and for each canonical window (learning.ts
268
+ * `deposit`, `indexSubSpans`). The store's negative filter refuses most
269
+ * misses without a lookup. */
270
+ function flatNode(ctx, span) {
271
+ const store = ctx.store;
272
+ return store.findFlatBranch
273
+ ? store.findFlatBranch(span)
274
+ : store.findBranch(Array.from(span, (b) => -(b + 1)));
275
+ }
276
+ /** THE READ SIDE NAMES A BRANCH EXACTLY AS THE WRITE SIDE DID. `intern`
277
+ * (store.ts) names a branch by its children; when they name none, it looks up
278
+ * the flat node over the same bytes and REUSES it (step 1b, "same bytes, same
279
+ * node") — so a deposit whose fold grouped `ver` + `!` was stored with the
280
+ * window `ver!` as that child. Reading by the children alone could never
281
+ * name such a deposit again: measured on the 31.7M-node store, 8 of 80 stored
282
+ * dialogue turns asked verbatim resolved to nothing (a 25-byte turn, a final
283
+ * `?` or `!`, …) and fell to the composition path. Same order as the write
284
+ * side: the children first, the bytes when they name nothing — and an unnamed
285
+ * child does not settle it, since the write side minted that child and still
286
+ * reached step 1b. */
287
+ function branchNaming(ctx, kids, span) {
288
+ if (kids.every((k) => k !== null)) {
289
+ const id = ctx.store.findBranch(kids);
290
+ if (id !== null)
291
+ return { id, byBytes: false };
292
+ }
293
+ if (kids.length < 2)
294
+ return { id: null, byBytes: false };
295
+ const id = flatNode(ctx, span);
296
+ if (id !== null && ctx.meter)
297
+ ctx.meter.flatBranchNames++;
298
+ return { id, byBytes: id !== null };
299
+ }
232
300
  /** The canonical node id of a byte span: perceive it in isolation — the way
233
301
  * training did — and recover its root bottom-up. Returns null if any part is
234
302
  * unknown. */
@@ -237,10 +305,10 @@ export function resolve(ctx, bytes) {
237
305
  return null;
238
306
  if (ctx.meter)
239
307
  ctx.meter.resolves++;
240
- const exact = foldTree(ctx, perceive(ctx, bytes), 0).node;
241
- if (exact !== null)
308
+ const { id: exact, byBytes } = exactNaming(ctx, bytes);
309
+ if (exact !== null && !byBytes)
242
310
  return exact;
243
- return canonResolve(ctx, bytes);
311
+ return canonResolve(ctx, bytes) ?? exact;
244
312
  }
245
313
  /** Equivalence-class resolution: when the exact content-addressed lookup
246
314
  * misses, find a stored node whose CANONICAL key equals the span's — the
@@ -258,7 +326,7 @@ export function canonResolve(ctx, bytes) {
258
326
  if (bytes.length < 2)
259
327
  return null;
260
328
  const memo = ctx.canonMemo;
261
- const memoKey = memo ? latin1Key(bytes) : "";
329
+ const memoKey = memo ? latin1(bytes) : "";
262
330
  if (memo) {
263
331
  const hit = memo.get(memoKey);
264
332
  if (hit !== undefined)
@@ -275,7 +343,7 @@ export function canonResolve(ctx, bytes) {
275
343
  // skips identity rows) — the exact content-addressed lookup of the
276
344
  // canonical bytes finds it directly.
277
345
  if (key.length !== bytes.length || !bytesEqual(key, bytes)) {
278
- const direct = foldTree(ctx, perceive(ctx, key), 0).node;
346
+ const direct = exactNode(ctx, key);
279
347
  if (direct !== null)
280
348
  return set(direct);
281
349
  }
@@ -295,17 +363,12 @@ export function canonResolve(ctx, bytes) {
295
363
  // resolved for these bytes is their FOLD — the deposit-shaped node that
296
364
  // carries the edges and halos. Re-folding the candidate's bytes lands
297
365
  // on exactly the node the canonical-case query would have found.
298
- const folded = foldTree(ctx, perceive(ctx, bytesOf), 0).node;
366
+ const folded = exactNode(ctx, bytesOf);
299
367
  const use = folded ?? id;
300
- // THE ADMISSION PREDICATE, by its own pair of probes: `traverse.ts`'s
301
- // `leadsSomewhere` is edge-or-halo, and `hasHalo` is the one that carries
302
- // the mass bar (`mass >= minHaloMass`). Asking `haloMass(use) > 0` instead
303
- // is the same answer only while `minHaloMass <= 1` (its default): raise the
304
- // bar and this site would rank a node as leading on evidence the law
305
- // refuses. Calling `leadsSomewhere` here is not possible — `traverse.ts`
306
- // imports THIS file, so it would be a cycle — which is why the pair is
307
- // spelled out rather than named.
308
- const leads = store.hasNext(use) || store.hasHalo(use);
368
+ // THE ADMISSION PREDICATE, asked of the store that owns it (edge or halo,
369
+ // the halo tier carrying the mass bar). Asking `haloMass(use) > 0` instead
370
+ // would agree only while `minHaloMass <= 1`.
371
+ const leads = store.leadsSomewhere(use);
309
372
  if (best === null || (leads && !bestLeads) ||
310
373
  (leads === bestLeads && use < best)) {
311
374
  best = use;
@@ -1,7 +1,8 @@
1
1
  import type { MindContext } from "./types.js";
2
+ import { type ClosureLayer, type DerivationState, type Span } from "./derivation.js";
2
3
  import type { Precomputed } from "./pipeline-mechanism.js";
3
- import { type DerivationState, type Span } from "./derivation.js";
4
- /** Extend a grounded answer forward across facts (multi-hop reasoning).
4
+ /** Extend a grounded answer forward across facts (multi-hop reasoning) — the
5
+ * WALK, run as one layer of the closure engine ({@link walkLayer}).
5
6
  * Pivots on the longest unconsumed learnt context each answer contains,
6
7
  * then follows the pivot's continuation to the next fact. **The chain ends
7
8
  * when it STOPS, never when a count runs out**: every exit is a refusal (no
@@ -17,11 +18,24 @@ import { type DerivationState, type Span } from "./derivation.js";
17
18
  * response's shared pre-computation — the post-grounding stages read the
18
19
  * same container the mechanisms did. */
19
20
  export declare function reason(ctx: MindContext, query: Uint8Array, d0: DerivationState, preConsumed: ReadonlySet<number>, pre: Precomputed, voiced?: readonly Uint8Array[]): Promise<DerivationState>;
21
+ /** The multi-hop walk as a {@link ClosureLayer}: it OFFERS a forward absorb or a
22
+ * pivot, the law admits. Its preamble — the two guards that say the question
23
+ * was already answered by the query's own position in the graph, and the
24
+ * pre-consumption of what the grounding spoke for — runs on the FIRST offer,
25
+ * against the state the engine hands it; a guard that fires makes that first
26
+ * offer `null`, so the walk ends where it begins and reports nothing more. */
27
+ export declare function walkLayer(ctx: MindContext, query: Uint8Array, preConsumed: ReadonlySet<number>, pre: Precomputed, voiced?: readonly Uint8Array[]): ClosureLayer;
20
28
  /** Fuse independent points of attention into one answer (multi-topic).
21
29
  * When the consensus climb finds more than one dominant point, each
22
30
  * independent point grounds its own answer; they are bridged together
23
31
  * by any learnt connector the graph holds between them. */
24
- export declare function fuseAttention(ctx: MindContext, query: Uint8Array, state: DerivationState, pre: Precomputed,
32
+ export declare function fuseAttention(ctx: MindContext, query: Uint8Array, state: DerivationState, pre: Precomputed, unclimbed?: boolean, primarySpans?: ReadonlyArray<Span>): Promise<DerivationState>;
33
+ /** Fusion as a {@link ClosureLayer}: ONE transition, offered once — the fused
34
+ * product of the independent points of attention, or nothing. The law admits
35
+ * it (`reaches`: the fusion composed structure this derivation had not stood
36
+ * on); the layer's own structural gates below decide whether there is a
37
+ * fusion to offer at all. */
38
+ export declare function fusionLayer(ctx: MindContext, query: Uint8Array, pre: Precomputed,
25
39
  /** True when `primary` never touched the consensus climb at all — e.g. a
26
40
  * pure ALU computation, which has no anchor of its own. commitVotes
27
41
  * ALWAYS admits the dominant root regardless of its vote (attention.ts:
@@ -35,4 +49,4 @@ unclimbed?: boolean,
35
49
  * which is the layer that knows how a given grounding records its evidence;
36
50
  * fuseAttention just reads a position from it. Empty or absent preserves
37
51
  * the original behaviour exactly. */
38
- primarySpans?: ReadonlyArray<Span>): Promise<DerivationState>;
52
+ primarySpans?: ReadonlyArray<Span>): ClosureLayer;