@hviana/sema 0.1.8 → 0.1.9

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 (131) hide show
  1. package/package.json +1 -1
  2. package/src/geometry.ts +14 -30
  3. package/src/mind/mechanisms/cast.ts +12 -7
  4. package/dist/example/demo.d.ts +0 -1
  5. package/dist/example/demo.js +0 -39
  6. package/dist/example/train_base.d.ts +0 -87
  7. package/dist/example/train_base.js +0 -2252
  8. package/dist/src/alphabet.d.ts +0 -7
  9. package/dist/src/alphabet.js +0 -33
  10. package/dist/src/alu/src/alu.d.ts +0 -185
  11. package/dist/src/alu/src/alu.js +0 -440
  12. package/dist/src/alu/src/expr.d.ts +0 -61
  13. package/dist/src/alu/src/expr.js +0 -318
  14. package/dist/src/alu/src/index.d.ts +0 -11
  15. package/dist/src/alu/src/index.js +0 -19
  16. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  17. package/dist/src/alu/src/kernel-arith.js +0 -264
  18. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  19. package/dist/src/alu/src/kernel-bits.js +0 -152
  20. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  21. package/dist/src/alu/src/kernel-logic.js +0 -60
  22. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  23. package/dist/src/alu/src/kernel-nd.js +0 -208
  24. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  25. package/dist/src/alu/src/kernel-numeric.js +0 -366
  26. package/dist/src/alu/src/operation.d.ts +0 -168
  27. package/dist/src/alu/src/operation.js +0 -189
  28. package/dist/src/alu/src/parser.d.ts +0 -221
  29. package/dist/src/alu/src/parser.js +0 -577
  30. package/dist/src/alu/src/resonance.d.ts +0 -55
  31. package/dist/src/alu/src/resonance.js +0 -126
  32. package/dist/src/alu/src/text.d.ts +0 -31
  33. package/dist/src/alu/src/text.js +0 -73
  34. package/dist/src/alu/src/value.d.ts +0 -109
  35. package/dist/src/alu/src/value.js +0 -300
  36. package/dist/src/alu/test/alu.test.d.ts +0 -1
  37. package/dist/src/alu/test/alu.test.js +0 -764
  38. package/dist/src/bytes.d.ts +0 -14
  39. package/dist/src/bytes.js +0 -59
  40. package/dist/src/canon.d.ts +0 -26
  41. package/dist/src/canon.js +0 -57
  42. package/dist/src/config.d.ts +0 -111
  43. package/dist/src/config.js +0 -91
  44. package/dist/src/derive/src/deduction.d.ts +0 -125
  45. package/dist/src/derive/src/deduction.js +0 -155
  46. package/dist/src/derive/src/index.d.ts +0 -7
  47. package/dist/src/derive/src/index.js +0 -11
  48. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  49. package/dist/src/derive/src/priority-queue.js +0 -73
  50. package/dist/src/derive/src/rewrite.d.ts +0 -56
  51. package/dist/src/derive/src/rewrite.js +0 -100
  52. package/dist/src/derive/src/trie.d.ts +0 -90
  53. package/dist/src/derive/src/trie.js +0 -217
  54. package/dist/src/derive/test/derive.test.d.ts +0 -1
  55. package/dist/src/derive/test/derive.test.js +0 -122
  56. package/dist/src/extension.d.ts +0 -37
  57. package/dist/src/extension.js +0 -7
  58. package/dist/src/geometry.d.ts +0 -148
  59. package/dist/src/geometry.js +0 -529
  60. package/dist/src/index.d.ts +0 -16
  61. package/dist/src/index.js +0 -18
  62. package/dist/src/ingest-cache.d.ts +0 -41
  63. package/dist/src/ingest-cache.js +0 -161
  64. package/dist/src/mind/articulation.d.ts +0 -6
  65. package/dist/src/mind/articulation.js +0 -99
  66. package/dist/src/mind/attention.d.ts +0 -72
  67. package/dist/src/mind/attention.js +0 -898
  68. package/dist/src/mind/canonical.d.ts +0 -29
  69. package/dist/src/mind/canonical.js +0 -88
  70. package/dist/src/mind/graph-search.d.ts +0 -270
  71. package/dist/src/mind/graph-search.js +0 -847
  72. package/dist/src/mind/index.d.ts +0 -5
  73. package/dist/src/mind/index.js +0 -5
  74. package/dist/src/mind/junction.d.ts +0 -95
  75. package/dist/src/mind/junction.js +0 -262
  76. package/dist/src/mind/learning.d.ts +0 -47
  77. package/dist/src/mind/learning.js +0 -233
  78. package/dist/src/mind/match.d.ts +0 -113
  79. package/dist/src/mind/match.js +0 -446
  80. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  81. package/dist/src/mind/mechanisms/alu.js +0 -29
  82. package/dist/src/mind/mechanisms/cast.d.ts +0 -35
  83. package/dist/src/mind/mechanisms/cast.js +0 -461
  84. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  85. package/dist/src/mind/mechanisms/confluence.js +0 -225
  86. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  87. package/dist/src/mind/mechanisms/cover.js +0 -179
  88. package/dist/src/mind/mechanisms/extraction.d.ts +0 -67
  89. package/dist/src/mind/mechanisms/extraction.js +0 -342
  90. package/dist/src/mind/mechanisms/recall.d.ts +0 -13
  91. package/dist/src/mind/mechanisms/recall.js +0 -235
  92. package/dist/src/mind/mind.d.ts +0 -273
  93. package/dist/src/mind/mind.js +0 -562
  94. package/dist/src/mind/pipeline-mechanism.d.ts +0 -142
  95. package/dist/src/mind/pipeline-mechanism.js +0 -213
  96. package/dist/src/mind/pipeline.d.ts +0 -20
  97. package/dist/src/mind/pipeline.js +0 -185
  98. package/dist/src/mind/primitives.d.ts +0 -63
  99. package/dist/src/mind/primitives.js +0 -251
  100. package/dist/src/mind/rationale.d.ts +0 -134
  101. package/dist/src/mind/rationale.js +0 -162
  102. package/dist/src/mind/reasoning.d.ts +0 -15
  103. package/dist/src/mind/reasoning.js +0 -162
  104. package/dist/src/mind/recognition.d.ts +0 -20
  105. package/dist/src/mind/recognition.js +0 -246
  106. package/dist/src/mind/resonance.d.ts +0 -23
  107. package/dist/src/mind/resonance.js +0 -0
  108. package/dist/src/mind/trace.d.ts +0 -15
  109. package/dist/src/mind/trace.js +0 -73
  110. package/dist/src/mind/traverse.d.ts +0 -113
  111. package/dist/src/mind/traverse.js +0 -488
  112. package/dist/src/mind/types.d.ts +0 -186
  113. package/dist/src/mind/types.js +0 -84
  114. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  115. package/dist/src/rabitq-ivf/src/database.js +0 -201
  116. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  117. package/dist/src/rabitq-ivf/src/index.js +0 -4
  118. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  119. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  120. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  121. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  122. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  123. package/dist/src/rabitq-ivf/src/rabitq.js +0 -283
  124. package/dist/src/sema.d.ts +0 -31
  125. package/dist/src/sema.js +0 -63
  126. package/dist/src/store-sqlite.d.ts +0 -184
  127. package/dist/src/store-sqlite.js +0 -942
  128. package/dist/src/store.d.ts +0 -654
  129. package/dist/src/store.js +0 -1612
  130. package/dist/src/vec.d.ts +0 -31
  131. package/dist/src/vec.js +0 -109
@@ -1,5 +0,0 @@
1
- export { Mind } from "./mind.js";
2
- export type { Input, Response } from "./mind.js";
3
- export type { ComputedSpan, ExtensionHost } from "./mind.js";
4
- export type { MechanismResult, PipelineMechanism, Precomputed, } from "./pipeline-mechanism.js";
5
- export type { InspectRationale, RationaleItem, RationaleStep, } from "./rationale.js";
@@ -1,5 +0,0 @@
1
- // mind/index.ts — public surface of the mind module.
2
- //
3
- // Re-exports the Mind class and all public types that were previously
4
- // exported from mind/mind.ts directly.
5
- export { Mind } from "./mind.js";
@@ -1,95 +0,0 @@
1
- import type { MindContext } from "./types.js";
2
- export interface Junction {
3
- /** The node whose learnt bytes evidence this junction (a container form,
4
- * a continuation, or a context). */
5
- id: number;
6
- /** The bytes that belong between left and right. */
7
- interior: Uint8Array;
8
- }
9
- /** Seed node ids to ascend from for one side of a junction: the side's own
10
- * node when it is a stored form, plus — when the node has no structural
11
- * parents — its canonical window ids. A non-W-aligned node may have no
12
- * parents, but its constituent W-grams typically do; the window ids
13
- * provide alternative ascent paths. The `parentsFirst(…, 1)` probe is a
14
- * single indexed lookup, far cheaper than computing every window id, so
15
- * the window-id path is only taken when the node alone cannot ascend.
16
- * Exported for callers (synonym junctions) that hold one side FIXED across
17
- * several calls and so compute its seeds once instead of per call. */
18
- export declare function junctionSeeds(ctx: MindContext, b: Uint8Array): number[];
19
- /** Per-response cache of the identity walks' pure reads (capped bytes,
20
- * parent pages, container pages), keyed by the response lifecycle object
21
- * (ctx.climbMemo). One response issues many walks whose ancestries overlap
22
- * heavily (pair sides repeat across combos, and synonym walks revisit the
23
- * same neighbourhoods); the store is read-only while a response is in flight,
24
- * so every one of these reads is a pure function of the id — repeats cost a
25
- * Map hit instead of a SQL statement or a byte reconstruction. */
26
- export interface WalkCache {
27
- /** id → prefix bytes read so far + whether they are the COMPLETE bytes
28
- * (shorter than the cap that read them). */
29
- reads: Map<number, {
30
- b: Uint8Array;
31
- complete: boolean;
32
- }>;
33
- parents: Map<number, number[]>;
34
- containers: Map<number, number[]>;
35
- }
36
- export declare function walkCache(ctx: MindContext): WalkCache | null;
37
- export declare function cachedRead(ctx: MindContext, cache: WalkCache | null, id: number, cap: number): Uint8Array;
38
- /** Tier 1 body, parameterised on already-resolved seed lists so a caller
39
- * holding one side FIXED across several calls (synonym junctions) pays for
40
- * that side's seeds once, not once per call. The byte-containment check
41
- * below ensures only genuine containers are returned regardless of seeds.
42
- *
43
- * BOUNDED at corpus scale by three disciplines (profiled on a 17.7M-node
44
- * store, where the unbounded form spent >90% of a query's CPU here):
45
- * • PHRASE-SCALE READS — a junction container is by contract a whole the
46
- * pair nearly exhausts (glue from a period to a phrase), so every visit
47
- * reads at most `maxContainer + 1` bytes (`bytesPrefix` stops early).
48
- * A node whose bytes exceed the cap cannot be a junction container, and
49
- * its ancestors are strictly larger — the branch is PRUNED, never
50
- * reconstructing a corpus-sized deposit (an oasst2 conversation) just
51
- * to reject it.
52
- * • EXPANSION BUDGET — at most √N·W nodes are popped in total: a √N-wide
53
- * frontier (the one fan-out convention) through the ~W structural
54
- * levels that separate phrase-scale content from its containers
55
- * (perception trees are W-ary, so a junction container lies within a
56
- * few levels of its parts). A side too common to decide within the
57
- * budget abstains here and falls through to the resonance tier (the
58
- * climb's own saturation semantics).
59
- * • per-node hub guards — parent fan-outs beyond √N are hubs (not
60
- * expanded); each node contributes at most one √N page of containers;
61
- * √N collected candidates decide. */
62
- export declare function junctionContainersFrom(ctx: MindContext, left: Uint8Array, right: Uint8Array, maxContainer: number, leftSeeds: number[], rightSeeds: number[],
63
- /** Shared expansion budget — a TIER's √N pops, not each walk's, when one
64
- * tier issues several walks (synonym junctions try up to 2·haloQueryK
65
- * siblings; without a shared budget each sibling would spend its own √N). */
66
- budget?: {
67
- n: number;
68
- },
69
- /** ORDER-FREE containment: also accept containers holding right-then-left.
70
- * A junction is evidence that the two forms were LEARNT TOGETHER; which
71
- * one the query happened to mention first is a fact about the query, not
72
- * about the learnt whole. The walk is identical (the seed ascent does not
73
- * depend on order) — only the byte-containment test gains a second probe,
74
- * so order-freedom costs two indexOf calls per visited node, never a
75
- * second walk. */
76
- unordered?: boolean): Junction[];
77
- /** Tier 1 entry point: every learnt whole that literally contains
78
- * left-then-right, found by ascending the structural DAG (parents +
79
- * containment links) from the two sides' content-addressed identities.
80
- * Both sides' seeds resolved fresh, one call. */
81
- export declare function junctionContainers(ctx: MindContext, left: Uint8Array, right: Uint8Array, maxContainer: number, unordered?: boolean): Junction[];
82
- /** Tier 2.5: synonym junctions — the container ascent (tier 1) applied to
83
- * halo siblings of left and right. When a distributional synonym of one
84
- * form participates in a learnt whole with the other form, the container
85
- * between the synonym and the other side is valid evidence for the
86
- * original pair. The container evidence is exact (content-addressed DAG
87
- * ascent, with window-id-enhanced seeds so non-W-aligned siblings still
88
- * ascend); the relaxation is only in which form occupies one side — a
89
- * distributional sibling rather than the exact form.
90
- *
91
- * ONE expansion budget is shared by every sibling walk in this call, so
92
- * cost is bounded at √N·W pops total regardless of how many siblings are
93
- * tried. A sibling whose bytes exceed `maxInterior` is skipped (it
94
- * cannot be junction-sized). */
95
- export declare function junctionSynonyms(ctx: MindContext, left: Uint8Array, right: Uint8Array, maxInterior: number, unordered?: boolean): Promise<Junction[]>;
@@ -1,262 +0,0 @@
1
- // junction.ts — content-addressed junction search (bridge Tier 1).
2
- //
3
- // "Which learnt wholes ran L and R together?" answered by DAG ascent, not a
4
- // similarity guess: hash-consing means any deposit containing L's bytes shares
5
- // L's node (or L's canonical-window ids — position-independent identities), so
6
- // climbing parents + containment links from L's and R's seeds reaches every
7
- // container that literally holds L-then-R. A resonance seed (the gist of the
8
- // bare concatenation — an object never learnt) could rank the true container
9
- // out of its top-k; the ascent cannot.
10
- //
11
- // Extracted from resonance.ts so BOTH the bridge (a connector between two
12
- // adjacent ANSWER pieces) and cross-region attention (the joint CONTEXT of two
13
- // non-adjacent QUERY regions) ascend by the same disciplined, bounded walk.
14
- import { read, resolve } from "./primitives.js";
15
- import { windowIds } from "./canonical.js";
16
- import { hubBound } from "./traverse.js";
17
- import { haloSiblings } from "./match.js";
18
- import { indexOf } from "../bytes.js";
19
- /** Seed node ids to ascend from for one side of a junction: the side's own
20
- * node when it is a stored form, plus — when the node has no structural
21
- * parents — its canonical window ids. A non-W-aligned node may have no
22
- * parents, but its constituent W-grams typically do; the window ids
23
- * provide alternative ascent paths. The `parentsFirst(…, 1)` probe is a
24
- * single indexed lookup, far cheaper than computing every window id, so
25
- * the window-id path is only taken when the node alone cannot ascend.
26
- * Exported for callers (synonym junctions) that hold one side FIXED across
27
- * several calls and so compute its seeds once instead of per call. */
28
- export function junctionSeeds(ctx, b) {
29
- const r = resolve(ctx, b);
30
- if (r !== null) {
31
- if (ctx.store.parentsFirst(r, 1).length > 0)
32
- return [r];
33
- const wids = [...windowIds(ctx, b).values()];
34
- return [r, ...wids];
35
- }
36
- const wids = [...windowIds(ctx, b).values()];
37
- if (wids.length <= 2)
38
- return wids;
39
- return [wids[0], wids[wids.length - 1]];
40
- }
41
- const walkCaches = new WeakMap();
42
- export function walkCache(ctx) {
43
- if (ctx.climbMemo === null)
44
- return null;
45
- let c = walkCaches.get(ctx.climbMemo);
46
- if (c === undefined) {
47
- walkCaches.set(ctx.climbMemo, c = { reads: new Map(), parents: new Map(), containers: new Map() });
48
- }
49
- return c;
50
- }
51
- export function cachedRead(ctx, cache, id, cap) {
52
- if (cache === null)
53
- return read(ctx, id, cap + 1);
54
- const hit = cache.reads.get(id);
55
- // A cached COMPLETE read serves any cap; a cached truncated read serves
56
- // any cap it already covers (the caller only checks `length > cap`).
57
- if (hit !== undefined && (hit.complete || hit.b.length > cap))
58
- return hit.b;
59
- const b = read(ctx, id, cap + 1);
60
- cache.reads.set(id, { b, complete: b.length <= cap });
61
- return b;
62
- }
63
- function cachedParents(ctx, cache, id, limit) {
64
- if (cache === null)
65
- return ctx.store.parentsFirst(id, limit);
66
- let v = cache.parents.get(id);
67
- if (v === undefined) {
68
- v = ctx.store.parentsFirst(id, limit);
69
- cache.parents.set(id, v);
70
- }
71
- return v;
72
- }
73
- function cachedContainers(ctx, cache, id, limit) {
74
- if (cache === null)
75
- return ctx.store.containersSlice(id, 0, limit);
76
- let v = cache.containers.get(id);
77
- if (v === undefined) {
78
- v = ctx.store.containersSlice(id, 0, limit);
79
- cache.containers.set(id, v);
80
- }
81
- return v;
82
- }
83
- /** Tier 1 body, parameterised on already-resolved seed lists so a caller
84
- * holding one side FIXED across several calls (synonym junctions) pays for
85
- * that side's seeds once, not once per call. The byte-containment check
86
- * below ensures only genuine containers are returned regardless of seeds.
87
- *
88
- * BOUNDED at corpus scale by three disciplines (profiled on a 17.7M-node
89
- * store, where the unbounded form spent >90% of a query's CPU here):
90
- * • PHRASE-SCALE READS — a junction container is by contract a whole the
91
- * pair nearly exhausts (glue from a period to a phrase), so every visit
92
- * reads at most `maxContainer + 1` bytes (`bytesPrefix` stops early).
93
- * A node whose bytes exceed the cap cannot be a junction container, and
94
- * its ancestors are strictly larger — the branch is PRUNED, never
95
- * reconstructing a corpus-sized deposit (an oasst2 conversation) just
96
- * to reject it.
97
- * • EXPANSION BUDGET — at most √N·W nodes are popped in total: a √N-wide
98
- * frontier (the one fan-out convention) through the ~W structural
99
- * levels that separate phrase-scale content from its containers
100
- * (perception trees are W-ary, so a junction container lies within a
101
- * few levels of its parts). A side too common to decide within the
102
- * budget abstains here and falls through to the resonance tier (the
103
- * climb's own saturation semantics).
104
- * • per-node hub guards — parent fan-outs beyond √N are hubs (not
105
- * expanded); each node contributes at most one √N page of containers;
106
- * √N collected candidates decide. */
107
- export function junctionContainersFrom(ctx, left, right, maxContainer, leftSeeds, rightSeeds,
108
- /** Shared expansion budget — a TIER's √N pops, not each walk's, when one
109
- * tier issues several walks (synonym junctions try up to 2·haloQueryK
110
- * siblings; without a shared budget each sibling would spend its own √N). */
111
- budget,
112
- /** ORDER-FREE containment: also accept containers holding right-then-left.
113
- * A junction is evidence that the two forms were LEARNT TOGETHER; which
114
- * one the query happened to mention first is a fact about the query, not
115
- * about the learnt whole. The walk is identical (the seed ascent does not
116
- * depend on order) — only the byte-containment test gains a second probe,
117
- * so order-freedom costs two indexOf calls per visited node, never a
118
- * second walk. */
119
- unordered = false) {
120
- const bound = hubBound(ctx);
121
- const joinedLength = left.length + right.length;
122
- const seeds = [...new Set([...leftSeeds, ...rightSeeds])];
123
- if (seeds.length === 0)
124
- return [];
125
- const b = budget ?? { n: bound * ctx.space.maxGroup };
126
- // DEPTH CAP: perception trees are W-ary and a junction container is
127
- // phrase-scale, so it sits within ~log_W(maxContainer) structural levels
128
- // of its parts — at most W levels for any practical W (plus the
129
- // containment hop the seeds already are). Ancestry beyond that depth is
130
- // strictly larger than any admissible container; walking it can only burn
131
- // budget, never find a junction.
132
- const maxDepth = ctx.space.maxGroup;
133
- const out = [];
134
- const cache = walkCache(ctx);
135
- const seen = new Set(seeds);
136
- const stack = seeds.map((id) => ({
137
- id,
138
- d: 0,
139
- }));
140
- while (stack.length > 0 && out.length < bound && b.n-- > 0) {
141
- const { id: x, d } = stack.pop();
142
- const f = cachedRead(ctx, cache, x, maxContainer);
143
- if (f.length > maxContainer)
144
- continue; // beyond phrase scale: prune branch
145
- if (unordered) {
146
- // Order-free containment does NOT require disjoint occurrences: two
147
- // grid-aligned fragments of the same whole legitimately OVERLAP inside
148
- // it ("red " at 0 and " cir" at 3 in `red circle`), and both being
149
- // literal substrings is the evidence. The interior is the gap between
150
- // them when they are disjoint, empty otherwise. Only the containment
151
- // test differs from the ordered form — and because occurrences may
152
- // overlap or abut, `f.length > joinedLength` is too strict (grid
153
- // fragments of one whole sum past it; "red " + "circle" exactly equals
154
- // `red circle`). The container must be a STRICT super-form of each
155
- // side, so that holding both is more than restating either.
156
- const li = indexOf(f, left, 0);
157
- const ri = li >= 0 ? indexOf(f, right, 0) : -1;
158
- if (li >= 0 && ri >= 0 && f.length > Math.max(left.length, right.length)) {
159
- const lo = Math.min(li + left.length, ri + right.length);
160
- const hi = Math.max(li, ri);
161
- out.push({
162
- id: x,
163
- interior: lo < hi ? f.subarray(lo, hi) : f.subarray(0, 0),
164
- });
165
- }
166
- }
167
- else if (f.length > joinedLength) {
168
- const li = indexOf(f, left, 0);
169
- if (li >= 0) {
170
- const ri = indexOf(f, right, li + left.length);
171
- if (ri >= 0) {
172
- out.push({ id: x, interior: f.subarray(li + left.length, ri) });
173
- }
174
- }
175
- }
176
- if (d >= maxDepth)
177
- continue; // deeper ancestry is beyond phrase scale
178
- const parents = cachedParents(ctx, cache, x, bound + 1);
179
- if (parents.length <= bound) { // beyond √N parents: a hub, not expanded
180
- for (const p of parents) {
181
- if (!seen.has(p)) {
182
- seen.add(p);
183
- stack.push({ id: p, d: d + 1 });
184
- }
185
- }
186
- }
187
- // Containment fan-out under the SAME hub reading as parents: a node
188
- // whose containers fill a whole √N page is COMMON content — its
189
- // containment ancestry reaches a non-discriminative slice of the corpus
190
- // (the climb's saturation semantics), and walking it would spend the
191
- // entire budget discovering nothing a junction could use. Such a node
192
- // is not expanded through containment; a pair whose sides are common
193
- // abstains here in a handful of pops and falls through to the resonance
194
- // tier. Below the page bound the read IS the full container list, so
195
- // the walk stays exact exactly where identity evidence discriminates.
196
- const containers = cachedContainers(ctx, cache, x, bound);
197
- if (containers.length < bound) {
198
- for (const c of containers) {
199
- if (!seen.has(c)) {
200
- seen.add(c);
201
- stack.push({ id: c, d: d + 1 });
202
- }
203
- }
204
- }
205
- }
206
- return out;
207
- }
208
- /** Tier 1 entry point: every learnt whole that literally contains
209
- * left-then-right, found by ascending the structural DAG (parents +
210
- * containment links) from the two sides' content-addressed identities.
211
- * Both sides' seeds resolved fresh, one call. */
212
- export function junctionContainers(ctx, left, right, maxContainer, unordered = false) {
213
- return junctionContainersFrom(ctx, left, right, maxContainer, junctionSeeds(ctx, left), junctionSeeds(ctx, right), undefined, unordered);
214
- }
215
- /** Tier 2.5: synonym junctions — the container ascent (tier 1) applied to
216
- * halo siblings of left and right. When a distributional synonym of one
217
- * form participates in a learnt whole with the other form, the container
218
- * between the synonym and the other side is valid evidence for the
219
- * original pair. The container evidence is exact (content-addressed DAG
220
- * ascent, with window-id-enhanced seeds so non-W-aligned siblings still
221
- * ascend); the relaxation is only in which form occupies one side — a
222
- * distributional sibling rather than the exact form.
223
- *
224
- * ONE expansion budget is shared by every sibling walk in this call, so
225
- * cost is bounded at √N·W pops total regardless of how many siblings are
226
- * tried. A sibling whose bytes exceed `maxInterior` is skipped (it
227
- * cannot be junction-sized). */
228
- export async function junctionSynonyms(ctx, left, right, maxInterior, unordered = false) {
229
- const out = [];
230
- const lId = resolve(ctx, left);
231
- const rId = resolve(ctx, right);
232
- if (lId === null && rId === null)
233
- return out;
234
- const budget = { n: hubBound(ctx) * ctx.space.maxGroup };
235
- // Left-side synonyms: containers of sibling+right. `right`'s seeds are
236
- // FIXED across every sibling this loop tries.
237
- if (lId !== null) {
238
- const rightSeeds = junctionSeeds(ctx, right);
239
- for (const sib of await haloSiblings(ctx, lId)) {
240
- const sibBytes = read(ctx, sib.id, maxInterior + 1);
241
- if (sibBytes.length === 0 || sibBytes.length > maxInterior)
242
- continue;
243
- const containers = junctionContainersFrom(ctx, sibBytes, right, sibBytes.length + right.length + maxInterior, junctionSeeds(ctx, sibBytes), rightSeeds, budget, unordered);
244
- for (const c of containers)
245
- out.push(c);
246
- }
247
- }
248
- // Right-side synonyms: containers of left+sibling. `left`'s seeds are
249
- // likewise fixed across this loop.
250
- if (rId !== null) {
251
- const leftSeeds = junctionSeeds(ctx, left);
252
- for (const sib of await haloSiblings(ctx, rId)) {
253
- const sibBytes = read(ctx, sib.id, maxInterior + 1);
254
- if (sibBytes.length === 0 || sibBytes.length > maxInterior)
255
- continue;
256
- const containers = junctionContainersFrom(ctx, left, sibBytes, left.length + sibBytes.length + maxInterior, leftSeeds, junctionSeeds(ctx, sibBytes), budget, unordered);
257
- for (const c of containers)
258
- out.push(c);
259
- }
260
- }
261
- return out;
262
- }
@@ -1,47 +0,0 @@
1
- import { Sema } from "../sema.js";
2
- import type { Input, MindContext } from "./types.js";
3
- /** Intern a perceived tree into node ids, bottom-up, sharing equal subtrees.
4
- * Returns the root node id and a map from tree nodes to their ids.
5
- *
6
- * Memoized by NODE IDENTITY (ctx._internIds): the pyramid fold shares a
7
- * prefix's subtree OBJECTS across an accumulated context's deposits, and a
8
- * node already interned needs nothing again — its id is permanent
9
- * (content-addressed) and its intern-time side effects (gist capture, kid
10
- * rows) fired at first mint; re-interning was pure lookups. A memo hit
11
- * therefore skips the WHOLE shared subtree, making the intern walk
12
- * O(new nodes) per deposit instead of O(context). Only the hit node
13
- * itself enters `ids`; descendants stay reachable via the memo (see
14
- * idOf in indexSubSpans and the changedNodes prune). */
15
- export declare function internTreeIds(ctx: MindContext, node: Sema, ids: Map<Sema, number>): Promise<number>;
16
- /** Index flat branches for sub-spans of a deposit's byte stream, linked to
17
- * their structural chunks via durable CONTAINMENT edges. */
18
- export declare function indexSubSpans(ctx: MindContext, tree: Sema, ids: Map<Sema, number>): Promise<boolean>;
19
- /** Perceive, intern, and index a single input. Returns the perceived tree,
20
- * root id, id map, and the changed (new) subtrees for halo reinforcement. */
21
- export declare function deposit(ctx: MindContext, input: Input, track: boolean): Promise<{
22
- tree: Sema;
23
- rootId: number;
24
- ids: Map<Sema, number>;
25
- changed: Sema[];
26
- }>;
27
- /** Ingest a single input (a bare experience, no continuation). */
28
- export declare function ingestOne(ctx: MindContext, input: Input): Promise<Sema & {
29
- id: number;
30
- }>;
31
- /** Ingest a pair (context, continuation) — learn an edge and pour halos. */
32
- export declare function ingestPair(ctx: MindContext, ctxInput: Input, cont: Input): Promise<void>;
33
- /** Dispatch the public ingest input shapes onto one-input / pair handlers —
34
- * THE one reading of ingest's polymorphic surface (scalar, (context,
35
- * continuation) pair, or a list mixing bare inputs and pairs). Both ingest
36
- * paths — the direct one below and {@link CachedIngest} — route through
37
- * this, so the shape-detection can never drift between them again (the
38
- * ingest cache once re-implemented it and drifted). */
39
- export declare function dispatchIngest(input: Input | (Input | [Input, Input])[], second: Input | undefined, onOne: (input: Input) => Promise<Sema & {
40
- id: number;
41
- }>, onPair: (ctxInput: Input, cont: Input) => Promise<void>): Promise<(Sema & {
42
- id: number;
43
- }) | undefined>;
44
- /** Ingest an input or array of inputs/pairs. The public ingest entry point. */
45
- export declare function ingest(ctx: MindContext, input: Input | (Input | [Input, Input])[], second?: Input): Promise<(Sema & {
46
- id: number;
47
- }) | undefined>;
@@ -1,233 +0,0 @@
1
- // learning.ts — ingest and deposition (Section 7 of the mind).
2
- //
3
- // Learning is DEPOSITION: perceive a stream into a tree and intern every
4
- // node. A fact is an EDGE between node ids; recall traverses edges.
5
- import { bindSeat, companySignature, isChunk } from "../sema.js";
6
- import { changedNodes } from "./types.js";
7
- import { inputBytes, perceiveDeposit, resolve, } from "./primitives.js";
8
- import { canonicalWindows, leafIdPrefix } from "./canonical.js";
9
- import { fold as foldVecs } from "../sema.js";
10
- /** Intern a perceived tree into node ids, bottom-up, sharing equal subtrees.
11
- * Returns the root node id and a map from tree nodes to their ids.
12
- *
13
- * Memoized by NODE IDENTITY (ctx._internIds): the pyramid fold shares a
14
- * prefix's subtree OBJECTS across an accumulated context's deposits, and a
15
- * node already interned needs nothing again — its id is permanent
16
- * (content-addressed) and its intern-time side effects (gist capture, kid
17
- * rows) fired at first mint; re-interning was pure lookups. A memo hit
18
- * therefore skips the WHOLE shared subtree, making the intern walk
19
- * O(new nodes) per deposit instead of O(context). Only the hit node
20
- * itself enters `ids`; descendants stay reachable via the memo (see
21
- * idOf in indexSubSpans and the changedNodes prune). */
22
- export async function internTreeIds(ctx, node, ids) {
23
- const known = ctx._internIds.get(node);
24
- if (known !== undefined) {
25
- ids.set(node, known);
26
- return known;
27
- }
28
- let id;
29
- if (node.kids === null) {
30
- id = await ctx.store.putLeaf(node.leaf ?? new Uint8Array(0), node.v);
31
- }
32
- else {
33
- const kds = [];
34
- for (const k of node.kids)
35
- kds.push(await internTreeIds(ctx, k, ids));
36
- id = await ctx.store.putBranch(kds, node.v);
37
- }
38
- ids.set(node, id);
39
- ctx._internIds.set(node, id);
40
- return id;
41
- }
42
- /** Index flat branches for sub-spans of a deposit's byte stream, linked to
43
- * their structural chunks via durable CONTAINMENT edges. */
44
- export async function indexSubSpans(ctx, tree, ids) {
45
- const chunkOf = [];
46
- const streamIds = [];
47
- const streamVecs = [];
48
- const collect = (n) => {
49
- if (isChunk(n)) {
50
- // A chunk inside a memo-skipped shared subtree is absent from `ids`;
51
- // the intern memo still knows it (same object). A miss on both (the
52
- // WeakMap entry was collected) only forfeits the seenBefore skip.
53
- const chunkId = ids.get(n) ?? ctx._internIds.get(n);
54
- for (const k of n.kids) {
55
- const lid = k.leaf ? ctx.store.findLeaf(k.leaf) : null;
56
- if (lid === null)
57
- return false;
58
- streamIds.push(lid);
59
- streamVecs.push(k.v);
60
- chunkOf.push(chunkId);
61
- }
62
- return true;
63
- }
64
- if (n.kids) {
65
- for (const k of n.kids)
66
- if (!collect(k))
67
- return false;
68
- }
69
- return true;
70
- };
71
- if (!collect(tree))
72
- return false;
73
- const W = ctx.space.maxGroup; // write side of the canonical contract
74
- const prev = ctx._prevSeen;
75
- const seenBefore = (off, len) => {
76
- if (!prev)
77
- return false;
78
- for (let i = off; i < off + len; i++) {
79
- const c = chunkOf[i];
80
- if (c === undefined || !prev.has(c))
81
- return false;
82
- }
83
- return true;
84
- };
85
- const lens = streamIds.length >= W ? canonicalWindows(W) : [streamIds.length];
86
- for (const len of lens) {
87
- if (len < 1)
88
- continue;
89
- for (let off = 0; off + len <= streamIds.length; off++) {
90
- if (seenBefore(off, len))
91
- continue;
92
- const winIds = streamIds.slice(off, off + len);
93
- const flatId = ctx.store.findBranch(winIds) ??
94
- await ctx.store.putBranch(winIds, foldVecs(ctx.space, streamVecs.slice(off, off + len)));
95
- for (let i = off; i < off + len; i++) {
96
- const c = chunkOf[i];
97
- if (c !== undefined)
98
- ctx.store.addContainer(flatId, c);
99
- }
100
- }
101
- }
102
- return true;
103
- }
104
- /** Perceive, intern, and index a single input. Returns the perceived tree,
105
- * root id, id map, and the changed (new) subtrees for halo reinforcement. */
106
- export async function deposit(ctx, input, track) {
107
- const bytes = inputBytes(ctx, input);
108
- // Deposit-shaped perception: stable-prefix tree SEEDING (see
109
- // perceiveDeposit) — an accumulated context re-folds only its new suffix,
110
- // O(turn) instead of O(context) per conversation turn. Cache-only here
111
- // (no store-probe fallback): a knownPrefixLength scan on every novel fact
112
- // would cost O(n²) hashing, while conversation replays are always warm —
113
- // re-deposition replays from the first turn, rebuilding the cache as it
114
- // goes.
115
- const tree = perceiveDeposit(ctx, bytes);
116
- const ids = new Map();
117
- const rootId = await internTreeIds(ctx, tree, ids);
118
- const indexed = await indexSubSpans(ctx, tree, ids);
119
- const leafIds = leafIdPrefix(ctx, bytes);
120
- if (leafIds.length === bytes.length && leafIds.length >= 2) {
121
- await ctx.store.putBranch(leafIds, tree.v);
122
- }
123
- const changed = (track && ctx._prevSeen)
124
- ? changedNodes(tree, ids, ctx._prevSeen)
125
- : [tree];
126
- if (track)
127
- ctx._prevSeen = indexed ? new Set(ids.values()) : null;
128
- return { tree, rootId, ids, changed };
129
- }
130
- /** Ingest a single input (a bare experience, no continuation). */
131
- export async function ingestOne(ctx, input) {
132
- const { tree, rootId, ids } = await deposit(ctx, input, true);
133
- ctx.store.indexTarget(rootId);
134
- const parts = tree.kids
135
- ? tree.kids.map((k) => ids.get(k))
136
- : [rootId];
137
- const stride = ctx.space.maxGroup;
138
- if (parts.length > stride) {
139
- for (let i = 0; i + stride < parts.length; i += stride) {
140
- await ctx.store.link(parts[i], parts[i + stride]);
141
- }
142
- if ((parts.length - 1) % stride !== 0) {
143
- const lastStart = Math.floor((parts.length - 1) / stride) * stride;
144
- if (lastStart < parts.length - 1) {
145
- await ctx.store.link(parts[lastStart], parts[parts.length - 1]);
146
- }
147
- }
148
- }
149
- else {
150
- for (const id of parts)
151
- ctx.store.indexTarget(id);
152
- }
153
- return Object.assign(tree, { id: rootId });
154
- }
155
- /** For each right-edge suffix of the context bytes, resolve it against the
156
- * store. A suffix whose resolved node is already a known form inherits the
157
- * continuation edge. Gate: ≥ 2 structural parents (reused across deposits),
158
- * or (halo > 0 ∧ already an edge source). Pure answers do not qualify. */
159
- async function propagateSuffixes(ctx, src, dst) {
160
- const W = ctx.space.maxGroup;
161
- const bytes = ctx.store.bytes(src);
162
- const n = bytes.length;
163
- if (n < 2 * W)
164
- return;
165
- // Existence prefilter — the write side of the canonical contract: every
166
- // deposit interns its WHOLE byte stream as a flat branch of per-byte leaf
167
- // ids (deposit(), canonical.ts). A suffix is a stored form exactly when
168
- // that flat twin exists, so one content-hash probe per offset decides;
169
- // only a hit pays for resolve()'s deposit-shaped perception. This keeps
170
- // the scan free of river folds — O(1) probes over cheap byte hashes
171
- // instead of O(suffix) vector folds per offset.
172
- const leafIds = leafIdPrefix(ctx, bytes);
173
- for (let i = 1; i <= n - W; i++) {
174
- if (ctx.store.findBranch(leafIds.slice(i)) === null)
175
- continue;
176
- const id = resolve(ctx, bytes.subarray(i));
177
- if (id === null || id === src)
178
- continue;
179
- const known = ctx.store.parentsFirst(id, 2).length >= 2 ||
180
- (ctx.store.haloMass(id) > 0 && ctx.store.hasNext(id));
181
- if (!known)
182
- continue;
183
- await ctx.store.link(id, dst);
184
- }
185
- }
186
- /** Ingest a pair (context, continuation) — learn an edge and pour halos. */
187
- export async function ingestPair(ctx, ctxInput, cont) {
188
- const c = await deposit(ctx, ctxInput, true);
189
- const cont_ = await deposit(ctx, cont, false);
190
- const ctxId = c.rootId, contId = cont_.rootId;
191
- await ctx.store.link(ctxId, contId);
192
- await propagateSuffixes(ctx, ctxId, contId);
193
- // Halos pour company SIGNATURES (identity), not gists (content) — see
194
- // companySignature in sema.ts.
195
- const contSeat = bindSeat(ctx.space, companySignature(ctx.space, contId), 1);
196
- for (const part of c.changed) {
197
- const partId = c.ids.get(part);
198
- await ctx.store.pourHalo(partId, contSeat);
199
- await ctx.store.pourHalo(contId, bindSeat(ctx.space, companySignature(ctx.space, partId), 0));
200
- }
201
- }
202
- /** Dispatch the public ingest input shapes onto one-input / pair handlers —
203
- * THE one reading of ingest's polymorphic surface (scalar, (context,
204
- * continuation) pair, or a list mixing bare inputs and pairs). Both ingest
205
- * paths — the direct one below and {@link CachedIngest} — route through
206
- * this, so the shape-detection can never drift between them again (the
207
- * ingest cache once re-implemented it and drifted). */
208
- export async function dispatchIngest(input, second, onOne, onPair) {
209
- if (Array.isArray(input) && !(input instanceof Uint8Array) &&
210
- input.width === undefined) {
211
- const arr = input;
212
- if (arr.length === 2 && !Array.isArray(arr[0]) && !Array.isArray(arr[1])) {
213
- await onPair(arr[0], arr[1]);
214
- return undefined;
215
- }
216
- for (const item of arr) {
217
- if (Array.isArray(item) && item.length === 2) {
218
- await onPair(item[0], item[1]);
219
- }
220
- else
221
- await onOne(item);
222
- }
223
- return undefined;
224
- }
225
- if (second === undefined)
226
- return onOne(input);
227
- await onPair(input, second);
228
- return undefined;
229
- }
230
- /** Ingest an input or array of inputs/pairs. The public ingest entry point. */
231
- export async function ingest(ctx, input, second) {
232
- return dispatchIngest(input, second, (i) => ingestOne(ctx, i), (a, b) => ingestPair(ctx, a, b));
233
- }