@hviana/sema 0.5.2 → 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 (157) hide show
  1. package/AGENTS.md +115 -53
  2. package/HOW_IT_WORKS.md +291 -184
  3. package/jsr.json +6 -0
  4. package/package.json +1 -1
  5. package/src/mind/bridge.ts +10 -104
  6. package/src/mind/match.ts +497 -7
  7. package/src/mind/{prefix-completion.ts → mechanisms/prefix-completion.ts} +66 -92
  8. package/src/mind/mechanisms/recall.ts +38 -129
  9. package/src/mind/mechanisms/reference.ts +343 -0
  10. package/src/mind/mind.ts +12 -8
  11. package/src/mind/pipeline-mechanism.ts +120 -3
  12. package/src/mind/pipeline.ts +16 -2
  13. package/src/mind/traverse.ts +92 -1
  14. package/src/store.ts +13 -4
  15. package/test/33-multi-candidate.test.mjs +21 -11
  16. package/test/70-prefix-completion.test.mjs +1 -1
  17. package/test/72-prefix-candidate-supply.test.mjs +7 -9
  18. package/test/74-prefix-trap-not-sprung-early.test.mjs +1 -1
  19. package/test/76-reference-binding.test.mjs +543 -0
  20. package/dist/example/demo.d.ts +0 -1
  21. package/dist/example/demo.js +0 -39
  22. package/dist/example/train_base.d.ts +0 -93
  23. package/dist/example/train_base.js +0 -2269
  24. package/dist/src/alphabet.d.ts +0 -7
  25. package/dist/src/alphabet.js +0 -33
  26. package/dist/src/alu/src/alu.d.ts +0 -185
  27. package/dist/src/alu/src/alu.js +0 -440
  28. package/dist/src/alu/src/expr.d.ts +0 -61
  29. package/dist/src/alu/src/expr.js +0 -318
  30. package/dist/src/alu/src/index.d.ts +0 -11
  31. package/dist/src/alu/src/index.js +0 -19
  32. package/dist/src/alu/src/kernel-arith.d.ts +0 -16
  33. package/dist/src/alu/src/kernel-arith.js +0 -264
  34. package/dist/src/alu/src/kernel-bits.d.ts +0 -19
  35. package/dist/src/alu/src/kernel-bits.js +0 -152
  36. package/dist/src/alu/src/kernel-logic.d.ts +0 -4
  37. package/dist/src/alu/src/kernel-logic.js +0 -60
  38. package/dist/src/alu/src/kernel-nd.d.ts +0 -3
  39. package/dist/src/alu/src/kernel-nd.js +0 -208
  40. package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
  41. package/dist/src/alu/src/kernel-numeric.js +0 -366
  42. package/dist/src/alu/src/operation.d.ts +0 -168
  43. package/dist/src/alu/src/operation.js +0 -189
  44. package/dist/src/alu/src/parser.d.ts +0 -221
  45. package/dist/src/alu/src/parser.js +0 -577
  46. package/dist/src/alu/src/resonance.d.ts +0 -55
  47. package/dist/src/alu/src/resonance.js +0 -126
  48. package/dist/src/alu/src/text.d.ts +0 -31
  49. package/dist/src/alu/src/text.js +0 -73
  50. package/dist/src/alu/src/value.d.ts +0 -109
  51. package/dist/src/alu/src/value.js +0 -300
  52. package/dist/src/alu/test/alu.test.d.ts +0 -1
  53. package/dist/src/alu/test/alu.test.js +0 -764
  54. package/dist/src/bytes.d.ts +0 -14
  55. package/dist/src/bytes.js +0 -59
  56. package/dist/src/canon.d.ts +0 -45
  57. package/dist/src/canon.js +0 -85
  58. package/dist/src/config.d.ts +0 -111
  59. package/dist/src/config.js +0 -91
  60. package/dist/src/derive/src/deduction.d.ts +0 -136
  61. package/dist/src/derive/src/deduction.js +0 -159
  62. package/dist/src/derive/src/index.d.ts +0 -8
  63. package/dist/src/derive/src/index.js +0 -11
  64. package/dist/src/derive/src/priority-queue.d.ts +0 -20
  65. package/dist/src/derive/src/priority-queue.js +0 -73
  66. package/dist/src/derive/src/rewrite.d.ts +0 -56
  67. package/dist/src/derive/src/rewrite.js +0 -100
  68. package/dist/src/derive/src/trie.d.ts +0 -90
  69. package/dist/src/derive/src/trie.js +0 -217
  70. package/dist/src/derive/test/derive.test.d.ts +0 -1
  71. package/dist/src/derive/test/derive.test.js +0 -122
  72. package/dist/src/extension.d.ts +0 -37
  73. package/dist/src/extension.js +0 -7
  74. package/dist/src/geometry.d.ts +0 -233
  75. package/dist/src/geometry.js +0 -1089
  76. package/dist/src/index.d.ts +0 -17
  77. package/dist/src/index.js +0 -19
  78. package/dist/src/ingest-cache.d.ts +0 -41
  79. package/dist/src/ingest-cache.js +0 -165
  80. package/dist/src/meter.d.ts +0 -176
  81. package/dist/src/meter.js +0 -274
  82. package/dist/src/mind/articulation.d.ts +0 -6
  83. package/dist/src/mind/articulation.js +0 -99
  84. package/dist/src/mind/attention.d.ts +0 -430
  85. package/dist/src/mind/attention.js +0 -2418
  86. package/dist/src/mind/bridge.d.ts +0 -39
  87. package/dist/src/mind/bridge.js +0 -998
  88. package/dist/src/mind/canonical.d.ts +0 -34
  89. package/dist/src/mind/canonical.js +0 -93
  90. package/dist/src/mind/frame-filler.d.ts +0 -15
  91. package/dist/src/mind/frame-filler.js +0 -535
  92. package/dist/src/mind/graph-search.d.ts +0 -294
  93. package/dist/src/mind/graph-search.js +0 -996
  94. package/dist/src/mind/index.d.ts +0 -9
  95. package/dist/src/mind/index.js +0 -5
  96. package/dist/src/mind/junction.d.ts +0 -137
  97. package/dist/src/mind/junction.js +0 -358
  98. package/dist/src/mind/learning.d.ts +0 -75
  99. package/dist/src/mind/learning.js +0 -265
  100. package/dist/src/mind/match.d.ts +0 -192
  101. package/dist/src/mind/match.js +0 -666
  102. package/dist/src/mind/mechanisms/alu.d.ts +0 -4
  103. package/dist/src/mind/mechanisms/alu.js +0 -36
  104. package/dist/src/mind/mechanisms/cast.d.ts +0 -89
  105. package/dist/src/mind/mechanisms/cast.js +0 -1186
  106. package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
  107. package/dist/src/mind/mechanisms/confluence.js +0 -271
  108. package/dist/src/mind/mechanisms/cover.d.ts +0 -6
  109. package/dist/src/mind/mechanisms/cover.js +0 -232
  110. package/dist/src/mind/mechanisms/extraction.d.ts +0 -33
  111. package/dist/src/mind/mechanisms/extraction.js +0 -327
  112. package/dist/src/mind/mechanisms/recall.d.ts +0 -16
  113. package/dist/src/mind/mechanisms/recall.js +0 -558
  114. package/dist/src/mind/mind.d.ts +0 -386
  115. package/dist/src/mind/mind.js +0 -754
  116. package/dist/src/mind/pipeline-mechanism.d.ts +0 -183
  117. package/dist/src/mind/pipeline-mechanism.js +0 -521
  118. package/dist/src/mind/pipeline.d.ts +0 -49
  119. package/dist/src/mind/pipeline.js +0 -331
  120. package/dist/src/mind/prefix-completion.d.ts +0 -59
  121. package/dist/src/mind/prefix-completion.js +0 -270
  122. package/dist/src/mind/primitives.d.ts +0 -85
  123. package/dist/src/mind/primitives.js +0 -333
  124. package/dist/src/mind/rationale.d.ts +0 -139
  125. package/dist/src/mind/rationale.js +0 -163
  126. package/dist/src/mind/reasoning.d.ts +0 -42
  127. package/dist/src/mind/reasoning.js +0 -305
  128. package/dist/src/mind/recognition.d.ts +0 -20
  129. package/dist/src/mind/recognition.js +0 -696
  130. package/dist/src/mind/resonance.d.ts +0 -35
  131. package/dist/src/mind/resonance.js +0 -365
  132. package/dist/src/mind/trace.d.ts +0 -15
  133. package/dist/src/mind/trace.js +0 -73
  134. package/dist/src/mind/traverse.d.ts +0 -158
  135. package/dist/src/mind/traverse.js +0 -716
  136. package/dist/src/mind/types.d.ts +0 -418
  137. package/dist/src/mind/types.js +0 -179
  138. package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
  139. package/dist/src/rabitq-ivf/src/database.js +0 -201
  140. package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
  141. package/dist/src/rabitq-ivf/src/index.js +0 -4
  142. package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
  143. package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
  144. package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
  145. package/dist/src/rabitq-ivf/src/prng.js +0 -36
  146. package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
  147. package/dist/src/rabitq-ivf/src/rabitq.js +0 -313
  148. package/dist/src/sema.d.ts +0 -41
  149. package/dist/src/sema.js +0 -77
  150. package/dist/src/store-sqlite.d.ts +0 -184
  151. package/dist/src/store-sqlite.js +0 -942
  152. package/dist/src/store.d.ts +0 -762
  153. package/dist/src/store.js +0 -1823
  154. package/dist/src/vec.d.ts +0 -31
  155. package/dist/src/vec.js +0 -109
  156. package/src/mind/frame-filler.ts +0 -604
  157. package/test/69-frame-filler.test.mjs +0 -115
@@ -1,418 +0,0 @@
1
- import type { Vec } from "../vec.js";
2
- import type { Sema } from "../sema.js";
3
- import type { BoundedMap, Store } from "../store.js";
4
- import type { Space } from "../sema.js";
5
- import type { Alphabet } from "../alphabet.js";
6
- import type { MindConfig } from "../config.js";
7
- import type { Meter } from "../meter.js";
8
- import type { GraphSearch, Leaf, Seg, Site } from "./graph-search.js";
9
- import type { Rationale } from "./rationale.js";
10
- import type { ContentFold, Grid } from "../geometry.js";
11
- /** One {@link MindContext._depositTrees} entry — see that field's doc.
12
- *
13
- * A PURE WORK CACHE. It carries the already-folded content segments of a
14
- * deposited stream so a longer stream sharing its byte prefix can skip
15
- * refolding them. It holds no turn boundaries and no continuation proof
16
- * because the deposit fold imposes nothing: reuse is bit-identical to a cold
17
- * fold, so a hit can only save time, never change a tree. */
18
- export interface DepositCacheEntry {
19
- /** The plain content fold's reusable segment state. */
20
- content: ContentFold;
21
- }
22
- export type Input = string | Uint8Array | Grid | Grid[];
23
- /** The host capabilities GraphSearch consults during a cover. MindContext
24
- * extends this so the Mind can pass itself as the host. */
25
- export interface GraphSearchHost {
26
- /** Work accumulator, or null/absent when nothing is profiling — see
27
- * src/meter.ts. Declared here (not only on MindContext) so the graph
28
- * search can report its chart effort without importing mind code. */
29
- readonly meter?: Meter | null;
30
- resolve(bytes: Uint8Array): number | null;
31
- recogniseSpan?(bytes: Uint8Array): {
32
- sites: ReadonlyArray<Site>;
33
- leaves: ReadonlyArray<Leaf>;
34
- splits: ReadonlySet<number>;
35
- starts: ReadonlySet<number>;
36
- };
37
- chooseNext?(node: number): number | undefined;
38
- }
39
- export interface Recognition {
40
- /** Forms that can lead somewhere — they have an edge or a halo. */
41
- sites: Site[];
42
- /** The query's perceived leaves (the search's covering axioms). */
43
- leaves: Leaf[];
44
- /** Sub-leaf positions where a form boundary falls between leaf edges. */
45
- splits: Set<number>;
46
- /** Leaf-parent (chunk) start positions from the query's OWN perceived
47
- * fold — the positions the fold itself chose as a grouping boundary, as
48
- * opposed to an offset a byte-level scan merely happens to land on. The
49
- * one boundary signal opportunistic cross-leaf recovery (recognition's
50
- * own canonical chains, the search's `fuse`) can lean on instead of
51
- * ASCII/word heuristics: see the `boundary` gate in recognition.ts. */
52
- starts: Set<number>;
53
- }
54
- /** How the consensus climb weights a region's Document-Frequency reach. */
55
- export type DFMode = "inverse" | "direct" | "combined";
56
- /** One POINT OF ATTENTION the consensus climb resolved. */
57
- export interface Attention {
58
- /** The learnt context this point resolves to. */
59
- anchor: number;
60
- /** IDF-weighted consensus vote — the strength that orders points. */
61
- vote: number;
62
- /** The query span of the point's STRONGEST contributing region — the argmax
63
- * over `wFocus` (see `peak`, which is that same region's weight), NOT a
64
- * union or hull over every region that voted. Measured on test/24 3.2: the
65
- * winning anchor's span here was 2 bytes while its contributing regions
66
- * together covered most of the query. It is the minimal honest statement
67
- * of what a grounding on this anchor rests on, and recall accounts exactly
68
- * it for that reason — widening it to every contributing region made recall
69
- * out-bid mechanisms that had genuinely explained more (a GENERATED list
70
- * degraded to a RETRIEVED one, test/24 3.2 and test/04 1). */
71
- start: number;
72
- end: number;
73
- /** The largest SINGLE region's contribution to this point's pooled vote —
74
- * the weight of the very region `start`..`end` delimits (both are the
75
- * argmax over `wFocus`), so the two fields describe one region: its
76
- * strength and its place.
77
- * `vote` is a sum over every region that agreed, so it grows with how many
78
- * places corroborated; `peak` is what the strongest one of them said on its
79
- * own. A consumer holding this point to consensusFloor(N) — a bar that
80
- * prices ONE region's maximally-discriminative evidence — must read `peak`,
81
- * not `vote`: six scaffolding regions summing past the floor is not the
82
- * same claim as one region clearing it. */
83
- peak: number;
84
- /** SCALE-INVARIANT confidence: the fraction of the query's OWN regions
85
- * whose evidence this point accounts for (Σ RegionVote.absorbed among
86
- * its contributors, over the query's total region count) — read PER-
87
- * ANCHOR, unlike the raw IDF vote (an absolute, ln(N)-scaled quantity
88
- * that means "strong" on a small store and "weak" on a large one for
89
- * the SAME degree of genuine consensus). A point whose breadth clears
90
- * `dominates` (> half the query's regions corroborate it) is real
91
- * consensus; one that does not is a coincidental single-region echo —
92
- * see test/35-attention-confidence.test.mjs. */
93
- breadth: number;
94
- /** DISPERSION: the number of distinct clusters this point's contributing
95
- * regions form, merging any two whose gap is under one river-fold
96
- * quantum W. Neither breadth NOR raw region count discriminates a
97
- * genuine further topic from a coincidental echo (both were tried and
98
- * falsified — breadth starves a genuine, evenly-split multi-topic query,
99
- * since no root in a real N-way split can exceed half the vote; raw
100
- * count doesn't separate them either, since a short, structurally simple
101
- * echo racks up as many corroborating regions as a real topic does).
102
- * Dispersion asks a different question: not how MUCH evidence, but how
103
- * many separate PLACES in the query corroborate it. A coincidental
104
- * match — one local phrase resonating with an unrelated stored form —
105
- * is structurally confined to ONE cluster no matter how strong its vote;
106
- * a genuine further topic is named in its own distinctive wording
107
- * somewhere the query's scaffolding does not reach, always a SEPARATE
108
- * cluster from whatever else corroborates it. See
109
- * test/37-cluster-dispersion-fusion.test.mjs. */
110
- clusters: number;
111
- }
112
- /** Both read-outs of one consensus climb. */
113
- export interface AttentionRead {
114
- roots: Attention[];
115
- ranked: Attention[];
116
- }
117
- /** A positioned region of a byte stream paired with its gist. */
118
- export interface Segment {
119
- start: number;
120
- end: number;
121
- v: Vec;
122
- }
123
- /** A region of the query's perceived tree for the consensus climb. */
124
- export interface Region {
125
- v: Vec;
126
- start: number;
127
- end: number;
128
- chunk: boolean;
129
- /** Whether the region's bytes resolve to a KNOWN node (content-addressed,
130
- * exact). Exact regions vote with full weight; approximate ones pay the
131
- * contrastive margin (see voteRegions) — under the linear fold a raw
132
- * resonance score is byte-overlap, evidence only in excess of its best
133
- * rival conclusion. */
134
- known: boolean;
135
- /** The stored node this region's bytes ARE, when the region came from a
136
- * recognised SITE — content-addressed and exact, so the climb has no
137
- * reason to re-derive it approximately. A perceived sub-tree leaves this
138
- * undefined; chunks get the same thing from `canonicalChunkId`. */
139
- id?: number;
140
- /** EVIDENCE, NOT A POINT OF ATTENTION. True for a region the query's own
141
- * fold never produced — a stored form that a content-defined cut SPLIT,
142
- * recovered by sliding-window lookup in collectRegions. The store
143
- * guarantees such a form is addressable (canonicalWindows interns both
144
- * lengths), so it may corroborate an anchor's vote; but the query did not
145
- * weave it as an independent structure, so it must not make the query look
146
- * like it holds one more point of attention than it does — it is kept out
147
- * of the root-cut distribution and out of the breadth ratio (see
148
- * poolVotes/commitVotes). Absent/false for every region from the fold.
149
- * (Flagging these `chunk: true` instead is REFUTED — a chunk is a
150
- * smallest unit the FOLD produced, and claiming first-class unit status
151
- * for an assembled span cost 5 tests.) */
152
- corroborating?: boolean;
153
- }
154
- /** Per-region vote data from the consensus climb's resonance pass. */
155
- export interface RegionVote {
156
- start: number;
157
- end: number;
158
- canonicalFailed: boolean;
159
- roots: readonly number[];
160
- w: number;
161
- wFocus: number;
162
- /** How many of the query's ORIGINAL regions this one vote's evidence
163
- * accounts for. 1 for an ordinary per-region vote (itself); for a
164
- * cross-region junction vote, 1 (itself) plus however many individual
165
- * votes it explained away (see crossRegionVotes) — the junction speaks
166
- * for all of them at once, and breadth accounting must not undercount it
167
- * to "one region" just because it collapsed to one pooled axiom.
168
- * Defaults to 1 when absent. */
169
- absorbed?: number;
170
- /** The SEPARATE query places this vote's evidence occupies, when that is
171
- * more than the one contiguous run [start, end]. A cross-region junction
172
- * vote is pooled as a single synthetic region spanning its endpoints and
173
- * the gap between them, so `[start, end]` reads as ONE place — yet the
174
- * vote exists precisely because two non-adjacent regions each voted and
175
- * only their conjunction resolved. Cluster counting (Attention.clusters)
176
- * asks "how many separate places in the query corroborate this?", and
177
- * answering it from the merged span makes every joint binding look like a
178
- * single local neighbourhood; fusion's dispersion gate then drops it
179
- * unless it also explains most of the whole query, which a binding inside
180
- * a MULTI-topic query structurally cannot. Absent for an ordinary
181
- * per-region vote, where the merged span already is the truth. */
182
- parts?: readonly (readonly [number, number])[];
183
- /** Carried through from {@link Region.corroborating}: this vote's evidence
184
- * is a stored form the query's fold SPLIT, not a structure the query wove.
185
- * Votes are what the pool sees (regions are not), so the flag has to
186
- * travel with the vote for the root election to honour it. */
187
- corroborating?: boolean;
188
- }
189
- /** The structural gate that first decided an {@link edgeAncestors} climb was
190
- * saturated (an abstention, not a discriminative conclusion) — pure
191
- * instrumentation for {@link ClimbConsensusData}'s reach trace; it never
192
- * feeds back into the climb itself. */
193
- export type SaturationReason = "byte-atom-commonality" | "predecessor-fan-in" | "distinct-context-limit" | "parent-fan-out" | "lateral-cone-limit";
194
- /** One saturation stop's provenance: which reason fired, at which node, the
195
- * observed count against the bound that decided it. */
196
- export interface SaturationStop {
197
- reason: SaturationReason;
198
- node: number;
199
- observed: number;
200
- limit: number;
201
- }
202
- /** The edge-bearing contexts reached by climbing from a node, plus saturation info. */
203
- export interface AncestorReach {
204
- roots: number[];
205
- contextsReached: number;
206
- saturated: boolean;
207
- /** The saturation gate that stopped this climb, when {@link saturated} is
208
- * true and a trace was requested — see {@link edgeAncestors}. Absent for
209
- * a non-saturated reach, and absent (even when saturated) when no trace
210
- * was requested — instrumentation must not allocate when tracing is off. */
211
- saturation?: SaturationStop;
212
- /** The number of nodes the climb actually PROCESSED (popped and examined
213
- * by its visit step; a transparent chain counts as its one terminal).
214
- * Present only when a trace was requested — same contract as
215
- * {@link saturation}: instrumentation must not allocate when tracing is
216
- * off. Purely a read-out; the climb never consults it. */
217
- visited?: number;
218
- /** The maximum structural ascent distance (in parent/containment hops,
219
- * transparent-chain interiors counted) from the start node among the
220
- * processed nodes. Present only when a trace was requested — see
221
- * {@link visited}. */
222
- maxDepth?: number;
223
- }
224
- /** Saturated-interval information for the noise-drop gate. */
225
- export interface SaturationInfo {
226
- leadingEnd: number;
227
- hasLeading: boolean;
228
- intervals: Array<{
229
- start: number;
230
- end: number;
231
- }>;
232
- }
233
- /** The items of poolVotes' deduction system. */
234
- export type AItem = {
235
- kind: "region";
236
- ri: number;
237
- } | {
238
- kind: "anchor";
239
- id: number;
240
- } | {
241
- kind: "anchorFocus";
242
- id: number;
243
- };
244
- export interface MindContext extends GraphSearchHost {
245
- store: Store;
246
- /** The work accumulator for the inference call in flight, or null when
247
- * nothing is profiling — see src/meter.ts. WRITE-ONLY from the engine's
248
- * point of view: no inference decision may read a counter, or the
249
- * determinism contract (AGENTS §2.1) is gone. Every call site is
250
- * `ctx.meter?.x++`, so an unprofiled response allocates nothing. */
251
- meter: Meter | null;
252
- space: Space;
253
- alphabet: Alphabet;
254
- cfg: MindConfig;
255
- search: GraphSearch;
256
- trace: Rationale | null;
257
- /** The content canonicalizer for THIS response, or null — injected by the
258
- * modality entry point (respondText passes the text canonicalizer; a
259
- * binary respond passes none). Resolution uses it as a fallback: when
260
- * the exact content-addressed lookup misses, the span's canonical key is
261
- * probed against the store's canon index (see src/canon.ts). The core
262
- * never inspects what the equivalence IS. */
263
- canon: ((bytes: Uint8Array) => Uint8Array) | null;
264
- /** Per-response memo of canonical-fallback resolutions, keyed by the
265
- * span's latin1 content key. Null outside respond(). */
266
- canonMemo: Map<string, number | null> | null;
267
- /** Memo of the consensus climb — content-keyed (latin1) so results
268
- * persist across conversation turns where the same byte spans recur.
269
- * Null outside respond(); during respondTurn() the conversation's
270
- * persistent map is swapped in. */
271
- climbMemo: Map<string, Map<string, AttentionRead>> | null;
272
- /** Stable identity for session-lifetime, write-invalidated structural
273
- * caches. Query-level climb results remain on climbMemo. */
274
- _structMemoKey: object;
275
- /** Memo of {@link recognise} — content-keyed (latin1) so recognised
276
- * forms carry forward across conversation turns. Bypassed while a
277
- * trace is attached. Null outside respond(). */
278
- recogniseMemo: Map<string, Recognition> | null;
279
- /** Memo of {@link perceive} — content-keyed (latin1). The general
280
- * cache the result-level memos each partially compensate for. NOT
281
- * bypassed under trace — perception emits no rationale steps.
282
- * Null outside respond(). */
283
- perceiveMemo: Map<string, Sema> | null;
284
- /** Subtree-resolution cache: Sema node → its store id and byte length.
285
- * Populated by {@link foldTree} during inference; checked before
286
- * walking children. When a conversation's pyramid reuses prefix
287
- * subtrees, this cache names them without a store probe. It does NOT let
288
- * {@link recognise} skip them: recognise walks with a `visit` callback and
289
- * emits its sites from it, so a skipped descent would mean fewer sites on
290
- * a warm cache than a cold one. foldTree short-circuits only for
291
- * visitor-less walks (O(suffix) there); a visiting walk stays O(context)
292
- * and banks the elided probes. Mind-lifetime (WeakMap keys are the Sema
293
- * objects the pyramid keeps alive).
294
- *
295
- * THAT REUSE IS A PRECONDITION, NOT A GIVEN: the keys are node IDENTITIES,
296
- * so it hits only while the conversation's fold hands back the SAME Sema
297
- * objects for the unchanged prefix. `_growContext` rebuilt the whole tree
298
- * with `bytesToTree` on every turn, so every key was fresh and this cache
299
- * could not hit even once — the O(suffix) claim above described an
300
- * intention rather than the code. It now grows the context through
301
- * {@link stablePrefixFoldIncremental}, which reuses each already-folded
302
- * segment: measured over four turns, turn 4 shared 69 of its 95 nodes with
303
- * turn 3 (26 new ≈ the new turn's own size). */
304
- _resolvedSubtrees: WeakMap<Sema, {
305
- id: number;
306
- len: number;
307
- }> | null;
308
- /** Completed assistant-turn byte spans in the current cumulative query.
309
- * Empty for ordinary respond(); response-scoped structural context for
310
- * mechanisms that must not re-derive already-produced replies. */
311
- answeredSpans: ReadonlyArray<readonly [number, number]>;
312
- /** Start offset of the user turn currently being answered. Zero for an
313
- * ordinary respond() and for the first turn of a conversation. */
314
- currentTurnStart: number;
315
- _edgeGuide: Vec | null;
316
- _edgeChoice: Map<number, number>;
317
- _prevSeen: Set<number> | null;
318
- /** Session cache of node-id → perceived gist, for candidate scoring
319
- * ({@link chooseAmong} in the reverse projection's recall path re-gists up to
320
- * √N contexts per pick — the measured bottleneck there). `chooseNext` does
321
- * NOT use this cache; forward-edge disambiguation uses prevOf counts
322
- * (distributional evidence) instead of gist comparison, because for short
323
- * answer candidates the gist is dominated by accidental byte-pattern
324
- * correlations. A node's bytes are immutable and perception is a pure
325
- * function of bytes, so an entry stays valid for the store's lifetime —
326
- * never invalidated. Bounded LRU (byte-sized); a miss only re-perceives,
327
- * never a correctness risk. */
328
- _gistCache: BoundedMap<number, Vec>;
329
- /** DEPOSIT-path perception cache: content key (latin1) of a deposited
330
- * input → its accumulated turn BOUNDARIES plus reusable fold state. A
331
- * deposit whose content extends a cached entry IS a conversation context
332
- * grown by one turn — the cached length is the new boundary — so it
333
- * folds with the SAME stable-prefix fold query-time perception uses
334
- * (structural train/inference agreement, load-bearing for recall),
335
- * reusing every already-folded segment via `stable` (see StableFold) —
336
- * O(turn) per deposit instead of O(context). A first-seen input takes the
337
- * same fold with no boundaries at all, and caches the segments it produced
338
- * so a later turn of the same conversation reuses them. Purely a
339
- * performance cache for the FOLD STATE; the boundaries are semantic but
340
- * derived only from the deposit sequence itself (an evicted chain falls
341
- * back to plain-fold behavior, exactly the pre-boundary shape). */
342
- _depositTrees: BoundedMap<string, DepositCacheEntry>;
343
- /** The byte lengths present in {@link _depositTrees} — the candidate
344
- * prefix lengths probed (longest first). Drifts on eviction (a stale
345
- * length only costs a miss); cleared with the map when it outgrows the
346
- * probe budget. */
347
- _depositLens: Set<number>;
348
- /** Mind-lifetime intern memo by NODE IDENTITY: perceived-tree node → its
349
- * content-addressed id. Valid forever (ids are permanent, Sema nodes
350
- * immutable); WeakMap, so entries live exactly as long as the pyramid
351
- * cache keeps the shared subtrees alive. Lets internTreeIds skip whole
352
- * shared subtrees and indexSubSpans keep its seenBefore window skip. */
353
- _internIds: WeakMap<Sema, number>;
354
- }
355
- /** Read a whole node's bytes. */
356
- export declare const ALL = 2147483647;
357
- /** Splice every chosen span in order — the whole cover as one byte string. */
358
- export declare function spliceAll(segs: Seg[]): Uint8Array | null;
359
- /** Whether a chosen span RESTATES the query rather than answering it: its
360
- * SUBSTITUTED bytes (an edge followed from a recognised site, not the
361
- * site's own literal text read back) already occur elsewhere in the query
362
- * — the same principle recall.ts's tiers apply to a whole-query projection
363
- * ("a projection that is a proper byte-subspan of the query restates part
364
- * of the question"). A LITERAL span (the site's own bytes, unchanged) is
365
- * exempt: naming what's already there at its OWN position is not a
366
- * substitution. A recognised site that is itself an entire PRIOR TURN of
367
- * a multi-turn query is exactly this shape: it carries a genuine learnt
368
- * continuation, but that continuation is something the asker already said
369
- * moments later in the SAME query, not a new answer. Below one river
370
- * window, byte overlap is chance, not evidence — the same floor
371
- * identityBar and reachThreshold hold every other structural-overlap claim
372
- * to. */
373
- export declare function segRestatesQuery(s: Seg, query: Uint8Array, queryLen: number, W: number): boolean;
374
- /** Lift the answer out of the cover for think: the recognised region, free of
375
- * the asker's surrounding (unrecognised) framing — and free of any chosen
376
- * span that only RESTATES content the query already contains (see {@link
377
- * segRestatesQuery}). A restating span is excluded from both the framing
378
- * (lo/hi) decision and the final concatenation: it is stale, not a second
379
- * answer, but the OTHER spans a derivation chose are independent evidence
380
- * and must not be discarded along with it. */
381
- /** The spans {@link liftAnswer} actually concatenates, in order — the answer
382
- * before it is joined. Exposed so a caller can ask what the lifted answer is
383
- * MADE OF without re-deriving the selection: in particular how much of it is
384
- * SCAFFOLDING (a `rec: false` span — query bytes carried through verbatim
385
- * because nothing explained them, the same spans the liftAnswer trace labels
386
- * "scaffolding" rather than "chosen").
387
- *
388
- * That quantity is load-bearing for the grounding decision. Two candidates
389
- * can leave the SAME number of query bytes unaccounted and therefore grade
390
- * identically, while one of them pads its answer with those bytes and the
391
- * other does not — measured on test/22's two-fact chain, cover and recall
392
- * both graded 11001 with 11 bytes unexplained, and cover won the tie only on
393
- * consideration order, answering "The capital of France is Paris famous for"
394
- * where recall had crossed the hop. Carrying an unexplained span into the
395
- * answer is strictly weaker than not explaining it: it manufactures fluency
396
- * out of the asker's own words. See the tie-break in pipeline.ts. */
397
- export declare function liftAnswerParts(segs: Seg[], queryLen: number, query: Uint8Array, W: number): Seg[];
398
- /** The SCAFFOLDING byte count of a lifted answer: how many of its bytes come
399
- * from spans nothing recognised (see {@link liftAnswerParts}).
400
- *
401
- * ONLY RUNS OF AT LEAST ONE RIVER WINDOW COUNT. Not all carried-through
402
- * bytes are a failure to explain: a period, a question mark, the space
403
- * between two fused topics are GLUE — they belong to the answer's surface,
404
- * and dropping them to look better-derived would be a worse answer, not a
405
- * more honest one. A substantive phrase the derivation never explained
406
- * ("famous for") is a different claim entirely.
407
- *
408
- * W is the line between them, and it is the same line the rest of the mind
409
- * already draws: below one river window byte overlap is chance, not evidence
410
- * (see identityBar, the bridge's attestedQ, and recognition's site floor).
411
- * Counting every scaffolding byte instead — which is what this did first —
412
- * made punctuation preservation lose a tie it should win, and test/00's
413
- * "period preserved" / "question mark preserved" caught it immediately. */
414
- export declare function liftedScaffolding(segs: Seg[], queryLen: number, query: Uint8Array, W: number): number;
415
- export declare function liftAnswer(segs: Seg[], queryLen: number, query: Uint8Array, W: number): Uint8Array | null;
416
- /** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
417
- * tracked deposit interned (`prevSeen`). */
418
- export declare function changedNodes(tree: Sema, ids: Map<Sema, number>, prevSeen: Set<number>): Sema[];
@@ -1,179 +0,0 @@
1
- // types.ts — all interfaces, types, and free functions for the mind.
2
- //
3
- // GraphSearchHost is defined first (minimal imports) so GraphSearch can import
4
- // it without pulling in the full MindContext.
5
- import { bytesEqual, concatBytes, indexOf } from "../bytes.js";
6
- import { dominates } from "../geometry.js";
7
- // ═══════════════════════════════════════════════════════════════════════════
8
- // FREE FUNCTIONS (pure, no state)
9
- // ═══════════════════════════════════════════════════════════════════════════
10
- /** Read a whole node's bytes. */
11
- export const ALL = 0x7fffffff;
12
- /** Splice every chosen span in order — the whole cover as one byte string. */
13
- export function spliceAll(segs) {
14
- if (!segs.some((s) => s.rec))
15
- return null;
16
- return concatBytes(segs.map((s) => s.bytes));
17
- }
18
- /** Whether a chosen span RESTATES the query rather than answering it: its
19
- * SUBSTITUTED bytes (an edge followed from a recognised site, not the
20
- * site's own literal text read back) already occur elsewhere in the query
21
- * — the same principle recall.ts's tiers apply to a whole-query projection
22
- * ("a projection that is a proper byte-subspan of the query restates part
23
- * of the question"). A LITERAL span (the site's own bytes, unchanged) is
24
- * exempt: naming what's already there at its OWN position is not a
25
- * substitution. A recognised site that is itself an entire PRIOR TURN of
26
- * a multi-turn query is exactly this shape: it carries a genuine learnt
27
- * continuation, but that continuation is something the asker already said
28
- * moments later in the SAME query, not a new answer. Below one river
29
- * window, byte overlap is chance, not evidence — the same floor
30
- * identityBar and reachThreshold hold every other structural-overlap claim
31
- * to. */
32
- export function segRestatesQuery(s, query, queryLen, W) {
33
- if (!s.rec)
34
- return false;
35
- const literal = s.j - s.i === s.bytes.length &&
36
- bytesEqual(s.bytes, query.subarray(s.i, s.j));
37
- if (literal)
38
- return false;
39
- return s.bytes.length >= W && s.bytes.length < queryLen &&
40
- indexOf(query, s.bytes, 0) >= 0;
41
- }
42
- /** Lift the answer out of the cover for think: the recognised region, free of
43
- * the asker's surrounding (unrecognised) framing — and free of any chosen
44
- * span that only RESTATES content the query already contains (see {@link
45
- * segRestatesQuery}). A restating span is excluded from both the framing
46
- * (lo/hi) decision and the final concatenation: it is stale, not a second
47
- * answer, but the OTHER spans a derivation chose are independent evidence
48
- * and must not be discarded along with it. */
49
- /** The spans {@link liftAnswer} actually concatenates, in order — the answer
50
- * before it is joined. Exposed so a caller can ask what the lifted answer is
51
- * MADE OF without re-deriving the selection: in particular how much of it is
52
- * SCAFFOLDING (a `rec: false` span — query bytes carried through verbatim
53
- * because nothing explained them, the same spans the liftAnswer trace labels
54
- * "scaffolding" rather than "chosen").
55
- *
56
- * That quantity is load-bearing for the grounding decision. Two candidates
57
- * can leave the SAME number of query bytes unaccounted and therefore grade
58
- * identically, while one of them pads its answer with those bytes and the
59
- * other does not — measured on test/22's two-fact chain, cover and recall
60
- * both graded 11001 with 11 bytes unexplained, and cover won the tie only on
61
- * consideration order, answering "The capital of France is Paris famous for"
62
- * where recall had crossed the hop. Carrying an unexplained span into the
63
- * answer is strictly weaker than not explaining it: it manufactures fluency
64
- * out of the asker's own words. See the tie-break in pipeline.ts. */
65
- export function liftAnswerParts(segs, queryLen, query, W) {
66
- const restated = segs.map((s) => segRestatesQuery(s, query, queryLen, W));
67
- const recognised = [];
68
- for (let k = 0; k < segs.length; k++) {
69
- if (segs[k].rec && !restated[k])
70
- recognised.push(k);
71
- }
72
- if (recognised.length === 0)
73
- return [];
74
- if (recognised.length === 1) {
75
- const s = segs[recognised[0]];
76
- if (s.computed && s.i > 0)
77
- return [s];
78
- if (dominates(s.j - s.i, queryLen)) {
79
- return segs.filter((_, k) => !restated[k]);
80
- }
81
- return [s];
82
- }
83
- const lo = recognised[0];
84
- const hi = recognised[recognised.length - 1];
85
- return segs.slice(lo, hi + 1).filter((_, k) => !restated[lo + k]);
86
- }
87
- /** The SCAFFOLDING byte count of a lifted answer: how many of its bytes come
88
- * from spans nothing recognised (see {@link liftAnswerParts}).
89
- *
90
- * ONLY RUNS OF AT LEAST ONE RIVER WINDOW COUNT. Not all carried-through
91
- * bytes are a failure to explain: a period, a question mark, the space
92
- * between two fused topics are GLUE — they belong to the answer's surface,
93
- * and dropping them to look better-derived would be a worse answer, not a
94
- * more honest one. A substantive phrase the derivation never explained
95
- * ("famous for") is a different claim entirely.
96
- *
97
- * W is the line between them, and it is the same line the rest of the mind
98
- * already draws: below one river window byte overlap is chance, not evidence
99
- * (see identityBar, the bridge's attestedQ, and recognition's site floor).
100
- * Counting every scaffolding byte instead — which is what this did first —
101
- * made punctuation preservation lose a tie it should win, and test/00's
102
- * "period preserved" / "question mark preserved" caught it immediately. */
103
- export function liftedScaffolding(segs, queryLen, query, W) {
104
- // MEASURED PER CONTIGUOUS RUN, not per span. A PASS span is one BYTE — the
105
- // cover charges unrecognised bytes individually — so asking whether a single
106
- // span reaches W would find no run ever, whatever the query. " famous for"
107
- // arrives as eleven one-byte spans in a row and is one eleven-byte run.
108
- let n = 0;
109
- let run = 0;
110
- const close = () => {
111
- if (run >= W)
112
- n += run;
113
- run = 0;
114
- };
115
- for (const s of liftAnswerParts(segs, queryLen, query, W)) {
116
- if (s.rec)
117
- close();
118
- else
119
- run += s.bytes.length;
120
- }
121
- close();
122
- return n;
123
- }
124
- export function liftAnswer(segs, queryLen, query, W) {
125
- // ONE selection rule, in {@link liftAnswerParts} — this is its join. The
126
- // two used to be separate copies of the same lo/hi/restated reasoning, which
127
- // is exactly how an answer and the accounting OF that answer drift apart.
128
- const parts = liftAnswerParts(segs, queryLen, query, W);
129
- if (parts.length === 0)
130
- return null;
131
- return concatBytes(parts.map((x) => x.bytes));
132
- }
133
- /** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
134
- * tracked deposit interned (`prevSeen`). */
135
- export function changedNodes(tree, ids, prevSeen) {
136
- const newCount = new Map();
137
- const count = (n) => {
138
- const memo = newCount.get(n);
139
- if (memo !== undefined)
140
- return memo;
141
- const id = ids.get(n);
142
- // PRUNE: a node whose id the previous deposit already interned is old,
143
- // and content addressing makes that transitive — the same id names the
144
- // same content, so every descendant was interned then too. The whole
145
- // subtree counts 0 without walking it; with the pyramid fold sharing a
146
- // conversation's prefix subtree, this is what keeps the changed-nodes
147
- // read O(new nodes) instead of O(context). (A node internTreeIds
148
- // memo-skipped has an id here exactly when it is such a shared root.)
149
- if (id !== undefined && prevSeen.has(id)) {
150
- newCount.set(n, 0);
151
- return 0;
152
- }
153
- let c = 1; // reachable only when NOT pruned above ⇒ this node is new
154
- if (n.kids) {
155
- for (const k of n.kids)
156
- c += count(k);
157
- }
158
- newCount.set(n, c);
159
- return c;
160
- };
161
- const total = count(tree);
162
- if (total === 0)
163
- return [tree];
164
- let n = tree;
165
- for (;;) {
166
- if (n.kids === null)
167
- return [n];
168
- let holder = null;
169
- for (const k of n.kids) {
170
- if (newCount.get(k) === total) {
171
- holder = k;
172
- break;
173
- }
174
- }
175
- if (holder === null)
176
- return [n];
177
- n = holder;
178
- }
179
- }