@hviana/sema 0.4.2 → 0.4.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 (135) hide show
  1. package/dist/example/demo.d.ts +1 -0
  2. package/dist/example/demo.js +39 -0
  3. package/dist/example/train_base.d.ts +87 -0
  4. package/dist/example/train_base.js +2252 -0
  5. package/dist/src/alphabet.d.ts +7 -0
  6. package/dist/src/alphabet.js +33 -0
  7. package/dist/src/alu/src/alu.d.ts +185 -0
  8. package/dist/src/alu/src/alu.js +440 -0
  9. package/dist/src/alu/src/expr.d.ts +61 -0
  10. package/dist/src/alu/src/expr.js +318 -0
  11. package/dist/src/alu/src/index.d.ts +11 -0
  12. package/dist/src/alu/src/index.js +19 -0
  13. package/dist/src/alu/src/kernel-arith.d.ts +16 -0
  14. package/dist/src/alu/src/kernel-arith.js +264 -0
  15. package/dist/src/alu/src/kernel-bits.d.ts +19 -0
  16. package/dist/src/alu/src/kernel-bits.js +152 -0
  17. package/dist/src/alu/src/kernel-logic.d.ts +4 -0
  18. package/dist/src/alu/src/kernel-logic.js +60 -0
  19. package/dist/src/alu/src/kernel-nd.d.ts +3 -0
  20. package/dist/src/alu/src/kernel-nd.js +208 -0
  21. package/dist/src/alu/src/kernel-numeric.d.ts +54 -0
  22. package/dist/src/alu/src/kernel-numeric.js +366 -0
  23. package/dist/src/alu/src/operation.d.ts +168 -0
  24. package/dist/src/alu/src/operation.js +189 -0
  25. package/dist/src/alu/src/parser.d.ts +221 -0
  26. package/dist/src/alu/src/parser.js +577 -0
  27. package/dist/src/alu/src/resonance.d.ts +55 -0
  28. package/dist/src/alu/src/resonance.js +126 -0
  29. package/dist/src/alu/src/text.d.ts +31 -0
  30. package/dist/src/alu/src/text.js +73 -0
  31. package/dist/src/alu/src/value.d.ts +109 -0
  32. package/dist/src/alu/src/value.js +300 -0
  33. package/dist/src/alu/test/alu.test.d.ts +1 -0
  34. package/dist/src/alu/test/alu.test.js +764 -0
  35. package/dist/src/bytes.d.ts +14 -0
  36. package/dist/src/bytes.js +59 -0
  37. package/dist/src/canon.d.ts +26 -0
  38. package/dist/src/canon.js +57 -0
  39. package/dist/src/config.d.ts +111 -0
  40. package/dist/src/config.js +91 -0
  41. package/dist/src/derive/src/deduction.d.ts +136 -0
  42. package/dist/src/derive/src/deduction.js +159 -0
  43. package/dist/src/derive/src/index.d.ts +8 -0
  44. package/dist/src/derive/src/index.js +11 -0
  45. package/dist/src/derive/src/priority-queue.d.ts +20 -0
  46. package/dist/src/derive/src/priority-queue.js +73 -0
  47. package/dist/src/derive/src/rewrite.d.ts +56 -0
  48. package/dist/src/derive/src/rewrite.js +100 -0
  49. package/dist/src/derive/src/trie.d.ts +90 -0
  50. package/dist/src/derive/src/trie.js +217 -0
  51. package/dist/src/derive/test/derive.test.d.ts +1 -0
  52. package/dist/src/derive/test/derive.test.js +122 -0
  53. package/dist/src/extension.d.ts +37 -0
  54. package/dist/src/extension.js +7 -0
  55. package/dist/src/geometry.d.ts +175 -0
  56. package/dist/src/geometry.js +823 -0
  57. package/dist/src/index.d.ts +17 -0
  58. package/dist/src/index.js +19 -0
  59. package/dist/src/ingest-cache.d.ts +41 -0
  60. package/dist/src/ingest-cache.js +165 -0
  61. package/dist/src/meter.d.ts +176 -0
  62. package/dist/src/meter.js +274 -0
  63. package/dist/src/mind/articulation.d.ts +6 -0
  64. package/dist/src/mind/articulation.js +99 -0
  65. package/dist/src/mind/attention.d.ts +414 -0
  66. package/dist/src/mind/attention.js +2082 -0
  67. package/dist/src/mind/bridge.d.ts +39 -0
  68. package/dist/src/mind/bridge.js +972 -0
  69. package/dist/src/mind/canonical.d.ts +34 -0
  70. package/dist/src/mind/canonical.js +93 -0
  71. package/dist/src/mind/graph-search.d.ts +294 -0
  72. package/dist/src/mind/graph-search.js +996 -0
  73. package/dist/src/mind/index.d.ts +9 -0
  74. package/dist/src/mind/index.js +5 -0
  75. package/dist/src/mind/junction.d.ts +137 -0
  76. package/dist/src/mind/junction.js +342 -0
  77. package/dist/src/mind/learning.d.ts +75 -0
  78. package/dist/src/mind/learning.js +270 -0
  79. package/dist/src/mind/match.d.ts +181 -0
  80. package/dist/src/mind/match.js +655 -0
  81. package/dist/src/mind/mechanisms/alu.d.ts +4 -0
  82. package/dist/src/mind/mechanisms/alu.js +36 -0
  83. package/dist/src/mind/mechanisms/cast.d.ts +89 -0
  84. package/dist/src/mind/mechanisms/cast.js +784 -0
  85. package/dist/src/mind/mechanisms/confluence.d.ts +24 -0
  86. package/dist/src/mind/mechanisms/confluence.js +255 -0
  87. package/dist/src/mind/mechanisms/cover.d.ts +6 -0
  88. package/dist/src/mind/mechanisms/cover.js +227 -0
  89. package/dist/src/mind/mechanisms/extraction.d.ts +33 -0
  90. package/dist/src/mind/mechanisms/extraction.js +300 -0
  91. package/dist/src/mind/mechanisms/recall.d.ts +16 -0
  92. package/dist/src/mind/mechanisms/recall.js +364 -0
  93. package/dist/src/mind/mind.d.ts +337 -0
  94. package/dist/src/mind/mind.js +626 -0
  95. package/dist/src/mind/pipeline-mechanism.d.ts +172 -0
  96. package/dist/src/mind/pipeline-mechanism.js +465 -0
  97. package/dist/src/mind/pipeline.d.ts +49 -0
  98. package/dist/src/mind/pipeline.js +275 -0
  99. package/dist/src/mind/primitives.d.ts +66 -0
  100. package/dist/src/mind/primitives.js +306 -0
  101. package/dist/src/mind/rationale.d.ts +139 -0
  102. package/dist/src/mind/rationale.js +163 -0
  103. package/dist/src/mind/reasoning.d.ts +40 -0
  104. package/dist/src/mind/reasoning.js +280 -0
  105. package/dist/src/mind/recognition.d.ts +20 -0
  106. package/dist/src/mind/recognition.js +504 -0
  107. package/dist/src/mind/resonance.d.ts +23 -0
  108. package/dist/src/mind/resonance.js +0 -0
  109. package/dist/src/mind/trace.d.ts +15 -0
  110. package/dist/src/mind/trace.js +73 -0
  111. package/dist/src/mind/traverse.d.ts +126 -0
  112. package/dist/src/mind/traverse.js +650 -0
  113. package/dist/src/mind/types.d.ts +333 -0
  114. package/dist/src/mind/types.js +130 -0
  115. package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
  116. package/dist/src/rabitq-ivf/src/database.js +201 -0
  117. package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
  118. package/dist/src/rabitq-ivf/src/index.js +4 -0
  119. package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
  120. package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
  121. package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
  122. package/dist/src/rabitq-ivf/src/prng.js +36 -0
  123. package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
  124. package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
  125. package/dist/src/sema.d.ts +31 -0
  126. package/dist/src/sema.js +63 -0
  127. package/dist/src/store-sqlite.d.ts +184 -0
  128. package/dist/src/store-sqlite.js +942 -0
  129. package/dist/src/store.d.ts +678 -0
  130. package/dist/src/store.js +1703 -0
  131. package/dist/src/vec.d.ts +31 -0
  132. package/dist/src/vec.js +109 -0
  133. package/package.json +1 -1
  134. package/src/mind/bridge.ts +55 -18
  135. package/src/mind/mind.ts +11 -2
@@ -0,0 +1,122 @@
1
+ // Self-contained tests for the `derive` library: the lightest-derivation
2
+ // engine (including a multi-premise *bridge* rule), the on-demand trie matcher,
3
+ // and the optimal cover. Uses node:test; no dependency on sema.
4
+ import { test } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { coverSequence, lightestDerivation, Trie, } from "../src/index.js";
7
+ // ── trie: on-demand matching, no length bound ──────────────────────────────
8
+ test("trie matchesAt reports forms beginning exactly at a position", () => {
9
+ const t = new Trie();
10
+ t.insert([1, 2], "ab");
11
+ t.insert([1, 2, 3], "abc");
12
+ t.insert([2, 3], "bc");
13
+ const at0 = t.matchesAt([1, 2, 3, 4], 0).map((m) => m.payload);
14
+ assert.deepEqual(at0, ["ab", "abc"]); // both forms starting at 0, shortest first
15
+ const at1 = t.matchesAt([1, 2, 3, 4], 1).map((m) => m.payload);
16
+ assert.deepEqual(at1, ["bc"]);
17
+ const at3 = t.matchesAt([1, 2, 3, 4], 3).map((m) => m.payload);
18
+ assert.deepEqual(at3, []); // nothing learned here → dead-ends immediately
19
+ });
20
+ test("trie scan finds every occurrence; duplicate inserts share an id", () => {
21
+ const t = new Trie();
22
+ const id1 = t.insert([7, 7], 1);
23
+ const id2 = t.insert([7, 7], 2); // same content
24
+ assert.equal(id1, id2);
25
+ assert.equal(t.size, 1);
26
+ const hits = t.scan([7, 7, 7]);
27
+ assert.equal(hits.length, 2); // [0,2) and [1,3)
28
+ });
29
+ // ── engine: Dijkstra/Knuth core with a bridge (multi-premise) rule ─────────
30
+ test("lightestDerivation solves a hypergraph with a bridge premise", () => {
31
+ // Items: "A", "B", "AB". Axioms A (3) and B (4). A binary bridge A ∧ B → AB
32
+ // at cost 1. The only derivation of AB costs 3 + 4 + 1 = 8.
33
+ const A = "A", B = "B", AB = "AB";
34
+ const system = {
35
+ key: (s) => s,
36
+ axioms: () => [{ item: A, cost: 3 }, { item: B, cost: 4 }],
37
+ isGoal: (s) => s === AB,
38
+ *rules(item) {
39
+ // Bridge fires from either premise; the engine waits until both are known.
40
+ if (item === A)
41
+ yield { premises: [A, B], conclusion: AB, cost: 1 };
42
+ if (item === B)
43
+ yield { premises: [A, B], conclusion: AB, cost: 1 };
44
+ },
45
+ };
46
+ const d = lightestDerivation(system);
47
+ assert.ok(d);
48
+ assert.equal(d.item, AB);
49
+ assert.equal(d.cost, 8);
50
+ assert.equal(d.premises.length, 2); // it really used the bridge
51
+ });
52
+ test("lightestDerivation picks the cheaper of competing derivations", () => {
53
+ // Two ways to reach the goal G from axiom S: S→G cost 10, or S→M→G cost 3+3.
54
+ const system = {
55
+ key: (s) => s,
56
+ axioms: () => [{ item: "S", cost: 0 }],
57
+ isGoal: (s) => s === "G",
58
+ *rules(item) {
59
+ if (item === "S") {
60
+ yield { premises: ["S"], conclusion: "G", cost: 10 };
61
+ yield { premises: ["S"], conclusion: "M", cost: 3 };
62
+ }
63
+ if (item === "M")
64
+ yield { premises: ["M"], conclusion: "G", cost: 3 };
65
+ },
66
+ };
67
+ const d = lightestDerivation(system);
68
+ assert.ok(d);
69
+ assert.equal(d.cost, 6); // via M, not the direct 10
70
+ assert.equal(d.rule?.premises[0], "M");
71
+ });
72
+ test("lightestDerivation returns null when the goal is unreachable", () => {
73
+ const system = {
74
+ key: (n) => "" + n,
75
+ axioms: () => [{ item: 0, cost: 0 }],
76
+ isGoal: (n) => n === 99,
77
+ *rules(n) {
78
+ if (n < 3)
79
+ yield { premises: [n], conclusion: n + 1, cost: 1 };
80
+ },
81
+ };
82
+ assert.equal(lightestDerivation(system), null);
83
+ });
84
+ // ── cover: optimal, not greedy ─────────────────────────────────────────────
85
+ test("coverSequence maximises coverage, beating greedy longest-match", () => {
86
+ // Over [0,4): one long span [1,4) (len 3) vs two short [0,2),[2,4) (cover 4).
87
+ // Greedy-longest takes [1,4) and covers 3; the optimal cover takes the pair.
88
+ const cover = coverSequence(4, [
89
+ { start: 1, end: 4, payload: "long" },
90
+ { start: 0, end: 2, payload: "left" },
91
+ { start: 2, end: 4, payload: "right" },
92
+ ]);
93
+ assert.equal(cover.covered, 4);
94
+ assert.equal(cover.uncovered, 0);
95
+ assert.deepEqual(cover.spans.map((s) => s.payload), ["left", "right"]);
96
+ });
97
+ test("coverSequence prefers fewer, longer spans on ties (unit weight)", () => {
98
+ // [0,3) covers the same as [0,1)+[1,3) but in one span → preferred.
99
+ const cover = coverSequence(3, [
100
+ { start: 0, end: 3, payload: "whole" },
101
+ { start: 0, end: 1, payload: "a" },
102
+ { start: 1, end: 3, payload: "b" },
103
+ ]);
104
+ assert.deepEqual(cover.spans.map((s) => s.payload), ["whole"]);
105
+ });
106
+ test("coverSequence leaves genuinely uncovered gaps uncovered", () => {
107
+ const cover = coverSequence(10, [
108
+ { start: 0, end: 3, payload: "x" },
109
+ { start: 6, end: 9, payload: "y" },
110
+ ]);
111
+ assert.equal(cover.covered, 6);
112
+ assert.equal(cover.uncovered, 4);
113
+ assert.deepEqual(cover.spans.map((s) => s.payload), ["x", "y"]);
114
+ });
115
+ test("coverSequence honours weights when coverage ties", () => {
116
+ // Both single-span covers cover all 2 symbols; the lighter weight wins.
117
+ const cover = coverSequence(2, [
118
+ { start: 0, end: 2, weight: 5, payload: "heavy" },
119
+ { start: 0, end: 2, weight: 1, payload: "light" },
120
+ ]);
121
+ assert.deepEqual(cover.spans.map((s) => s.payload), ["light"]);
122
+ });
@@ -0,0 +1,37 @@
1
+ /** A half-open byte span of a query an extension computed, and the canonical
2
+ * result bytes it is authoritative for. */
3
+ export interface ComputedSpan {
4
+ i: number;
5
+ j: number;
6
+ bytes: Uint8Array;
7
+ }
8
+ /** The generic capabilities the mind lends every extension — nothing here
9
+ * names any particular extension, and every member is machinery the mind
10
+ * already has:
11
+ *
12
+ * MEANING (resonance and grounding — the same mechanisms recall uses):
13
+ * • meaningOf — which of some labelled forms does a span mean? Pure gist
14
+ * nearness; the extension supplies its own vocabulary and gives the answer
15
+ * its own reading.
16
+ * • continuation — the grounded form the corpus continues a form to (the
17
+ * continuation fixpoint recall grounds answers with), or null when the
18
+ * form leads nowhere. The mind answers only "where does this form lead?";
19
+ * what that continuation MEANS is the extension's business.
20
+ *
21
+ * GEOMETRY (the perception tree's own structure):
22
+ * • segment — coherent runs by the alphabet-space merge the perception tree
23
+ * uses, so an extension's notion of "separator" is the learnt geometry's.
24
+ * • reach — the river's grouping capacity (maxGroup), bounding how far apart
25
+ * two spans may sit and still be read as one construction. */
26
+ export interface ExtensionHost {
27
+ meaningOf(bytes: Uint8Array, anchors: ReadonlyArray<{
28
+ name: string;
29
+ form: Uint8Array;
30
+ }>): Promise<string | null>;
31
+ continuation(bytes: Uint8Array): Promise<Uint8Array | null>;
32
+ segment(bytes: Uint8Array): Array<{
33
+ i: number;
34
+ j: number;
35
+ }>;
36
+ reach: number;
37
+ }
@@ -0,0 +1,7 @@
1
+ // extension.ts — shared types for computational extensions.
2
+ //
3
+ // Extensions (ALU, user computation packs) implement the PipelineMechanism
4
+ // interface (pipeline-mechanism.ts) — this file holds only the types that
5
+ // both the extension author and the pipeline need, without coupling either
6
+ // to a specific mechanism.
7
+ export {};
@@ -0,0 +1,175 @@
1
+ import { Vec } from "./vec.js";
2
+ import { Sema, Space } from "./sema.js";
3
+ import { Alphabet } from "./alphabet.js";
4
+ /** The store's geometric identity bar: cosine ≥ 1 − 1/√D is the similarity at
5
+ * which `intern` already treats two gists as the SAME node. Recall reuses it
6
+ * to accept a near-identical query, and the climb to accept a containing form —
7
+ * one derived constant, never a tuned threshold. NOTE: this fixed bar is
8
+ * the ESTIMATOR floor of an identity claim; a whole-span claim over a span
9
+ * longer than the perception quantum must use the scale-aware
10
+ * {@link identityBar}, which converts the tolerated fraction into bytes. */
11
+ export declare function mergeThreshold(D: number): number;
12
+ /** The scale-aware IDENTITY bar for a whole-span resonance claim over a span
13
+ * of `len` bytes. Under the linear fold a cosine reads "fraction of aligned
14
+ * shared bytes", so a FIXED cosine bar admits a byte budget that grows with
15
+ * the span: 1 − 1/√D over a 4·√D-byte span tolerates four whole river
16
+ * windows of foreign content while still claiming "near-identical". An
17
+ * identity claim may tolerate at most ONE river window W — the perception
18
+ * quantum, the same single-window budget near-dedup's differsByOneWindow
19
+ * grants — so the bar is 1 − W/len, floored at mergeThreshold(D), below
20
+ * which the RaBitQ estimator cannot certify identity anyway. This is the
21
+ * angle+magnitude form of the identity test: the ANGLE carries the shared
22
+ * fraction, the span's MAGNITUDE (√len, the linear fold's own norm) converts
23
+ * the tolerated fraction into tolerated bytes. Derived from W, D and the
24
+ * span; never tuned. */
25
+ export declare function identityBar(D: number, maxGroup: number, len: number): number;
26
+ /** The reach bar: half a river quantum, derived from the fold's own geometry.
27
+ * A branch folds up to `maxGroup` children, so two forms that differ in ONE
28
+ * whole child — the smallest distinction perception can mean — sit at cosine
29
+ * ≈ 1 − 1/maxGroup. Half that quantum, 1 − 1/(2·maxGroup), is closer than any
30
+ * single-child difference can be: a positional echo of the same content.
31
+ *
32
+ * Recall uses this as its confidence floor: a query whose nearest resonant
33
+ * form sits below this bar is structurally unrelated to everything in the store
34
+ * — further than any single-child variant — and the system returns null rather
35
+ * than fabricate an answer from an unrelated form. Derived, never tuned. */
36
+ export declare function reachThreshold(maxGroup: number): number;
37
+ /** The estimator's own noise floor: 1/√D — ONE standard deviation of the
38
+ * cosine between two independent random vectors in D dimensions (the same σ
39
+ * {@link significanceBar} takes three of). It is the smallest difference in
40
+ * cosine that is distinguishable from the rotation-uniformised RaBitQ
41
+ * estimation error (see the MEASUREMENT CAVEAT above): a contrastive margin
42
+ * below it is quantisation noise, not evidence. The consensus climb gates a
43
+ * region's vote on its discriminative margin clearing this floor — the
44
+ * minimal "above noise" bar, one σ, not the stricter 3σ relatedness bar.
45
+ * Derived, never tuned. */
46
+ export declare function estimatorNoise(D: number): number;
47
+ /** The statistical-significance bar for whole-query resonance: 3/√D.
48
+ * In D dimensions the expected cosine of two independent random vectors is 0
49
+ * with standard deviation 1/√D. A cosine ≥ 3/√D is three standard deviations
50
+ * above chance — the query is statistically related to the store, not merely
51
+ * sharing random byte noise. Below this bar the consensus climb (which trusts
52
+ * sub-region resonance) is skipped: there is no evidence the query belongs to
53
+ * the same distribution as the stored content. Derived, never tuned. */
54
+ export declare function significanceBar(D: number): number;
55
+ /** The concept (halo) threshold: the cosine above which two nodes share a
56
+ * distributional concept. A halo is a superposition of episode signatures in
57
+ * D-dimensional space, so the expected cosine between two unrelated halos is 0
58
+ * with standard deviation 1/√D. The structural midpoint 0.5 separates "more
59
+ * similar than not" from noise; the +0.5/√D term adds one half-sigma margin
60
+ * that vanishes as D → ∞, accounting for the wider noise band at lower D
61
+ * without inventing a tuned constant. At D=1024 this gives 0.516, within
62
+ * 3% of 0.5 — existing behavior is preserved while threshold and D move
63
+ * together. Derived, never tuned. */
64
+ export declare function conceptThreshold(D: number): number;
65
+ /** The HALF-DOMINANCE predicate: whether a part covering `partLen` of a
66
+ * whole of `wholeLen` covers STRICTLY more than half of it. A span that
67
+ * dominates its whole can no longer discriminate the whole's own content —
68
+ * the one test behind liftAnswer's keep-the-frame rule, collectRegions'
69
+ * wrapper exclusion, and CAST's frame-depth majority (each cites this).
70
+ * CAST's frame-FRACTION gate is the deliberately CLOSED variant (≥ ½ is
71
+ * already unusable there) and stays inline where it is documented.
72
+ * Derived from the structural midpoint, never tuned. */
73
+ export declare function dominates(partLen: number, wholeLen: number): boolean;
74
+ /** The consensus-vote significance floor: ln(N) + 1/2, where N is the number
75
+ * of learnt contexts (edge sources). A single region's IDF-weighted vote for
76
+ * an anchor reached through c contexts is at most ln(N/c) ≤ ln(N); the +1/2
77
+ * demands the pooled vote exceed what ONE maximally-specific region could
78
+ * contribute by half a unit — i.e. genuine corroboration beyond a lone
79
+ * region's echo at this corpus scale. The ONE floor both consumers gate on:
80
+ * recallByResonance trusting a climb anchor, and commitVotes admitting a
81
+ * further point of attention. Defined once here so the two can never
82
+ * drift apart. Derived from N, never tuned. */
83
+ export declare function consensusFloor(N: number): number;
84
+ /** The coverage bar for the reach (interior) index, when vector-similarity
85
+ * gating is used. Returns the concept threshold — the structural midpoint
86
+ * (~0.5 at D=1024) where two forms are "more similar than not."
87
+ *
88
+ * Currently UNUSED in the hot training path: interior nodes are indexed
89
+ * unconditionally (hash-cons dedup bounds the index naturally).
90
+ * Post-hoc structural compaction ({@link Store.compactContentIndex})
91
+ * replaces runtime coverage gating with a batch pass that removes
92
+ * structurally-isolated entries. Derived, never tuned. */
93
+ export declare function coverageBar(_maxGroup: number, D: number): number;
94
+ export interface Folded {
95
+ tree: Sema;
96
+ /** Byte length of the subtree — carried incrementally so the stable-prefix
97
+ * boundary scan never re-walks subtrees (the old per-level walk was
98
+ * O(n log n) over the whole input). */
99
+ len: number;
100
+ }
101
+ export interface Grid {
102
+ width: number;
103
+ height: number;
104
+ channels: number;
105
+ data: Uint8Array;
106
+ dims?: number[];
107
+ }
108
+ export declare function contentBoundaries(space: Space, bytes: Uint8Array): number[];
109
+ /** Find the longest prefix of `bytes` whose leaf-id signature matches a
110
+ * known branch via `lookup`. Returns the byte-length of that prefix, or 0. */
111
+ export declare function knownPrefixLength(bytes: Uint8Array, leafAt: (i: number) => number | null, lookup: (leafIds: number[]) => number | null): number;
112
+ /** Bytes → Sema tree. `leafAt` and `lookup` are store capabilities for
113
+ * detecting previously-stored prefixes so the river can split at the
114
+ * correct boundary. Pass them through from `perceive`; the geometry
115
+ * computes the stable prefix internally.
116
+ *
117
+ * `boundaries` is the CALLER-computed stable-prefix boundary set (§10.3):
118
+ * strictly-increasing proper byte offsets, each the length of a prefix that
119
+ * is already a stored whole-stream form. When given, the fold splits into
120
+ * the segments between consecutive boundaries — each folded independently,
121
+ * exactly as it folded when it was learned — and the segment roots join
122
+ * LEFT-NESTED (((s₀·s₁)·s₂)…), so every learnt cumulative-context root
123
+ * reappears as an identical subtree (and, by hash-consing, the very same
124
+ * node) inside the grown stream. This is what lets a conversation's next
125
+ * turn extend perception instead of refolding it: identical prefixes
126
+ * produce identical subtrees regardless of what follows them. */
127
+ export declare function bytesToTree(space: Space, alphabet: Alphabet, bytes: Uint8Array, leafAt?: (i: number) => number | null, lookup?: (leafIds: number[]) => number | null, boundaries?: readonly number[]): Sema;
128
+ /** A stable-prefix fold's reusable state: the segment edge offsets and each
129
+ * segment's independently-folded root ({@link riverFoldRaw} output). A
130
+ * grown stream whose boundary set EXTENDS a previous fold's reuses every
131
+ * matching segment's Folded unchanged (segments fold independently by
132
+ * construction, so reuse is bit-identical to refolding) and folds only the
133
+ * new right-edge segment — O(turn) per extension. Purely a cache: the
134
+ * produced tree never depends on cache state. */
135
+ export interface StableFold {
136
+ edges: number[];
137
+ segs: Folded[];
138
+ }
139
+ /** {@link stablePrefixFold} with incremental segment reuse — same cuts, same
140
+ * segment folds, same left-nested join, same single root normalize; `prev`
141
+ * only elides recomputing segments whose [start,end) offsets it already
142
+ * folded over a byte-identical prefix (the caller keys the cache by
143
+ * content). Requires a non-empty effective boundary set. */
144
+ export declare function stablePrefixFoldIncremental(space: Space, alphabet: Alphabet, bytes: Uint8Array, boundaries: readonly number[], prev?: StableFold): {
145
+ tree: Sema;
146
+ fold: StableFold;
147
+ };
148
+ /** Plain river fold WITHOUT the final root normalize — the segment-level
149
+ * building block of {@link stablePrefixFold} (interiors must keep their
150
+ * byte-proportional magnitude; only the whole perception's root is ever
151
+ * normalized). Exported so callers that COMPOSE already-existing structural
152
+ * parts into a hypothetical synthetic root (see {@link composeStructuralGist})
153
+ * can feed the same raw primitive instead of duplicating its mathematics. */
154
+ export declare function riverFoldRaw(space: Space, row: Folded[]): Folded;
155
+ /** One already-existing structural vector to compose, paired with the byte
156
+ * span (query-slot) length it stands in for. `len`, not the vector's own
157
+ * magnitude, is what {@link composeStructuralGist} restores — the composed
158
+ * slot's NATURAL span, exactly as the linear river fold would carry it. */
159
+ export interface StructuralPart {
160
+ v: Vec;
161
+ len: number;
162
+ }
163
+ /** Synthesize a hypothetical internal structure from already-existing
164
+ * structural vectors — NOT from bytes. This is the raw positional
165
+ * composition the linear river fold already uses (see the folding header
166
+ * above): each part is positionally bound into its own seat, its natural
167
+ * span magnitude is preserved, the parts are linearly superposed, and only
168
+ * the final synthetic root is normalized. It never calls {@link gistOf}
169
+ * (there is no `gistOf` here — geometry.ts has no store), never perceives a
170
+ * concatenated byte string, and never interns or stores a new node: the
171
+ * result is an opaque, ungrounded Vec for an ANN probe only. */
172
+ export declare function composeStructuralGist(space: Space, parts: readonly StructuralPart[]): Vec;
173
+ export declare function hilbertBytes(grid: Grid): Uint8Array;
174
+ export declare function gridToTree(space: Space, alphabet: Alphabet, grid: Grid): Sema;
175
+ export declare function stackGrids(frames: Grid[]): Grid;